1use crate::{
2 bstr::{BStr, BString},
3 tree, Tree, TreeRef,
4};
5use std::cell::RefCell;
6use std::cmp::Ordering;
7
8pub mod editor;
10
11mod ref_iter;
12pub mod write;
14
15#[doc(alias = "TreeUpdateBuilder", alias = "git2")]
23#[derive(Clone)]
24pub struct Editor<'a> {
25 find: &'a dyn crate::FindExt,
27 object_hash: gix_hash::Kind,
29 trees: std::collections::HashMap<BString, Tree>,
33 path_buf: RefCell<BString>,
35 tree_buf: Vec<u8>,
37}
38
39#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Hash)]
46#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
47pub struct EntryMode(pub u16);
48
49impl std::fmt::Debug for EntryMode {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "EntryMode({:#o})", self.0)
52 }
53}
54
55#[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash)]
60#[repr(u16)]
61#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
62pub enum EntryKind {
63 Tree = 0o040000u16,
65 Blob = 0o100644,
67 BlobExecutable = 0o100755,
69 Link = 0o120000,
71 Commit = 0o160000,
73}
74
75impl From<EntryKind> for EntryMode {
76 fn from(value: EntryKind) -> Self {
77 EntryMode(value as u16)
78 }
79}
80
81impl From<EntryMode> for EntryKind {
82 fn from(value: EntryMode) -> Self {
83 value.kind()
84 }
85}
86
87impl EntryKind {
89 pub fn as_octal_str(&self) -> &'static BStr {
91 use EntryKind::*;
92 let bytes: &[u8] = match self {
93 Tree => b"40000",
94 Blob => b"100644",
95 BlobExecutable => b"100755",
96 Link => b"120000",
97 Commit => b"160000",
98 };
99 bytes.into()
100 }
101}
102
103impl std::ops::Deref for EntryMode {
104 type Target = u16;
105
106 fn deref(&self) -> &Self::Target {
107 &self.0
108 }
109}
110
111const IFMT: u16 = 0o170000;
112
113impl EntryMode {
114 pub const fn kind(&self) -> EntryKind {
116 let etype = self.0 & IFMT;
117 if etype == 0o100000 {
118 if self.0 & 0o000100 == 0o000100 {
119 EntryKind::BlobExecutable
120 } else {
121 EntryKind::Blob
122 }
123 } else if etype == EntryKind::Link as u16 {
124 EntryKind::Link
125 } else if etype == EntryKind::Tree as u16 {
126 EntryKind::Tree
127 } else {
128 EntryKind::Commit
129 }
130 }
131
132 pub const fn is_tree(&self) -> bool {
134 self.0 & IFMT == EntryKind::Tree as u16
135 }
136
137 pub const fn is_commit(&self) -> bool {
139 self.0 & IFMT == EntryKind::Commit as u16
140 }
141
142 pub const fn is_link(&self) -> bool {
144 self.0 & IFMT == EntryKind::Link as u16
145 }
146
147 pub const fn is_no_tree(&self) -> bool {
149 self.0 & IFMT != EntryKind::Tree as u16
150 }
151
152 pub const fn is_blob(&self) -> bool {
154 self.0 & IFMT == 0o100000
155 }
156
157 pub const fn is_executable(&self) -> bool {
159 matches!(self.kind(), EntryKind::BlobExecutable)
160 }
161
162 pub const fn is_blob_or_symlink(&self) -> bool {
164 matches!(
165 self.kind(),
166 EntryKind::Blob | EntryKind::BlobExecutable | EntryKind::Link
167 )
168 }
169
170 pub const fn as_str(&self) -> &'static str {
172 use EntryKind::*;
173 match self.kind() {
174 Tree => "tree",
175 Blob => "blob",
176 BlobExecutable => "exe",
177 Link => "link",
178 Commit => "commit",
179 }
180 }
181
182 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}
203
204impl TreeRef<'_> {
205 pub fn to_owned(&self) -> Tree {
210 self.clone().into()
211 }
212
213 pub fn into_owned(self) -> Tree {
215 self.into()
216 }
217}
218
219#[derive(PartialEq, Eq, Debug, Hash, Clone, Copy)]
221#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
222pub struct EntryRef<'a> {
223 pub mode: tree::EntryMode,
225 pub filename: &'a BStr,
227 #[cfg_attr(feature = "serde", serde(borrow))]
231 pub oid: &'a gix_hash::oid,
232}
233
234impl PartialOrd for EntryRef<'_> {
235 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
236 Some(self.cmp(other))
237 }
238}
239
240impl Ord for EntryRef<'_> {
241 fn cmp(&self, b: &Self) -> Ordering {
242 let a = self;
243 let common = a.filename.len().min(b.filename.len());
244 a.filename[..common].cmp(&b.filename[..common]).then_with(|| {
245 let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/'));
246 let b = b.filename.get(common).or_else(|| b.mode.is_tree().then_some(&b'/'));
247 a.cmp(&b)
248 })
249 }
250}
251
252#[derive(PartialEq, Eq, Debug, Hash, Clone)]
254#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
255pub struct Entry {
256 pub mode: EntryMode,
258 pub filename: BString,
260 pub oid: gix_hash::ObjectId,
262}
263
264impl PartialOrd for Entry {
265 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
266 Some(self.cmp(other))
267 }
268}
269
270impl Ord for Entry {
271 fn cmp(&self, b: &Self) -> Ordering {
272 let a = self;
273 let common = a.filename.len().min(b.filename.len());
274 a.filename[..common].cmp(&b.filename[..common]).then_with(|| {
275 let a = a.filename.get(common).or_else(|| a.mode.is_tree().then_some(&b'/'));
276 let b = b.filename.get(common).or_else(|| b.mode.is_tree().then_some(&b'/'));
277 a.cmp(&b)
278 })
279 }
280}