@@ -45,17 +45,99 @@ pub struct Editor<'a> {
45
45
/// create it by converting [`EntryKind`] into `EntryMode`.
46
46
#[ derive( Clone , Copy , PartialEq , Eq , Ord , PartialOrd , Hash ) ]
47
47
#[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
48
- pub struct EntryMode ( u16 ) ;
48
+ pub struct EntryMode {
49
+ // Represents the value read from Git, except that "040000" is represented with 0o140000 but
50
+ // "40000" is represented with 0o40000
51
+ internal : u16 ,
52
+ }
53
+
54
+ impl EntryMode {
55
+ /// Expose the value as a u16 (lossy, unlike the internal representation that is hidden)
56
+ const fn value ( self ) -> u16 {
57
+ // Demangle the hack: In the case where the second leftmost octet is 4 (Tree), the leftmost bit is
58
+ // there to represent whether the bytes representation should have 5 or 6 octets
59
+ if self . internal & IFMT == 0o140000 {
60
+ 0o040000
61
+ } else {
62
+ self . internal
63
+ }
64
+ }
65
+
66
+ /// Return the representation as used in the git internal format, which is octal and written
67
+ /// to the `backing` buffer. The respective sub-slice that was written to is returned.
68
+ pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
69
+ if self . internal == 0 {
70
+ std:: slice:: from_ref ( & b'0' )
71
+ } else {
72
+ let mut nb = 0 ;
73
+ let mut n = self . internal ;
74
+ while n > 0 {
75
+ let remainder = ( n % 8 ) as u8 ;
76
+ backing[ nb] = b'0' + remainder;
77
+ n /= 8 ;
78
+ nb += 1 ;
79
+ }
80
+ let res = & mut backing[ ..nb] ;
81
+ res. reverse ( ) ;
82
+ // Hack: `0o140000` represents `"040000"`, `0o40000` represents `"40000"`
83
+ if res[ 0 ] == b'1' && res[ 1 ] == b'4' {
84
+ res[ 0 ] = b'0' ;
85
+ }
86
+ res
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
+ if b < b'0' || b > b'7' {
110
+ return None ;
111
+ }
112
+ // More than 6 octal digits we must have hit the delimiter or the input was malformed
113
+ if idx > 6 {
114
+ return None ;
115
+ }
116
+ mode = ( mode << 3 ) + ( b - b'0' ) as u16 ;
117
+ idx += 1 ;
118
+ }
119
+ // Hack: `0o140000` represents `"040000"`, `0o40000` represents `"40000"`
120
+ if mode == 0o40000 && i[ 0 ] == b'0' {
121
+ mode += 0o100000 ;
122
+ }
123
+ Some ( ( Self { internal : mode } , & i[ ( space_pos + 1 ) ..] ) )
124
+ }
125
+
126
+ /// Construct an EntryMode from bytes represented as in the git internal format
127
+ pub fn from_bytes ( i : & [ u8 ] ) -> Option < Self > {
128
+ Self :: extract_from_bytes ( i) . map ( |( mode, _rest) | mode)
129
+ }
130
+ }
49
131
50
132
impl std:: fmt:: Debug for EntryMode {
51
133
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
52
- write ! ( f, "EntryMode({:#o })" , self . 0 )
134
+ write ! ( f, "EntryMode(0o{ })" , self . as_bytes ( & mut Default :: default ( ) ) )
53
135
}
54
136
}
55
137
56
138
impl std:: fmt:: Octal for EntryMode {
57
139
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
58
- write ! ( f, "{:o }" , self . 0 )
140
+ write ! ( f, "{}" , self . as_bytes ( & mut Default :: default ( ) ) )
59
141
}
60
142
}
61
143
@@ -79,21 +161,9 @@ pub enum EntryKind {
79
161
Commit = 0o160000 ,
80
162
}
81
163
82
- impl From < u16 > for EntryMode {
83
- fn from ( value : u16 ) -> Self {
84
- EntryMode ( value)
85
- }
86
- }
87
-
88
- impl From < EntryMode > for u16 {
89
- fn from ( value : EntryMode ) -> Self {
90
- value. 0
91
- }
92
- }
93
-
94
164
impl From < EntryKind > for EntryMode {
95
165
fn from ( value : EntryKind ) -> Self {
96
- EntryMode ( value as u16 )
166
+ EntryMode { internal : value as u16 }
97
167
}
98
168
}
99
169
@@ -119,22 +189,14 @@ impl EntryKind {
119
189
}
120
190
}
121
191
122
- impl std:: ops:: Deref for EntryMode {
123
- type Target = u16 ;
124
-
125
- fn deref ( & self ) -> & Self :: Target {
126
- & self . 0
127
- }
128
- }
129
-
130
192
const IFMT : u16 = 0o170000 ;
131
193
132
194
impl EntryMode {
133
195
/// Discretize the raw mode into an enum with well-known state while dropping unnecessary details.
134
196
pub const fn kind ( & self ) -> EntryKind {
135
- let etype = self . 0 & IFMT ;
197
+ let etype = self . value ( ) & IFMT ;
136
198
if etype == 0o100000 {
137
- if self . 0 & 0o000100 == 0o000100 {
199
+ if self . value ( ) & 0o000100 == 0o000100 {
138
200
EntryKind :: BlobExecutable
139
201
} else {
140
202
EntryKind :: Blob
@@ -150,27 +212,27 @@ impl EntryMode {
150
212
151
213
/// Return true if this entry mode represents a Tree/directory
152
214
pub const fn is_tree ( & self ) -> bool {
153
- self . 0 & IFMT == EntryKind :: Tree as u16
215
+ self . value ( ) & IFMT == EntryKind :: Tree as u16
154
216
}
155
217
156
218
/// Return true if this entry mode represents the commit of a submodule.
157
219
pub const fn is_commit ( & self ) -> bool {
158
- self . 0 & IFMT == EntryKind :: Commit as u16
220
+ self . value ( ) & IFMT == EntryKind :: Commit as u16
159
221
}
160
222
161
223
/// Return true if this entry mode represents a symbolic link
162
224
pub const fn is_link ( & self ) -> bool {
163
- self . 0 & IFMT == EntryKind :: Link as u16
225
+ self . value ( ) & IFMT == EntryKind :: Link as u16
164
226
}
165
227
166
228
/// Return true if this entry mode represents anything BUT Tree/directory
167
229
pub const fn is_no_tree ( & self ) -> bool {
168
- self . 0 & IFMT != EntryKind :: Tree as u16
230
+ self . value ( ) & IFMT != EntryKind :: Tree as u16
169
231
}
170
232
171
233
/// Return true if the entry is any kind of blob.
172
234
pub const fn is_blob ( & self ) -> bool {
173
- self . 0 & IFMT == 0o100000
235
+ self . value ( ) & IFMT == 0o100000
174
236
}
175
237
176
238
/// Return true if the entry is an executable blob.
@@ -198,30 +260,9 @@ impl EntryMode {
198
260
}
199
261
}
200
262
201
- /// Return the representation as used in the git internal format, which is octal and written
202
- /// to the `backing` buffer. The respective sub-slice that was written to is returned.
203
- pub fn as_bytes < ' a > ( & self , backing : & ' a mut [ u8 ; 6 ] ) -> & ' a BStr {
204
- if self . 0 == 0 {
205
- std:: slice:: from_ref ( & b'0' )
206
- } else {
207
- let mut nb = 0 ;
208
- let mut n = self . 0 ;
209
- while n > 0 {
210
- let remainder = ( n % 8 ) as u8 ;
211
- backing[ nb] = b'0' + remainder;
212
- n /= 8 ;
213
- nb += 1 ;
214
- }
215
- let res = & mut backing[ ..nb] ;
216
- res. reverse ( ) ;
217
- res
218
- }
219
- . into ( )
220
- }
221
-
222
263
/// Display the octal representation of this EntryMode
223
264
pub fn as_octal_representation ( & self ) -> fmt:: Result {
224
- Ok ( print ! ( "{:o }" , self . 0 ) )
265
+ Ok ( print ! ( "{:}" , self . as_bytes ( & mut Default :: default ( ) ) ) )
225
266
}
226
267
}
227
268
0 commit comments