@@ -44,11 +44,98 @@ pub struct Editor<'a> {
44
44
/// create it by converting [`EntryKind`] into `EntryMode`.
45
45
#[ derive( Clone , Copy , PartialEq , Eq , Ord , PartialOrd , Hash ) ]
46
46
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
47
- pub struct EntryMode ( pub u16 ) ;
47
+ pub struct EntryMode {
48
+ // Represents the value read from Git, except that "040000" is represented with 0o140000 but
49
+ // "40000" is represented with 0o40000
50
+ internal : u16 ,
51
+ }
52
+
53
+ impl TryFrom < u32 > for tree:: EntryMode {
54
+ type Error = u32 ;
55
+ fn try_from ( mode : u32 ) -> Result < Self , Self :: Error > {
56
+ Ok ( match mode {
57
+ 0o40000 | 0o120000 | 0o160000 => EntryMode { internal : mode as u16 } ,
58
+ blob_mode if blob_mode & 0o100000 == 0o100000 => EntryMode { internal : mode as u16 } ,
59
+ _ => return Err ( mode) ,
60
+ } )
61
+ }
62
+ }
63
+
64
+ impl EntryMode {
65
+ /// Expose the value as a u16 (lossy, unlike the internal representation that is hidden)
66
+ pub const fn value ( self ) -> u16 {
67
+ self . internal
68
+ }
69
+
70
+ /// Return the representation as used in the git internal format, which is octal and written
71
+ /// to the `backing` buffer. The respective sub-slice that was written to is returned.
72
+ pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
73
+ if self . internal == 0 {
74
+ std:: slice:: from_ref ( & b'0' )
75
+ } else {
76
+ for ( idx, backing_octet) in backing. iter_mut ( ) . enumerate ( ) {
77
+ let bit_pos = 3 /* because base 8 and 2^3 == 8*/ * ( 6 - idx - 1 ) ;
78
+ let oct_mask = 0b111 << bit_pos;
79
+ let digit = ( self . internal & oct_mask) >> bit_pos;
80
+ * backing_octet = b'0' + digit as u8 ;
81
+ }
82
+ if backing[ 1 ] == b'4' {
83
+ & backing[ 1 ..6 ]
84
+ } else {
85
+ & backing[ 0 ..6 ]
86
+ }
87
+ }
88
+ . into ( )
89
+ }
90
+
91
+ /// Construct an EntryMode from bytes represented as in the git internal format
92
+ /// Return the mode and the remainder of the bytes
93
+ pub ( crate ) fn extract_from_bytes ( i : & [ u8 ] ) -> Option < ( Self , & ' _ [ u8 ] ) > {
94
+ let mut mode = 0 ;
95
+ let mut idx = 0 ;
96
+ let mut space_pos = 0 ;
97
+ if i. is_empty ( ) {
98
+ return None ;
99
+ }
100
+ // const fn, this is why we can't have nice things (like `.iter().any()`)
101
+ while idx < i. len ( ) {
102
+ let b = i[ idx] ;
103
+ // Delimiter, return what we got
104
+ if b == b' ' {
105
+ space_pos = idx;
106
+ break ;
107
+ }
108
+ // Not a pure octal input
109
+ // Performance matters here, so `!(b'0'..=b'7').contains(&b)` won't do
110
+ #[ allow( clippy:: manual_range_contains) ]
111
+ if b < b'0' || b > b'7' {
112
+ return None ;
113
+ }
114
+ // More than 6 octal digits we must have hit the delimiter or the input was malformed
115
+ if idx > 6 {
116
+ return None ;
117
+ }
118
+ mode = ( mode << 3 ) + ( b - b'0' ) as u16 ;
119
+ idx += 1 ;
120
+ }
121
+ Some ( ( Self { internal : mode } , & i[ ( space_pos + 1 ) ..] ) )
122
+ }
123
+
124
+ /// Construct an EntryMode from bytes represented as in the git internal format
125
+ pub fn from_bytes ( i : & [ u8 ] ) -> Option < Self > {
126
+ Self :: extract_from_bytes ( i) . map ( |( mode, _rest) | mode)
127
+ }
128
+ }
48
129
49
130
impl std:: fmt:: Debug for EntryMode {
50
131
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
51
- write ! ( f, "EntryMode({:#o})" , self . 0 )
132
+ write ! ( f, "EntryMode(0o{})" , self . as_bytes( & mut Default :: default ( ) ) )
133
+ }
134
+ }
135
+
136
+ impl std:: fmt:: Octal for EntryMode {
137
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
138
+ write ! ( f, "{}" , self . as_bytes( & mut Default :: default ( ) ) )
52
139
}
53
140
}
54
141
@@ -74,7 +161,7 @@ pub enum EntryKind {
74
161
75
162
impl From < EntryKind > for EntryMode {
76
163
fn from ( value : EntryKind ) -> Self {
77
- EntryMode ( value as u16 )
164
+ EntryMode { internal : value as u16 }
78
165
}
79
166
}
80
167
@@ -100,22 +187,14 @@ impl EntryKind {
100
187
}
101
188
}
102
189
103
- impl std:: ops:: Deref for EntryMode {
104
- type Target = u16 ;
105
-
106
- fn deref ( & self ) -> & Self :: Target {
107
- & self . 0
108
- }
109
- }
110
-
111
190
const IFMT : u16 = 0o170000 ;
112
191
113
192
impl EntryMode {
114
193
/// Discretize the raw mode into an enum with well-known state while dropping unnecessary details.
115
194
pub const fn kind ( & self ) -> EntryKind {
116
- let etype = self . 0 & IFMT ;
195
+ let etype = self . value ( ) & IFMT ;
117
196
if etype == 0o100000 {
118
- if self . 0 & 0o000100 == 0o000100 {
197
+ if self . value ( ) & 0o000100 == 0o000100 {
119
198
EntryKind :: BlobExecutable
120
199
} else {
121
200
EntryKind :: Blob
@@ -131,27 +210,27 @@ impl EntryMode {
131
210
132
211
/// Return true if this entry mode represents a Tree/directory
133
212
pub const fn is_tree ( & self ) -> bool {
134
- self . 0 & IFMT == EntryKind :: Tree as u16
213
+ self . value ( ) & IFMT == EntryKind :: Tree as u16
135
214
}
136
215
137
216
/// Return true if this entry mode represents the commit of a submodule.
138
217
pub const fn is_commit ( & self ) -> bool {
139
- self . 0 & IFMT == EntryKind :: Commit as u16
218
+ self . value ( ) & IFMT == EntryKind :: Commit as u16
140
219
}
141
220
142
221
/// Return true if this entry mode represents a symbolic link
143
222
pub const fn is_link ( & self ) -> bool {
144
- self . 0 & IFMT == EntryKind :: Link as u16
223
+ self . value ( ) & IFMT == EntryKind :: Link as u16
145
224
}
146
225
147
226
/// Return true if this entry mode represents anything BUT Tree/directory
148
227
pub const fn is_no_tree ( & self ) -> bool {
149
- self . 0 & IFMT != EntryKind :: Tree as u16
228
+ self . value ( ) & IFMT != EntryKind :: Tree as u16
150
229
}
151
230
152
231
/// Return true if the entry is any kind of blob.
153
232
pub const fn is_blob ( & self ) -> bool {
154
- self . 0 & IFMT == 0o100000
233
+ self . value ( ) & IFMT == 0o100000
155
234
}
156
235
157
236
/// Return true if the entry is an executable blob.
@@ -178,27 +257,6 @@ impl EntryMode {
178
257
Commit => "commit" ,
179
258
}
180
259
}
181
-
182
- /// Return the representation as used in the git internal format, which is octal and written
183
- /// to the `backing` buffer. The respective sub-slice that was written to is returned.
184
- pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
185
- if self . 0 == 0 {
186
- std:: slice:: from_ref ( & b'0' )
187
- } else {
188
- let mut nb = 0 ;
189
- let mut n = self . 0 ;
190
- while n > 0 {
191
- let remainder = ( n % 8 ) as u8 ;
192
- backing[ nb] = b'0' + remainder;
193
- n /= 8 ;
194
- nb += 1 ;
195
- }
196
- let res = & mut backing[ ..nb] ;
197
- res. reverse ( ) ;
198
- res
199
- }
200
- . into ( )
201
- }
202
260
}
203
261
204
262
impl TreeRef < ' _ > {
0 commit comments