1use debian_control::fields::MultiArch;
6use std::collections::{HashMap, HashSet};
7use std::path::{Path, PathBuf};
8use toml_edit::{value, DocumentMut, Table};
9
10pub use toml_edit;
11
12pub const DEFAULT_MAINTAINER: &str =
14 "Debian Rust Maintainers <pkg-rust-maintainers@alioth-lists.debian.net>";
15
16pub const DEFAULT_SECTION: &str = "rust";
18
19pub const CURRENT_STANDARDS_VERSION: &str = "4.5.1";
21
22pub const DEFAULT_PRIORITY: debian_control::Priority = debian_control::Priority::Optional;
24
25pub struct DebcargoEditor {
27 debcargo_toml_path: Option<PathBuf>,
29
30 pub debcargo: DocumentMut,
32
33 pub cargo: Option<DocumentMut>,
35}
36
37impl From<DocumentMut> for DebcargoEditor {
38 fn from(doc: DocumentMut) -> Self {
39 Self {
40 cargo: None,
41 debcargo_toml_path: None,
42 debcargo: doc,
43 }
44 }
45}
46
47impl Default for DebcargoEditor {
48 fn default() -> Self {
49 Self::new()
50 }
51}
52
53impl DebcargoEditor {
54 pub fn new() -> Self {
56 Self {
57 debcargo_toml_path: None,
58 debcargo: DocumentMut::new(),
59 cargo: None,
60 }
61 }
62
63 fn crate_name(&self) -> Option<&str> {
65 self.cargo
66 .as_ref()
67 .and_then(|c| c["package"]["name"].as_str())
68 }
69
70 fn crate_version(&self) -> Option<semver::Version> {
72 self.cargo
73 .as_ref()
74 .and_then(|c| c["package"]["version"].as_str())
75 .map(|s| semver::Version::parse(s).unwrap())
76 }
77
78 pub fn open(path: &Path) -> Result<Self, std::io::Error> {
80 let content = std::fs::read_to_string(path)?;
81 Ok(Self {
82 debcargo_toml_path: Some(path.to_path_buf()),
83 cargo: None,
84 debcargo: content.parse().unwrap(),
85 })
86 }
87
88 pub fn from_directory(path: &std::path::Path) -> Result<Self, std::io::Error> {
90 let debcargo_toml_path = path.join("debian/debcargo.toml");
91 let debcargo_toml = std::fs::read_to_string(&debcargo_toml_path)?;
92 let cargo_toml = std::fs::read_to_string(path.join("Cargo.toml"))?;
93 Ok(Self {
94 debcargo_toml_path: Some(debcargo_toml_path),
95 debcargo: debcargo_toml.parse().unwrap(),
96 cargo: Some(cargo_toml.parse().unwrap()),
97 })
98 }
99
100 pub fn commit(&self) -> std::io::Result<bool> {
102 let old_contents = std::fs::read_to_string(self.debcargo_toml_path.as_ref().unwrap())?;
103 let new_contents = self.debcargo.to_string();
104 if old_contents == new_contents {
105 return Ok(false);
106 }
107 std::fs::write(
108 self.debcargo_toml_path.as_ref().unwrap(),
109 new_contents.as_bytes(),
110 )?;
111 Ok(true)
112 }
113
114 pub fn source(&mut self) -> DebcargoSource<'_> {
116 DebcargoSource { main: self }
117 }
118
119 fn semver_suffix(&self) -> bool {
120 self.debcargo["source"]
121 .get("semver_suffix")
122 .and_then(|v| v.as_bool())
123 .unwrap_or(false)
124 }
125
126 pub fn binaries(&mut self) -> impl Iterator<Item = DebcargoBinary<'_>> {
128 let semver_suffix = self.semver_suffix();
129
130 let mut ret: HashMap<String, String> = HashMap::new();
131 ret.insert(
132 debcargo_binary_name(
133 self.crate_name().unwrap(),
134 &if semver_suffix {
135 semver_pair(&self.crate_version().unwrap())
136 } else {
137 "".to_string()
138 },
139 ),
140 "lib".to_string(),
141 );
142
143 if self.debcargo["bin"].as_bool().unwrap_or(!semver_suffix) {
144 let bin_name = self.debcargo["bin_name"]
145 .as_str()
146 .unwrap_or_else(|| self.crate_name().unwrap());
147 ret.insert(bin_name.to_owned(), "bin".to_string());
148 }
149
150 let global_summary = self.global_summary();
151 let global_description = self.global_description();
152 let crate_name = self.crate_name().unwrap().to_string();
153 let crate_version = self.crate_version().unwrap();
154 let features = self.features();
155
156 self.debcargo
157 .as_table_mut()
158 .iter_mut()
159 .filter_map(move |(key, item)| {
160 let kind = ret.remove(&key.to_string())?;
161 Some(DebcargoBinary::new(
162 kind,
163 key.to_string(),
164 item.as_table_mut().unwrap(),
165 global_summary.clone(),
166 global_description.clone(),
167 crate_name.clone(),
168 crate_version.clone(),
169 semver_suffix,
170 features.clone(),
171 ))
172 })
173 }
174
175 fn global_summary(&self) -> Option<String> {
176 if let Some(summary) = self.debcargo.get("summary").and_then(|v| v.as_str()) {
177 Some(format!("{} - Rust source code", summary))
178 } else {
179 self.cargo.as_ref().and_then(|c| {
180 c["package"]
181 .get("description")
182 .and_then(|v| v.as_str())
183 .map(|s| s.split('\n').next().unwrap().to_string())
184 })
185 }
186 }
187
188 fn global_description(&self) -> Option<String> {
189 self.debcargo
190 .get("description")
191 .and_then(|v| v.as_str())
192 .map(|description| description.to_owned())
193 }
194
195 fn features(&self) -> Option<HashSet<String>> {
196 self.cargo
197 .as_ref()
198 .and_then(|c| c["features"].as_table())
199 .map(|t| t.iter().map(|(k, _)| k.to_string()).collect())
200 }
201}
202
203pub struct DebcargoSource<'a> {
205 main: &'a mut DebcargoEditor,
206}
207
208impl DebcargoSource<'_> {
209 pub fn toml_section_mut(&mut self) -> &mut Table {
211 if !self.main.debcargo.contains_key("source") {
212 self.main.debcargo["source"] = toml_edit::Item::Table(Table::new());
213 }
214 self.main.debcargo["source"].as_table_mut().unwrap()
215 }
216
217 pub fn set_standards_version(&mut self, version: &str) -> &mut Self {
219 self.toml_section_mut()["standards-version"] = value(version);
220 self
221 }
222
223 pub fn standards_version(&self) -> &str {
225 self.main
226 .debcargo
227 .get("source")
228 .and_then(|s| s.get("standards-version"))
229 .and_then(|v| v.as_str())
230 .unwrap_or(CURRENT_STANDARDS_VERSION)
231 }
232
233 pub fn set_homepage(&mut self, homepage: &str) -> &mut Self {
235 self.toml_section_mut()["homepage"] = value(homepage);
236 self
237 }
238
239 pub fn homepage(&self) -> Option<&str> {
241 let default_homepage = self
242 .main
243 .cargo
244 .as_ref()
245 .and_then(|c| c.get("package"))
246 .and_then(|x| x.get("homepage"))
247 .and_then(|v| v.as_str());
248 self.main
249 .debcargo
250 .get("source")
251 .and_then(|s| s.get("homepage"))
252 .and_then(|v| v.as_str())
253 .or(default_homepage)
254 }
255
256 pub fn set_vcs_git(&mut self, git: &str) -> &mut Self {
258 self.toml_section_mut()["vcs_git"] = value(git);
259 self
260 }
261
262 pub fn vcs_git(&self) -> Option<String> {
264 let default_git = self.main.crate_name().map(|c| {
265 format!(
266 "https://salsa.debian.org/rust-team/debcargo-conf.git [src/{}]",
267 c.to_lowercase()
268 )
269 });
270
271 self.main
272 .debcargo
273 .get("source")
274 .and_then(|s| s.get("vcs_git"))
275 .and_then(|v| v.as_str())
276 .map_or(default_git, |s| Some(s.to_string()))
277 }
278
279 pub fn vcs_browser(&self) -> Option<String> {
281 let default_vcs_browser = self.main.crate_name().map(|c| {
282 format!(
283 "https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/{}",
284 c.to_lowercase()
285 )
286 });
287
288 self.main
289 .debcargo
290 .get("source")
291 .and_then(|s| s.get("vcs_browser"))
292 .and_then(|v| v.as_str())
293 .map_or(default_vcs_browser, |s| Some(s.to_string()))
294 }
295
296 pub fn set_vcs_browser(&mut self, browser: &str) -> &mut Self {
298 self.toml_section_mut()["vcs_browser"] = value(browser);
299 self
300 }
301
302 pub fn section(&self) -> &str {
304 self.main
305 .debcargo
306 .get("source")
307 .and_then(|s| s.get("section"))
308 .and_then(|v| v.as_str())
309 .unwrap_or(DEFAULT_SECTION)
310 }
311
312 pub fn set_section(&mut self, section: &str) -> &mut Self {
314 self.toml_section_mut()["section"] = value(section);
315 self
316 }
317
318 pub fn name(&self) -> Option<String> {
320 let crate_name = self.main.crate_name()?;
321 let semver_suffix = self.main.semver_suffix();
322 if semver_suffix {
323 let crate_version = self.main.crate_version()?;
324 Some(format!(
325 "rust-{}-{}",
326 debnormalize(crate_name),
327 semver_pair(&crate_version)
328 ))
329 } else {
330 Some(format!("rust-{}", debnormalize(crate_name)))
331 }
332 }
333
334 pub fn priority(&self) -> debian_control::Priority {
336 self.main
337 .debcargo
338 .get("source")
339 .and_then(|s| s.get("priority"))
340 .and_then(|v| v.as_str())
341 .and_then(|s| s.parse().ok())
342 .unwrap_or(DEFAULT_PRIORITY)
343 }
344
345 pub fn set_priority(&mut self, priority: debian_control::Priority) -> &mut Self {
347 self.toml_section_mut()["priority"] = value(priority.to_string());
348 self
349 }
350
351 pub fn rules_requires_root(&self) -> bool {
353 self.main
354 .debcargo
355 .get("source")
356 .and_then(|s| s.get("requires_root"))
357 .and_then(|v| v.as_bool())
358 .unwrap_or(false)
359 }
360
361 pub fn set_rules_requires_root(&mut self, requires_root: bool) -> &mut Self {
363 self.toml_section_mut()["requires_root"] = value(if requires_root { "yes" } else { "no" });
364 self
365 }
366
367 pub fn maintainer(&self) -> &str {
369 self.main
370 .debcargo
371 .get("source")
372 .and_then(|s| s.get("maintainer"))
373 .and_then(|v| v.as_str())
374 .unwrap_or(DEFAULT_MAINTAINER)
375 }
376
377 pub fn set_maintainer(&mut self, maintainer: &str) -> &mut Self {
379 self.toml_section_mut()["maintainer"] = value(maintainer);
380 self
381 }
382
383 pub fn uploaders(&self) -> Option<Vec<String>> {
385 self.main
386 .debcargo
387 .get("source")
388 .and_then(|s| s.get("uploaders"))
389 .and_then(|x| x.as_array())
390 .map(|a| {
391 a.iter()
392 .filter_map(|v| v.as_str())
393 .map(|s| s.to_string())
394 .collect()
395 })
396 }
397
398 pub fn set_uploaders(&mut self, uploaders: Vec<String>) -> &mut Self {
400 let mut array = toml_edit::Array::new();
401 for u in uploaders {
402 array.push(u);
403 }
404 self.toml_section_mut()["uploaders"] = value(array);
405 self
406 }
407
408 pub fn extra_lines(&self) -> Vec<String> {
410 self.main
411 .debcargo
412 .get("source")
413 .and_then(|s| s.get("extra_lines"))
414 .and_then(|x| x.as_array())
415 .map(|a| {
416 a.iter()
417 .filter_map(|v| v.as_str())
418 .map(|s| s.to_string())
419 .collect()
420 })
421 .unwrap_or_default()
422 }
423
424 pub fn set_extra_lines(&mut self, lines: Vec<String>) -> &mut Self {
426 let mut array = toml_edit::Array::new();
427 for line in lines {
428 array.push(line);
429 }
430 self.toml_section_mut()["extra_lines"] = value(array);
431 self
432 }
433
434 pub fn add_extra_line(&mut self, line: String) -> &mut Self {
436 let mut lines = self.extra_lines();
437 if !lines.contains(&line) {
438 lines.push(line);
439 self.set_extra_lines(lines);
440 }
441 self
442 }
443
444 pub fn remove_extra_line(&mut self, line: &str) -> &mut Self {
446 let lines = self.extra_lines();
447 let filtered: Vec<String> = lines.into_iter().filter(|l| l != line).collect();
448 self.set_extra_lines(filtered);
449 self
450 }
451
452 pub fn get_extra_field(&self, field_name: &str) -> Option<String> {
455 let prefix = format!("{}:", field_name);
456 self.extra_lines()
457 .iter()
458 .find(|line| line.starts_with(&prefix))
459 .map(|line| line[prefix.len()..].trim().to_string())
460 }
461
462 pub fn set_extra_field(&mut self, field_name: &str, value: &str) -> &mut Self {
465 let field_line = format!("{}: {}", field_name, value);
466 let prefix = format!("{}:", field_name);
467
468 let mut lines = self.extra_lines();
469 let mut found = false;
470
471 for line in &mut lines {
473 if line.starts_with(&prefix) {
474 *line = field_line.clone();
475 found = true;
476 break;
477 }
478 }
479
480 if !found {
482 lines.push(field_line);
483 }
484
485 self.set_extra_lines(lines);
486 self
487 }
488
489 pub fn remove_extra_field(&mut self, field_name: &str) -> &mut Self {
491 let prefix = format!("{}:", field_name);
492 let lines = self.extra_lines();
493 let filtered: Vec<String> = lines
494 .into_iter()
495 .filter(|line| !line.starts_with(&prefix))
496 .collect();
497 self.set_extra_lines(filtered);
498 self
499 }
500
501 pub fn set_vcs_url(&mut self, vcs_type: &str, url: &str) -> &mut Self {
504 match vcs_type.to_lowercase().as_str() {
505 "git" => self.set_vcs_git(url),
506 "browser" => self.set_vcs_browser(url),
507 _ => self.set_extra_field(&format!("Vcs-{}", vcs_type), url),
508 }
509 }
510
511 pub fn get_vcs_url(&self, vcs_type: &str) -> Option<String> {
514 match vcs_type.to_lowercase().as_str() {
515 "git" => self.vcs_git(),
516 "browser" => self.vcs_browser(),
517 _ => self.get_extra_field(&format!("Vcs-{}", vcs_type)),
518 }
519 }
520}
521
522#[allow(dead_code)]
523pub struct DebcargoBinary<'a> {
525 table: &'a mut Table,
526 key: String,
527 name: String,
528 section: String,
529 global_summary: Option<String>,
530 global_description: Option<String>,
531 crate_name: String,
532 crate_version: semver::Version,
533 semver_suffix: bool,
534 features: Option<HashSet<String>>,
535}
536
537impl<'a> DebcargoBinary<'a> {
538 fn new(
539 key: String,
540 name: String,
541 table: &'a mut Table,
542 global_summary: Option<String>,
543 global_description: Option<String>,
544 crate_name: String,
545 crate_version: semver::Version,
546 semver_suffix: bool,
547 features: Option<HashSet<String>>,
548 ) -> Self {
549 Self {
550 key: key.to_owned(),
551 name,
552 section: format!("packages.{}", key),
553 table,
554 global_summary,
555 global_description,
556 crate_name,
557 crate_version,
558 semver_suffix,
559 features,
560 }
561 }
562
563 pub fn name(&self) -> &str {
565 &self.name
566 }
567
568 pub fn architecture(&self) -> Option<&str> {
570 Some("any")
571 }
572
573 pub fn multi_arch(&self) -> Option<MultiArch> {
575 Some(MultiArch::Same)
576 }
577
578 pub fn section(&self) -> Option<&str> {
580 self.table["section"].as_str()
581 }
582
583 pub fn summary(&self) -> Option<&str> {
585 if let Some(summary) = self.table.get("summary").and_then(|v| v.as_str()) {
586 Some(summary)
587 } else {
588 self.global_summary.as_deref()
589 }
590 }
591
592 pub fn long_description(&self) -> Option<String> {
594 if let Some(description) = self.table.get("description").and_then(|v| v.as_str()) {
595 Some(description.to_string())
596 } else if let Some(description) = self.global_description.as_ref() {
597 Some(description.clone())
598 } else {
599 match self.key.as_str() {
600 "lib" => Some(format!("Source code for Debianized Rust crate \"{}\"", self.crate_name)),
601 "bin" => Some("This package contains the source for the Rust mio crate, packaged by debcargo for use with cargo and dh-cargo.".to_string()),
602 _ => None,
603 }
604 }
605 }
606
607 pub fn description(&self) -> Option<String> {
609 Some(crate::control::format_description(
610 self.summary()?,
611 self.long_description()?.lines().collect(),
612 ))
613 }
614
615 pub fn depends(&self) -> Option<&str> {
617 self.table["depends"].as_str()
618 }
619
620 pub fn recommends(&self) -> Option<&str> {
622 self.table["recommends"].as_str()
623 }
624
625 pub fn suggests(&self) -> Option<&str> {
627 self.table["suggests"].as_str()
628 }
629
630 #[allow(dead_code)]
631 fn default_provides(&self) -> Option<String> {
632 let mut ret = HashSet::new();
633 let semver_suffix = self.semver_suffix;
634 let semver = &self.crate_version;
635
636 let mut suffixes = vec![];
637 if !semver_suffix {
638 suffixes.push("".to_string());
639 }
640
641 suffixes.push(format!("-{}", semver.major));
642 suffixes.push(format!("-{}.{}", semver.major, semver.minor));
643 suffixes.push(format!(
644 "-{}.{}.{}",
645 semver.major, semver.minor, semver.patch
646 ));
647 for ver_suffix in suffixes {
648 let mut feature_suffixes = HashSet::new();
649 feature_suffixes.insert("".to_string());
650 feature_suffixes.insert("+default".to_string());
651 feature_suffixes.extend(
652 self.features
653 .as_ref()
654 .map(|k| k.iter().map(|k| format!("+{}", k)).collect::<HashSet<_>>())
655 .unwrap_or_default(),
656 );
657 for feature_suffix in feature_suffixes {
658 ret.insert(debcargo_binary_name(
659 &self.crate_name,
660 &format!("{}{}", ver_suffix, &feature_suffix),
661 ));
662 }
663 }
664 ret.remove(self.name());
665 if ret.is_empty() {
666 None
667 } else {
668 Some(format!(
669 "\n{}",
670 &ret.iter()
671 .map(|s| format!("{} (= ${{binary:Version}})", s))
672 .collect::<Vec<_>>()
673 .join(",\n ")
674 ))
675 }
676 }
677}
678
679fn debnormalize(s: &str) -> String {
680 s.to_lowercase().replace('_', "-")
681}
682
683fn semver_pair(s: &semver::Version) -> String {
684 format!("{}.{}", s.major, s.minor)
685}
686
687fn debcargo_binary_name(crate_name: &str, suffix: &str) -> String {
688 format!("librust-{}{}-dev", debnormalize(crate_name), suffix)
689}
690
691pub fn unmangle_debcargo_version(version: &str) -> String {
693 version.replace("~", "-")
694}
695
696#[cfg(test)]
697mod tests {
698 #[test]
699 fn test_debcargo_binary_name() {
700 assert_eq!(super::debcargo_binary_name("foo", ""), "librust-foo-dev");
701 assert_eq!(
702 super::debcargo_binary_name("foo", "-1"),
703 "librust-foo-1-dev"
704 );
705 assert_eq!(
706 super::debcargo_binary_name("foo", "-1.2"),
707 "librust-foo-1.2-dev"
708 );
709 assert_eq!(
710 super::debcargo_binary_name("foo", "-1.2.3"),
711 "librust-foo-1.2.3-dev"
712 );
713 }
714
715 #[test]
716 fn test_semver_pair() {
717 assert_eq!(super::semver_pair(&"1.2.3".parse().unwrap()), "1.2");
718 assert_eq!(super::semver_pair(&"1.2.6".parse().unwrap()), "1.2");
719 }
720
721 #[test]
722 fn test_debnormalize() {
723 assert_eq!(super::debnormalize("foo_bar"), "foo-bar");
724 assert_eq!(super::debnormalize("foo"), "foo");
725 }
726
727 #[test]
728 fn test_debcargo_editor() {
729 let mut editor = super::DebcargoEditor::new();
730 editor.debcargo["source"]["standards-version"] = toml_edit::value("4.5.1");
731 editor.debcargo["source"]["homepage"] = toml_edit::value("https://example.com");
732 editor.debcargo["source"]["vcs_git"] = toml_edit::value("https://example.com");
733 editor.debcargo["source"]["vcs_browser"] = toml_edit::value("https://example.com");
734 editor.debcargo["source"]["section"] = toml_edit::value("notrust");
735 editor.debcargo["source"]["priority"] = toml_edit::value("optional");
736 editor.debcargo["source"]["requires_root"] = toml_edit::value("no");
737 editor.debcargo["source"]["maintainer"] =
738 toml_edit::value("Jelmer Vernooij <jelmer@debian.org>");
739
740 assert_eq!(editor.source().standards_version(), "4.5.1");
741 assert_eq!(
742 editor.source().vcs_git().as_deref(),
743 Some("https://example.com")
744 );
745 assert_eq!(
746 editor.source().vcs_browser().as_deref(),
747 Some("https://example.com")
748 );
749 assert_eq!(editor.source().section(), "notrust");
750 assert_eq!(editor.source().priority(), super::DEFAULT_PRIORITY);
751 assert!(!editor.source().rules_requires_root());
752 assert_eq!(
753 editor.source().maintainer(),
754 "Jelmer Vernooij <jelmer@debian.org>"
755 );
756 assert_eq!(editor.source().name(), None);
757 assert_eq!(editor.source().uploaders(), None);
758 assert_eq!(editor.source().homepage(), Some("https://example.com"));
759 }
760
761 #[test]
762 fn test_extra_lines_manipulation() {
763 let mut editor = super::DebcargoEditor::new();
764 let mut source = editor.source();
765
766 assert_eq!(source.extra_lines(), Vec::<String>::new());
768
769 source.set_extra_lines(vec![
771 "Vcs-Svn: https://svn.example.com/repo".to_string(),
772 "X-Custom: value".to_string(),
773 ]);
774 assert_eq!(
775 source.extra_lines(),
776 vec![
777 "Vcs-Svn: https://svn.example.com/repo".to_string(),
778 "X-Custom: value".to_string(),
779 ]
780 );
781
782 source.add_extra_line("Another-Field: another value".to_string());
784 assert_eq!(
785 source.extra_lines(),
786 vec![
787 "Vcs-Svn: https://svn.example.com/repo".to_string(),
788 "X-Custom: value".to_string(),
789 "Another-Field: another value".to_string(),
790 ]
791 );
792
793 source.add_extra_line("X-Custom: value".to_string());
795 assert_eq!(
796 source.extra_lines(),
797 vec![
798 "Vcs-Svn: https://svn.example.com/repo".to_string(),
799 "X-Custom: value".to_string(),
800 "Another-Field: another value".to_string(),
801 ]
802 );
803
804 source.remove_extra_line("X-Custom: value");
806 assert_eq!(
807 source.extra_lines(),
808 vec![
809 "Vcs-Svn: https://svn.example.com/repo".to_string(),
810 "Another-Field: another value".to_string(),
811 ]
812 );
813 }
814
815 #[test]
816 fn test_extra_field_manipulation() {
817 let mut editor = super::DebcargoEditor::new();
818 let mut source = editor.source();
819
820 assert_eq!(source.get_extra_field("Vcs-Svn"), None);
822
823 source.set_extra_field("Vcs-Svn", "https://svn.example.com/repo");
825 assert_eq!(
826 source.get_extra_field("Vcs-Svn"),
827 Some("https://svn.example.com/repo".to_string())
828 );
829
830 source.set_extra_field("Vcs-Svn", "https://svn.example.com/new-repo");
832 assert_eq!(
833 source.get_extra_field("Vcs-Svn"),
834 Some("https://svn.example.com/new-repo".to_string())
835 );
836 assert_eq!(
838 source.extra_lines(),
839 vec!["Vcs-Svn: https://svn.example.com/new-repo".to_string()]
840 );
841
842 source.set_extra_field("X-Custom", "custom value");
844 assert_eq!(
845 source.get_extra_field("X-Custom"),
846 Some("custom value".to_string())
847 );
848 assert_eq!(
849 source.get_extra_field("Vcs-Svn"),
850 Some("https://svn.example.com/new-repo".to_string())
851 );
852 assert_eq!(
853 source.extra_lines(),
854 vec![
855 "Vcs-Svn: https://svn.example.com/new-repo".to_string(),
856 "X-Custom: custom value".to_string(),
857 ]
858 );
859
860 source.remove_extra_field("Vcs-Svn");
862 assert_eq!(source.get_extra_field("Vcs-Svn"), None);
863 assert_eq!(
864 source.get_extra_field("X-Custom"),
865 Some("custom value".to_string())
866 );
867 assert_eq!(
868 source.extra_lines(),
869 vec!["X-Custom: custom value".to_string()]
870 );
871 }
872
873 #[test]
874 fn test_set_vcs_url() {
875 let mut editor = super::DebcargoEditor::new();
876 let mut source = editor.source();
877
878 source.set_vcs_url("Git", "https://github.com/example/repo.git");
880 assert_eq!(
881 source.vcs_git(),
882 Some("https://github.com/example/repo.git".to_string())
883 );
884
885 source.set_vcs_url("Browser", "https://github.com/example/repo");
887 assert_eq!(
888 source.vcs_browser(),
889 Some("https://github.com/example/repo".to_string())
890 );
891
892 source.set_vcs_url("Svn", "https://svn.example.com/repo");
894 assert_eq!(
895 source.get_extra_field("Vcs-Svn"),
896 Some("https://svn.example.com/repo".to_string())
897 );
898
899 source.set_vcs_url("Bzr", "https://bzr.example.com/repo");
901 assert_eq!(
902 source.get_extra_field("Vcs-Bzr"),
903 Some("https://bzr.example.com/repo".to_string())
904 );
905
906 source.set_vcs_url("git", "https://gitlab.com/example/repo.git");
908 assert_eq!(
909 source.vcs_git(),
910 Some("https://gitlab.com/example/repo.git".to_string())
911 );
912
913 source.set_vcs_url("browser", "https://gitlab.com/example/repo");
914 assert_eq!(
915 source.vcs_browser(),
916 Some("https://gitlab.com/example/repo".to_string())
917 );
918 }
919
920 #[test]
921 fn test_extra_field_parsing() {
922 let mut editor = super::DebcargoEditor::new();
923 let mut source = editor.source();
924
925 source.set_extra_lines(vec!["Vcs-Svn: https://svn.example.com/repo".to_string()]);
927 assert_eq!(
928 source.get_extra_field("Vcs-Svn"),
929 Some("https://svn.example.com/repo".to_string())
930 );
931
932 source.set_extra_lines(vec!["Vcs-Bzr:https://bzr.example.com/repo".to_string()]);
934 assert_eq!(
935 source.get_extra_field("Vcs-Bzr"),
936 Some("https://bzr.example.com/repo".to_string())
937 );
938 }
939
940 #[test]
941 fn test_get_vcs_url() {
942 let mut editor = super::DebcargoEditor::new();
943 let mut source = editor.source();
944
945 source.set_vcs_git("https://github.com/example/repo.git");
947 source.set_vcs_browser("https://github.com/example/repo");
948 source.set_extra_field("Vcs-Svn", "https://svn.example.com/repo");
949 source.set_extra_field("Vcs-Bzr", "https://bzr.example.com/repo");
950
951 assert_eq!(
953 source.get_vcs_url("Git"),
954 Some("https://github.com/example/repo.git".to_string())
955 );
956 assert_eq!(
957 source.get_vcs_url("git"),
958 Some("https://github.com/example/repo.git".to_string())
959 );
960
961 assert_eq!(
963 source.get_vcs_url("Browser"),
964 Some("https://github.com/example/repo".to_string())
965 );
966 assert_eq!(
967 source.get_vcs_url("browser"),
968 Some("https://github.com/example/repo".to_string())
969 );
970
971 assert_eq!(
973 source.get_vcs_url("Svn"),
974 Some("https://svn.example.com/repo".to_string())
975 );
976 assert_eq!(
977 source.get_vcs_url("Bzr"),
978 Some("https://bzr.example.com/repo".to_string())
979 );
980
981 assert_eq!(source.get_vcs_url("Hg"), None);
983 }
984}