1use std::cmp::Ordering;
22
23use radicle_git_ext::Oid;
24#[cfg(feature = "serde")]
25use serde::{
26 ser::{SerializeStruct as _, Serializer},
27 Serialize,
28};
29use url::Url;
30
31use crate::{fs, Commit};
32
33#[derive(Clone, Debug)]
37pub struct Tree {
38 id: Oid,
40 entries: Vec<Entry>,
42 commit: Commit,
44}
45
46impl Tree {
47 pub(crate) fn new(id: Oid, mut entries: Vec<Entry>, commit: Commit) -> Self {
49 entries.sort();
50 Self {
51 id,
52 entries,
53 commit,
54 }
55 }
56
57 pub fn object_id(&self) -> Oid {
58 self.id
59 }
60
61 pub fn commit(&self) -> &Commit {
63 &self.commit
64 }
65
66 pub fn entries(&self) -> &Vec<Entry> {
68 &self.entries
69 }
70}
71
72#[cfg(feature = "serde")]
73impl Serialize for Tree {
74 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100 where
101 S: Serializer,
102 {
103 const FIELDS: usize = 4;
104 let mut state = serializer.serialize_struct("Tree", FIELDS)?;
105 state.serialize_field("oid", &self.id)?;
106 state.serialize_field("entries", &self.entries)?;
107 state.serialize_field("lastCommit", &self.commit)?;
108 state.end()
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113pub enum EntryKind {
114 Tree(Oid),
115 Blob(Oid),
116 Submodule { id: Oid, url: Option<Url> },
117}
118
119impl PartialOrd for EntryKind {
120 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
121 Some(self.cmp(other))
122 }
123}
124
125impl Ord for EntryKind {
126 fn cmp(&self, other: &Self) -> Ordering {
127 match (self, other) {
128 (EntryKind::Submodule { .. }, EntryKind::Submodule { .. }) => Ordering::Equal,
129 (EntryKind::Submodule { .. }, EntryKind::Tree(_)) => Ordering::Equal,
130 (EntryKind::Tree(_), EntryKind::Submodule { .. }) => Ordering::Equal,
131 (EntryKind::Tree(_), EntryKind::Tree(_)) => Ordering::Equal,
132 (EntryKind::Tree(_), EntryKind::Blob(_)) => Ordering::Less,
133 (EntryKind::Blob(_), EntryKind::Tree(_)) => Ordering::Greater,
134 (EntryKind::Submodule { .. }, EntryKind::Blob(_)) => Ordering::Less,
135 (EntryKind::Blob(_), EntryKind::Submodule { .. }) => Ordering::Greater,
136 (EntryKind::Blob(_), EntryKind::Blob(_)) => Ordering::Equal,
137 }
138 }
139}
140
141#[derive(Clone, Debug)]
150pub struct Entry {
151 name: String,
152 entry: EntryKind,
153
154 commit: Commit,
156}
157
158impl Entry {
159 pub(crate) fn new(name: String, entry: EntryKind, commit: Commit) -> Self {
160 Self {
161 name,
162 entry,
163 commit,
164 }
165 }
166
167 pub fn name(&self) -> &str {
168 &self.name
169 }
170
171 pub fn entry(&self) -> &EntryKind {
172 &self.entry
173 }
174
175 pub fn is_tree(&self) -> bool {
176 matches!(self.entry, EntryKind::Tree(_))
177 }
178
179 pub fn commit(&self) -> &Commit {
180 &self.commit
181 }
182
183 pub fn object_id(&self) -> Oid {
184 match self.entry {
185 EntryKind::Blob(id) => id,
186 EntryKind::Tree(id) => id,
187 EntryKind::Submodule { id, .. } => id,
188 }
189 }
190}
191
192impl Ord for Entry {
194 fn cmp(&self, other: &Self) -> Ordering {
195 self.entry
196 .cmp(&other.entry)
197 .then(self.name.cmp(&other.name))
198 }
199}
200
201impl PartialOrd for Entry {
202 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
203 Some(self.cmp(other))
204 }
205}
206
207impl PartialEq for Entry {
208 fn eq(&self, other: &Self) -> bool {
209 self.entry == other.entry && self.name == other.name
210 }
211}
212
213impl Eq for Entry {}
214
215impl From<fs::Entry> for EntryKind {
216 fn from(entry: fs::Entry) -> Self {
217 match entry {
218 fs::Entry::File(f) => EntryKind::Blob(f.id()),
219 fs::Entry::Directory(d) => EntryKind::Tree(d.id()),
220 fs::Entry::Submodule(u) => EntryKind::Submodule {
221 id: u.id(),
222 url: u.url().clone(),
223 },
224 }
225 }
226}
227
228#[cfg(feature = "serde")]
229impl Serialize for Entry {
230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
253 where
254 S: Serializer,
255 {
256 const FIELDS: usize = 5;
257 let mut state = serializer.serialize_struct("TreeEntry", FIELDS)?;
258 state.serialize_field("name", &self.name)?;
259 state.serialize_field(
260 "kind",
261 match self.entry {
262 EntryKind::Blob(_) => "blob",
263 EntryKind::Tree(_) => "tree",
264 EntryKind::Submodule { .. } => "submodule",
265 },
266 )?;
267 if let EntryKind::Submodule { url: Some(url), .. } = &self.entry {
268 state.serialize_field("url", url)?;
269 };
270 state.serialize_field("oid", &self.object_id())?;
271 state.serialize_field("lastCommit", &self.commit)?;
272 state.end()
273 }
274}