radicle_source/object/
tree.rs1use std::{convert::TryFrom as _, str::FromStr as _};
19
20use serde::{
21 ser::{SerializeStruct as _, Serializer},
22 Serialize,
23};
24
25use radicle_surf::{
26 file_system,
27 vcs::git::{Browser, Rev},
28};
29
30use crate::{
31 commit,
32 error::Error,
33 object::{Info, ObjectType},
34 revision::Revision,
35};
36
37pub struct Tree {
39 pub path: String,
41 pub entries: Vec<TreeEntry>,
43 pub info: Info,
45}
46
47impl Serialize for Tree {
48 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
49 where
50 S: Serializer,
51 {
52 let mut state = serializer.serialize_struct("Tree", 3)?;
53 state.serialize_field("path", &self.path)?;
54 state.serialize_field("entries", &self.entries)?;
55 state.serialize_field("info", &self.info)?;
56 state.end()
57 }
58}
59
60pub struct TreeEntry {
63 pub info: Info,
65 pub path: String,
67}
68
69impl Serialize for TreeEntry {
70 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
71 where
72 S: Serializer,
73 {
74 let mut state = serializer.serialize_struct("Tree", 2)?;
75 state.serialize_field("path", &self.path)?;
76 state.serialize_field("info", &self.info)?;
77 state.end()
78 }
79}
80
81pub fn tree<P>(
87 browser: &mut Browser<'_>,
88 maybe_revision: Option<Revision<P>>,
89 maybe_prefix: Option<String>,
90) -> Result<Tree, Error>
91where
92 P: ToString,
93{
94 let maybe_revision = maybe_revision.map(Rev::try_from).transpose()?;
95 let prefix = maybe_prefix.unwrap_or_default();
96
97 if let Some(revision) = maybe_revision {
98 browser.rev(revision)?;
99 }
100
101 let path = if prefix == "/" || prefix.is_empty() {
102 file_system::Path::root()
103 } else {
104 file_system::Path::from_str(&prefix)?
105 };
106
107 let root_dir = browser.get_directory()?;
108 let prefix_dir = if path.is_root() {
109 root_dir
110 } else {
111 root_dir
112 .find_directory(path.clone())
113 .ok_or_else(|| Error::PathNotFound(path.clone()))?
114 };
115 let mut prefix_contents = prefix_dir.list_directory();
116 prefix_contents.sort();
117
118 let entries_results: Result<Vec<TreeEntry>, Error> = prefix_contents
119 .iter()
120 .map(|(label, system_type)| {
121 let entry_path = if path.is_root() {
122 file_system::Path::new(label.clone())
123 } else {
124 let mut p = path.clone();
125 p.push(label.clone());
126 p
127 };
128 let mut commit_path = file_system::Path::root();
129 commit_path.append(entry_path.clone());
130
131 let info = Info {
132 name: label.to_string(),
133 object_type: match system_type {
134 file_system::SystemType::Directory => ObjectType::Tree,
135 file_system::SystemType::File => ObjectType::Blob,
136 },
137 last_commit: None,
138 };
139
140 Ok(TreeEntry {
141 info,
142 path: entry_path.to_string(),
143 })
144 })
145 .collect();
146
147 let mut entries = entries_results?;
148
149 entries.sort_by(|a, b| a.info.object_type.cmp(&b.info.object_type));
154
155 let last_commit = if path.is_root() {
156 Some(commit::Header::from(browser.get().first()))
157 } else {
158 None
159 };
160 let name = if path.is_root() {
161 "".into()
162 } else {
163 let (_first, last) = path.split_last();
164 last.to_string()
165 };
166 let info = Info {
167 name,
168 object_type: ObjectType::Tree,
169 last_commit,
170 };
171
172 Ok(Tree {
173 path: prefix,
174 entries,
175 info,
176 })
177}