debian_analyzer/
abstract_control.rs1use crate::relations::ensure_relation;
4use breezyshim::tree::Tree;
5use debian_control::lossless::relations::{Entry, Relations};
6use std::path::Path;
7
8pub trait AbstractControlEditor {
10 fn source<'a>(&'a mut self) -> Option<Box<dyn AbstractSource<'a> + 'a>>;
12
13 fn binaries<'a>(&'a mut self) -> Vec<Box<dyn AbstractBinary + 'a>>;
15
16 fn commit(&self) -> bool;
18
19 fn wrap_and_sort(&mut self);
21}
22
23pub trait AbstractSource<'a> {
25 fn name(&self) -> Option<String>;
27
28 fn ensure_build_dep(&mut self, dep: Entry);
30
31 fn set_maintainer(&mut self, maintainer: &str);
33
34 fn set_uploaders(&mut self, uploaders: &[&str]);
36}
37
38pub trait AbstractBinary {
40 fn name(&self) -> Option<String>;
42}
43
44use crate::debcargo::{DebcargoBinary, DebcargoEditor, DebcargoSource};
45use debian_control::{Binary as PlainBinary, Control as PlainControl, Source as PlainSource};
46
47impl AbstractControlEditor for DebcargoEditor {
48 fn source<'a>(&'a mut self) -> Option<Box<dyn AbstractSource<'a> + 'a>> {
49 Some(Box::new(DebcargoEditor::source(self)) as Box<dyn AbstractSource<'a>>)
50 }
51
52 fn binaries<'a>(&'a mut self) -> Vec<Box<dyn AbstractBinary + 'a>> {
53 DebcargoEditor::binaries(self)
54 .map(|b| Box::new(b) as Box<dyn AbstractBinary>)
55 .collect()
56 }
57
58 fn commit(&self) -> bool {
59 DebcargoEditor::commit(self).unwrap()
60 }
61
62 fn wrap_and_sort(&mut self) {}
63}
64
65impl AbstractBinary for PlainBinary {
66 fn name(&self) -> Option<String> {
67 self.name()
68 }
69}
70
71impl<'a> AbstractSource<'a> for PlainSource {
72 fn name(&self) -> Option<String> {
73 self.name()
74 }
75
76 fn ensure_build_dep(&mut self, dep: Entry) {
77 if let Some(mut build_deps) = self.build_depends() {
78 ensure_relation(&mut build_deps, dep);
79 self.set_build_depends(&build_deps);
80 } else {
81 self.set_build_depends(&Relations::from(vec![dep]));
82 }
83 }
84
85 fn set_maintainer(&mut self, maintainer: &str) {
86 (self as &mut debian_control::lossless::Source).set_maintainer(maintainer);
87 }
88
89 fn set_uploaders(&mut self, uploaders: &[&str]) {
90 (self as &mut debian_control::lossless::Source).set_uploaders(uploaders);
91 }
92}
93
94impl<'a> AbstractBinary for DebcargoBinary<'a> {
95 fn name(&self) -> Option<String> {
96 Some(self.name().to_string())
97 }
98}
99
100impl<'a> AbstractSource<'a> for DebcargoSource<'a> {
101 fn name(&self) -> Option<String> {
102 self.name()
103 }
104
105 fn ensure_build_dep(&mut self, dep: Entry) {
106 if let Some(build_deps) = self
108 .toml_section_mut()
109 .get_mut("build_depends")
110 .and_then(|v| v.as_array_mut())
111 {
112 build_deps.push(dep.to_string());
113 }
114 }
115
116 fn set_maintainer(&mut self, maintainer: &str) {
117 (self as &mut crate::debcargo::DebcargoSource).set_maintainer(maintainer);
118 }
119
120 fn set_uploaders(&mut self, uploaders: &[&str]) {
121 (self as &mut crate::debcargo::DebcargoSource)
122 .set_uploaders(uploaders.iter().map(|s| s.to_string()).collect::<Vec<_>>());
123 }
124}
125
126impl<E: crate::editor::Editor<PlainControl>> AbstractControlEditor for E {
127 fn source<'a>(&'a mut self) -> Option<Box<dyn AbstractSource<'a> + 'a>> {
128 PlainControl::source(self).map(|s| Box::new(s) as Box<dyn AbstractSource>)
129 }
130
131 fn binaries<'a>(&'a mut self) -> Vec<Box<dyn AbstractBinary + 'a>> {
132 PlainControl::binaries(self)
133 .map(|b| Box::new(b) as Box<dyn AbstractBinary>)
134 .collect()
135 }
136
137 fn commit(&self) -> bool {
138 !(self as &dyn crate::editor::Editor<PlainControl>)
139 .commit()
140 .unwrap()
141 .is_empty()
142 }
143
144 fn wrap_and_sort(&mut self) {
145 (self as &mut dyn crate::editor::Editor<PlainControl>).wrap_and_sort(
146 deb822_lossless::Indentation::Spaces(4),
147 false,
148 None,
149 )
150 }
151}
152
153pub fn edit_control<'a>(
155 tree: &breezyshim::workingtree::WorkingTree,
156 subpath: &Path,
157) -> Result<Box<dyn AbstractControlEditor + 'a>, crate::editor::EditorError> {
158 if tree.has_filename(&subpath.join("debian/debcargo.toml")) {
159 Ok(Box::new(crate::debcargo::DebcargoEditor::from_directory(
160 &tree.abspath(subpath).unwrap(),
161 )?))
162 } else {
163 let control_path = tree.abspath(&subpath.join(std::path::Path::new("debian/control")));
164 Ok(Box::new(crate::control::TemplatedControlEditor::open(
165 control_path.unwrap(),
166 )?) as Box<dyn AbstractControlEditor>)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use breezyshim::controldir::{create_standalone_workingtree, ControlDirFormat};
174 use breezyshim::tree::MutableTree;
175 use std::path::Path;
176 use std::str::FromStr;
177
178 #[test]
179 fn test_edit_control_debcargo() {
180 let td = tempfile::tempdir().unwrap();
181 let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
182 tree.mkdir(Path::new("debian")).unwrap();
184 std::fs::write(
185 td.path().join("debian/debcargo.toml"),
186 br#"
187maintainer = "Alice <alice@example.com>"
188homepage = "https://example.com"
189description = "Example package"
190"#,
191 )
192 .unwrap();
193
194 std::fs::write(
195 td.path().join("Cargo.toml"),
196 br#"
197[package]
198name = "example"
199version = "0.1.0"
200edition = "2018"
201"#,
202 )
203 .unwrap();
204
205 tree.add(&[(Path::new("debian")), (Path::new("debian/debcargo.toml"))])
206 .unwrap();
207
208 let editor = super::edit_control(&tree, Path::new("")).unwrap();
209
210 editor.commit();
211 }
212
213 #[test]
214 fn test_edit_control_regular() {
215 let td = tempfile::tempdir().unwrap();
216 let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
217 tree.mkdir(Path::new("debian")).unwrap();
219 tree.put_file_bytes_non_atomic(
220 Path::new("debian/control"),
221 br#"
222Source: example
223Maintainer: Alice <alice@example.com>
224Homepage: https://example.com
225
226Package: example
227Architecture: any
228Description: Example package
229"#,
230 )
231 .unwrap();
232
233 tree.add(&[(Path::new("debian")), (Path::new("debian/control"))])
234 .unwrap();
235
236 let editor = super::edit_control(&tree, Path::new("")).unwrap();
237
238 editor.commit();
239 }
240
241 #[test]
242 fn test_edit_source_ensure_build_depends() {
243 let td = tempfile::tempdir().unwrap();
244 let tree = create_standalone_workingtree(td.path(), &ControlDirFormat::default()).unwrap();
245 tree.mkdir(Path::new("debian")).unwrap();
247 tree.put_file_bytes_non_atomic(
248 Path::new("debian/control"),
249 br#"
250Source: example
251Maintainer: Alice <alice@example.com>
252Build-Depends: libc6
253
254Package: example
255Architecture: any
256Description: Example package
257"#,
258 )
259 .unwrap();
260 tree.add(&[Path::new("debian/control")]).unwrap();
261
262 let mut editor = super::edit_control(&tree, Path::new("")).unwrap();
263 let mut source = editor.source().unwrap();
264 source.ensure_build_dep(
265 debian_control::lossless::relations::Entry::from_str("libssl-dev").unwrap(),
266 );
267 std::mem::drop(source);
268 editor.commit();
269
270 let text = tree.get_file_text(Path::new("debian/control")).unwrap();
271 assert_eq!(
272 std::str::from_utf8(&text).unwrap(),
273 r#"
274Source: example
275Maintainer: Alice <alice@example.com>
276Build-Depends: libc6, libssl-dev
277
278Package: example
279Architecture: any
280Description: Example package
281"#
282 );
283 }
284}