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(
103 self.debcargo_toml_path
104 .as_ref()
105 .expect("debcargo_toml_path should be set"),
106 )?;
107 let new_contents = self.debcargo.to_string();
108 if old_contents == new_contents {
109 return Ok(false);
110 }
111 std::fs::write(
112 self.debcargo_toml_path.as_ref().unwrap(),
113 new_contents.as_bytes(),
114 )?;
115 Ok(true)
116 }
117
118 pub fn source(&mut self) -> DebcargoSource<'_> {
120 DebcargoSource { main: self }
121 }
122
123 fn semver_suffix(&self) -> bool {
124 self.debcargo["source"]
125 .get("semver_suffix")
126 .and_then(|v| v.as_bool())
127 .unwrap_or(false)
128 }
129
130 pub fn binaries(&mut self) -> impl Iterator<Item = DebcargoBinary<'_>> {
132 let semver_suffix = self.semver_suffix();
133
134 let mut ret: HashMap<String, String> = HashMap::new();
135 ret.insert(
136 debcargo_binary_name(
137 self.crate_name().unwrap(),
138 &if semver_suffix {
139 semver_pair(&self.crate_version().unwrap())
140 } else {
141 "".to_string()
142 },
143 ),
144 "lib".to_string(),
145 );
146
147 if self.debcargo["bin"].as_bool().unwrap_or(!semver_suffix) {
148 let bin_name = self.debcargo["bin_name"]
149 .as_str()
150 .unwrap_or_else(|| self.crate_name().unwrap());
151 ret.insert(bin_name.to_owned(), "bin".to_string());
152 }
153
154 let global_summary = self.global_summary();
155 let global_description = self.global_description();
156 let crate_name = self.crate_name().unwrap().to_string();
157 let crate_version = self.crate_version().unwrap();
158 let features = self.features();
159
160 self.debcargo
161 .as_table_mut()
162 .iter_mut()
163 .filter_map(move |(key, item)| {
164 let kind = ret.remove(&key.to_string())?;
165 Some(DebcargoBinary::new(
166 kind,
167 key.to_string(),
168 item.as_table_mut().unwrap(),
169 global_summary.clone(),
170 global_description.clone(),
171 crate_name.clone(),
172 crate_version.clone(),
173 semver_suffix,
174 features.clone(),
175 ))
176 })
177 }
178
179 fn global_summary(&self) -> Option<String> {
180 if let Some(summary) = self.debcargo.get("summary").and_then(|v| v.as_str()) {
181 Some(format!("{} - Rust source code", summary))
182 } else {
183 self.cargo.as_ref().and_then(|c| {
184 c["package"]
185 .get("description")
186 .and_then(|v| v.as_str())
187 .map(|s| s.split('\n').next().unwrap().to_string())
188 })
189 }
190 }
191
192 fn global_description(&self) -> Option<String> {
193 self.debcargo
194 .get("description")
195 .and_then(|v| v.as_str())
196 .map(|description| description.to_owned())
197 }
198
199 fn features(&self) -> Option<HashSet<String>> {
200 self.cargo
201 .as_ref()
202 .and_then(|c| c["features"].as_table())
203 .map(|t| t.iter().map(|(k, _)| k.to_string()).collect())
204 }
205}
206
207pub struct DebcargoSource<'a> {
209 main: &'a mut DebcargoEditor,
210}
211
212impl DebcargoSource<'_> {
213 pub fn toml_section_mut(&mut self) -> &mut Table {
215 if !self.main.debcargo.contains_key("source") {
216 self.main.debcargo["source"] = toml_edit::Item::Table(Table::new());
217 }
218 self.main.debcargo["source"].as_table_mut().unwrap()
219 }
220
221 pub fn set_standards_version(&mut self, version: &str) -> &mut Self {
223 self.toml_section_mut()["standards-version"] = value(version);
224 self
225 }
226
227 pub fn standards_version(&self) -> &str {
229 self.main
230 .debcargo
231 .get("source")
232 .and_then(|s| s.get("standards-version"))
233 .and_then(|v| v.as_str())
234 .unwrap_or(CURRENT_STANDARDS_VERSION)
235 }
236
237 pub fn set_homepage(&mut self, homepage: &str) -> &mut Self {
239 self.toml_section_mut()["homepage"] = value(homepage);
240 self
241 }
242
243 pub fn homepage(&self) -> Option<&str> {
245 let default_homepage = self
246 .main
247 .cargo
248 .as_ref()
249 .and_then(|c| c.get("package"))
250 .and_then(|x| x.get("homepage"))
251 .and_then(|v| v.as_str());
252 self.main
253 .debcargo
254 .get("source")
255 .and_then(|s| s.get("homepage"))
256 .and_then(|v| v.as_str())
257 .or(default_homepage)
258 }
259
260 pub fn set_vcs_git(&mut self, git: &str) -> &mut Self {
262 self.toml_section_mut()["vcs_git"] = value(git);
263 self
264 }
265
266 pub fn vcs_git(&self) -> Option<String> {
268 let default_git = self.main.crate_name().map(|c| {
269 format!(
270 "https://salsa.debian.org/rust-team/debcargo-conf.git [src/{}]",
271 c.to_lowercase()
272 )
273 });
274
275 self.main
276 .debcargo
277 .get("source")
278 .and_then(|s| s.get("vcs_git"))
279 .and_then(|v| v.as_str())
280 .map_or(default_git, |s| Some(s.to_string()))
281 }
282
283 pub fn vcs_browser(&self) -> Option<String> {
285 let default_vcs_browser = self.main.crate_name().map(|c| {
286 format!(
287 "https://salsa.debian.org/rust-team/debcargo-conf/tree/master/src/{}",
288 c.to_lowercase()
289 )
290 });
291
292 self.main
293 .debcargo
294 .get("source")
295 .and_then(|s| s.get("vcs_browser"))
296 .and_then(|v| v.as_str())
297 .map_or(default_vcs_browser, |s| Some(s.to_string()))
298 }
299
300 pub fn set_vcs_browser(&mut self, browser: &str) -> &mut Self {
302 self.toml_section_mut()["vcs_browser"] = value(browser);
303 self
304 }
305
306 pub fn section(&self) -> &str {
308 self.main
309 .debcargo
310 .get("source")
311 .and_then(|s| s.get("section"))
312 .and_then(|v| v.as_str())
313 .unwrap_or(DEFAULT_SECTION)
314 }
315
316 pub fn set_section(&mut self, section: &str) -> &mut Self {
318 self.toml_section_mut()["section"] = value(section);
319 self
320 }
321
322 pub fn name(&self) -> Option<String> {
324 let crate_name = self.main.crate_name()?;
325 let semver_suffix = self.main.semver_suffix();
326 if semver_suffix {
327 let crate_version = self.main.crate_version()?;
328 Some(format!(
329 "rust-{}-{}",
330 debnormalize(crate_name),
331 semver_pair(&crate_version)
332 ))
333 } else {
334 Some(format!("rust-{}", debnormalize(crate_name)))
335 }
336 }
337
338 pub fn priority(&self) -> debian_control::Priority {
340 self.main
341 .debcargo
342 .get("source")
343 .and_then(|s| s.get("priority"))
344 .and_then(|v| v.as_str())
345 .and_then(|s| s.parse().ok())
346 .unwrap_or(DEFAULT_PRIORITY)
347 }
348
349 pub fn set_priority(&mut self, priority: debian_control::Priority) -> &mut Self {
351 self.toml_section_mut()["priority"] = value(priority.to_string());
352 self
353 }
354
355 pub fn rules_requires_root(&self) -> bool {
357 self.main
358 .debcargo
359 .get("source")
360 .and_then(|s| s.get("requires_root"))
361 .and_then(|v| v.as_bool())
362 .unwrap_or(false)
363 }
364
365 pub fn set_rules_requires_root(&mut self, requires_root: bool) -> &mut Self {
367 self.toml_section_mut()["requires_root"] = value(if requires_root { "yes" } else { "no" });
368 self
369 }
370
371 pub fn maintainer(&self) -> &str {
373 self.main
374 .debcargo
375 .get("source")
376 .and_then(|s| s.get("maintainer"))
377 .and_then(|v| v.as_str())
378 .unwrap_or(DEFAULT_MAINTAINER)
379 }
380
381 pub fn set_maintainer(&mut self, maintainer: &str) -> &mut Self {
383 self.toml_section_mut()["maintainer"] = value(maintainer);
384 self
385 }
386
387 pub fn uploaders(&self) -> Option<Vec<String>> {
389 self.main
390 .debcargo
391 .get("source")
392 .and_then(|s| s.get("uploaders"))
393 .and_then(|x| x.as_array())
394 .map(|a| {
395 a.iter()
396 .filter_map(|v| v.as_str())
397 .map(|s| s.to_string())
398 .collect()
399 })
400 }
401
402 pub fn set_uploaders(&mut self, uploaders: Vec<String>) -> &mut Self {
404 let mut array = toml_edit::Array::new();
405 for u in uploaders {
406 array.push(u);
407 }
408 self.toml_section_mut()["uploaders"] = value(array);
409 self
410 }
411
412 pub fn extra_lines(&self) -> Vec<String> {
414 self.main
415 .debcargo
416 .get("source")
417 .and_then(|s| s.get("extra_lines"))
418 .and_then(|x| x.as_array())
419 .map(|a| {
420 a.iter()
421 .filter_map(|v| v.as_str())
422 .map(|s| s.to_string())
423 .collect()
424 })
425 .unwrap_or_default()
426 }
427
428 pub fn set_extra_lines(&mut self, lines: Vec<String>) -> &mut Self {
430 let mut array = toml_edit::Array::new();
431 for line in lines {
432 array.push(line);
433 }
434 self.toml_section_mut()["extra_lines"] = value(array);
435 self
436 }
437
438 pub fn add_extra_line(&mut self, line: String) -> &mut Self {
440 let mut lines = self.extra_lines();
441 if !lines.contains(&line) {
442 lines.push(line);
443 self.set_extra_lines(lines);
444 }
445 self
446 }
447
448 pub fn remove_extra_line(&mut self, line: &str) -> &mut Self {
450 let lines = self.extra_lines();
451 let filtered: Vec<String> = lines.into_iter().filter(|l| l != line).collect();
452 self.set_extra_lines(filtered);
453 self
454 }
455
456 pub fn get_extra_field(&self, field_name: &str) -> Option<String> {
459 let prefix = format!("{}:", field_name);
460 self.extra_lines()
461 .iter()
462 .find(|line| line.starts_with(&prefix))
463 .map(|line| line[prefix.len()..].trim().to_string())
464 }
465
466 pub fn set_extra_field(&mut self, field_name: &str, value: &str) -> &mut Self {
469 let field_line = format!("{}: {}", field_name, value);
470 let prefix = format!("{}:", field_name);
471
472 let mut lines = self.extra_lines();
473 let mut found = false;
474
475 for line in &mut lines {
477 if line.starts_with(&prefix) {
478 *line = field_line.clone();
479 found = true;
480 break;
481 }
482 }
483
484 if !found {
486 lines.push(field_line);
487 }
488
489 self.set_extra_lines(lines);
490 self
491 }
492
493 pub fn remove_extra_field(&mut self, field_name: &str) -> &mut Self {
495 let prefix = format!("{}:", field_name);
496 let lines = self.extra_lines();
497 let filtered: Vec<String> = lines
498 .into_iter()
499 .filter(|line| !line.starts_with(&prefix))
500 .collect();
501 self.set_extra_lines(filtered);
502 self
503 }
504
505 pub fn set_vcs_url(&mut self, vcs_type: &str, url: &str) -> &mut Self {
508 match vcs_type.to_lowercase().as_str() {
509 "git" => self.set_vcs_git(url),
510 "browser" => self.set_vcs_browser(url),
511 _ => self.set_extra_field(&format!("Vcs-{}", vcs_type), url),
512 }
513 }
514
515 pub fn get_vcs_url(&self, vcs_type: &str) -> Option<String> {
518 match vcs_type.to_lowercase().as_str() {
519 "git" => self.vcs_git(),
520 "browser" => self.vcs_browser(),
521 _ => self.get_extra_field(&format!("Vcs-{}", vcs_type)),
522 }
523 }
524}
525
526#[allow(dead_code)]
527pub struct DebcargoBinary<'a> {
529 table: &'a mut Table,
530 key: String,
531 name: String,
532 section: String,
533 global_summary: Option<String>,
534 global_description: Option<String>,
535 crate_name: String,
536 crate_version: semver::Version,
537 semver_suffix: bool,
538 features: Option<HashSet<String>>,
539}
540
541impl<'a> DebcargoBinary<'a> {
542 fn new(
543 key: String,
544 name: String,
545 table: &'a mut Table,
546 global_summary: Option<String>,
547 global_description: Option<String>,
548 crate_name: String,
549 crate_version: semver::Version,
550 semver_suffix: bool,
551 features: Option<HashSet<String>>,
552 ) -> Self {
553 Self {
554 key: key.to_owned(),
555 name,
556 section: format!("packages.{}", key),
557 table,
558 global_summary,
559 global_description,
560 crate_name,
561 crate_version,
562 semver_suffix,
563 features,
564 }
565 }
566
567 pub fn name(&self) -> &str {
569 &self.name
570 }
571
572 pub fn architecture(&self) -> Option<&str> {
574 Some("any")
575 }
576
577 pub fn multi_arch(&self) -> Option<MultiArch> {
579 Some(MultiArch::Same)
580 }
581
582 pub fn section(&self) -> Option<&str> {
584 self.table["section"].as_str()
585 }
586
587 pub fn summary(&self) -> Option<&str> {
589 if let Some(summary) = self.table.get("summary").and_then(|v| v.as_str()) {
590 Some(summary)
591 } else {
592 self.global_summary.as_deref()
593 }
594 }
595
596 pub fn long_description(&self) -> Option<String> {
598 if let Some(description) = self.table.get("description").and_then(|v| v.as_str()) {
599 Some(description.to_string())
600 } else if let Some(description) = self.global_description.as_ref() {
601 Some(description.clone())
602 } else {
603 match self.key.as_str() {
604 "lib" => Some(format!("Source code for Debianized Rust crate \"{}\"", self.crate_name)),
605 "bin" => Some("This package contains the source for the Rust mio crate, packaged by debcargo for use with cargo and dh-cargo.".to_string()),
606 _ => None,
607 }
608 }
609 }
610
611 pub fn description(&self) -> Option<String> {
613 Some(crate::control::format_description(
614 self.summary()?,
615 self.long_description()?.lines().collect(),
616 ))
617 }
618
619 pub fn depends(&self) -> Option<&str> {
621 self.table["depends"].as_str()
622 }
623
624 pub fn recommends(&self) -> Option<&str> {
626 self.table["recommends"].as_str()
627 }
628
629 pub fn suggests(&self) -> Option<&str> {
631 self.table["suggests"].as_str()
632 }
633
634 #[allow(dead_code)]
635 fn default_provides(&self) -> Option<String> {
636 let mut ret = HashSet::new();
637 let semver_suffix = self.semver_suffix;
638 let semver = &self.crate_version;
639
640 let mut suffixes = vec![];
641 if !semver_suffix {
642 suffixes.push("".to_string());
643 }
644
645 suffixes.push(format!("-{}", semver.major));
646 suffixes.push(format!("-{}.{}", semver.major, semver.minor));
647 suffixes.push(format!(
648 "-{}.{}.{}",
649 semver.major, semver.minor, semver.patch
650 ));
651 for ver_suffix in suffixes {
652 let mut feature_suffixes = HashSet::new();
653 feature_suffixes.insert("".to_string());
654 feature_suffixes.insert("+default".to_string());
655 feature_suffixes.extend(
656 self.features
657 .as_ref()
658 .map(|k| k.iter().map(|k| format!("+{}", k)).collect::<HashSet<_>>())
659 .unwrap_or_default(),
660 );
661 for feature_suffix in feature_suffixes {
662 ret.insert(debcargo_binary_name(
663 &self.crate_name,
664 &format!("{}{}", ver_suffix, &feature_suffix),
665 ));
666 }
667 }
668 ret.remove(self.name());
669 if ret.is_empty() {
670 None
671 } else {
672 Some(format!(
673 "\n{}",
674 &ret.iter()
675 .map(|s| format!("{} (= ${{binary:Version}})", s))
676 .collect::<Vec<_>>()
677 .join(",\n ")
678 ))
679 }
680 }
681}
682
683fn debnormalize(s: &str) -> String {
684 s.to_lowercase().replace('_', "-")
685}
686
687fn semver_pair(s: &semver::Version) -> String {
688 format!("{}.{}", s.major, s.minor)
689}
690
691fn debcargo_binary_name(crate_name: &str, suffix: &str) -> String {
692 format!("librust-{}{}-dev", debnormalize(crate_name), suffix)
693}
694
695pub fn unmangle_debcargo_version(version: &str) -> String {
697 version.replace("~", "-")
698}
699
700#[cfg(test)]
701mod tests {
702 #[test]
703 fn test_debcargo_binary_name() {
704 assert_eq!(super::debcargo_binary_name("foo", ""), "librust-foo-dev");
705 assert_eq!(
706 super::debcargo_binary_name("foo", "-1"),
707 "librust-foo-1-dev"
708 );
709 assert_eq!(
710 super::debcargo_binary_name("foo", "-1.2"),
711 "librust-foo-1.2-dev"
712 );
713 assert_eq!(
714 super::debcargo_binary_name("foo", "-1.2.3"),
715 "librust-foo-1.2.3-dev"
716 );
717 }
718
719 #[test]
720 fn test_semver_pair() {
721 assert_eq!(super::semver_pair(&"1.2.3".parse().unwrap()), "1.2");
722 assert_eq!(super::semver_pair(&"1.2.6".parse().unwrap()), "1.2");
723 }
724
725 #[test]
726 fn test_debnormalize() {
727 assert_eq!(super::debnormalize("foo_bar"), "foo-bar");
728 assert_eq!(super::debnormalize("foo"), "foo");
729 }
730
731 #[test]
732 fn test_debcargo_editor() {
733 let mut editor = super::DebcargoEditor::new();
734 editor.debcargo["source"]["standards-version"] = toml_edit::value("4.5.1");
735 editor.debcargo["source"]["homepage"] = toml_edit::value("https://example.com");
736 editor.debcargo["source"]["vcs_git"] = toml_edit::value("https://example.com");
737 editor.debcargo["source"]["vcs_browser"] = toml_edit::value("https://example.com");
738 editor.debcargo["source"]["section"] = toml_edit::value("notrust");
739 editor.debcargo["source"]["priority"] = toml_edit::value("optional");
740 editor.debcargo["source"]["requires_root"] = toml_edit::value("no");
741 editor.debcargo["source"]["maintainer"] =
742 toml_edit::value("Jelmer Vernooij <jelmer@debian.org>");
743
744 assert_eq!(editor.source().standards_version(), "4.5.1");
745 assert_eq!(
746 editor.source().vcs_git().as_deref(),
747 Some("https://example.com")
748 );
749 assert_eq!(
750 editor.source().vcs_browser().as_deref(),
751 Some("https://example.com")
752 );
753 assert_eq!(editor.source().section(), "notrust");
754 assert_eq!(editor.source().priority(), super::DEFAULT_PRIORITY);
755 assert!(!editor.source().rules_requires_root());
756 assert_eq!(
757 editor.source().maintainer(),
758 "Jelmer Vernooij <jelmer@debian.org>"
759 );
760 assert_eq!(editor.source().name(), None);
761 assert_eq!(editor.source().uploaders(), None);
762 assert_eq!(editor.source().homepage(), Some("https://example.com"));
763 }
764
765 #[test]
766 fn test_extra_lines_manipulation() {
767 let mut editor = super::DebcargoEditor::new();
768 let mut source = editor.source();
769
770 assert_eq!(source.extra_lines(), Vec::<String>::new());
772
773 source.set_extra_lines(vec![
775 "Vcs-Svn: https://svn.example.com/repo".to_string(),
776 "X-Custom: value".to_string(),
777 ]);
778 assert_eq!(
779 source.extra_lines(),
780 vec![
781 "Vcs-Svn: https://svn.example.com/repo".to_string(),
782 "X-Custom: value".to_string(),
783 ]
784 );
785
786 source.add_extra_line("Another-Field: another value".to_string());
788 assert_eq!(
789 source.extra_lines(),
790 vec![
791 "Vcs-Svn: https://svn.example.com/repo".to_string(),
792 "X-Custom: value".to_string(),
793 "Another-Field: another value".to_string(),
794 ]
795 );
796
797 source.add_extra_line("X-Custom: value".to_string());
799 assert_eq!(
800 source.extra_lines(),
801 vec![
802 "Vcs-Svn: https://svn.example.com/repo".to_string(),
803 "X-Custom: value".to_string(),
804 "Another-Field: another value".to_string(),
805 ]
806 );
807
808 source.remove_extra_line("X-Custom: value");
810 assert_eq!(
811 source.extra_lines(),
812 vec![
813 "Vcs-Svn: https://svn.example.com/repo".to_string(),
814 "Another-Field: another value".to_string(),
815 ]
816 );
817 }
818
819 #[test]
820 fn test_extra_field_manipulation() {
821 let mut editor = super::DebcargoEditor::new();
822 let mut source = editor.source();
823
824 assert_eq!(source.get_extra_field("Vcs-Svn"), None);
826
827 source.set_extra_field("Vcs-Svn", "https://svn.example.com/repo");
829 assert_eq!(
830 source.get_extra_field("Vcs-Svn"),
831 Some("https://svn.example.com/repo".to_string())
832 );
833
834 source.set_extra_field("Vcs-Svn", "https://svn.example.com/new-repo");
836 assert_eq!(
837 source.get_extra_field("Vcs-Svn"),
838 Some("https://svn.example.com/new-repo".to_string())
839 );
840 assert_eq!(
842 source.extra_lines(),
843 vec!["Vcs-Svn: https://svn.example.com/new-repo".to_string()]
844 );
845
846 source.set_extra_field("X-Custom", "custom value");
848 assert_eq!(
849 source.get_extra_field("X-Custom"),
850 Some("custom value".to_string())
851 );
852 assert_eq!(
853 source.get_extra_field("Vcs-Svn"),
854 Some("https://svn.example.com/new-repo".to_string())
855 );
856 assert_eq!(
857 source.extra_lines(),
858 vec![
859 "Vcs-Svn: https://svn.example.com/new-repo".to_string(),
860 "X-Custom: custom value".to_string(),
861 ]
862 );
863
864 source.remove_extra_field("Vcs-Svn");
866 assert_eq!(source.get_extra_field("Vcs-Svn"), None);
867 assert_eq!(
868 source.get_extra_field("X-Custom"),
869 Some("custom value".to_string())
870 );
871 assert_eq!(
872 source.extra_lines(),
873 vec!["X-Custom: custom value".to_string()]
874 );
875 }
876
877 #[test]
878 fn test_set_vcs_url() {
879 let mut editor = super::DebcargoEditor::new();
880 let mut source = editor.source();
881
882 source.set_vcs_url("Git", "https://github.com/example/repo.git");
884 assert_eq!(
885 source.vcs_git(),
886 Some("https://github.com/example/repo.git".to_string())
887 );
888
889 source.set_vcs_url("Browser", "https://github.com/example/repo");
891 assert_eq!(
892 source.vcs_browser(),
893 Some("https://github.com/example/repo".to_string())
894 );
895
896 source.set_vcs_url("Svn", "https://svn.example.com/repo");
898 assert_eq!(
899 source.get_extra_field("Vcs-Svn"),
900 Some("https://svn.example.com/repo".to_string())
901 );
902
903 source.set_vcs_url("Bzr", "https://bzr.example.com/repo");
905 assert_eq!(
906 source.get_extra_field("Vcs-Bzr"),
907 Some("https://bzr.example.com/repo".to_string())
908 );
909
910 source.set_vcs_url("git", "https://gitlab.com/example/repo.git");
912 assert_eq!(
913 source.vcs_git(),
914 Some("https://gitlab.com/example/repo.git".to_string())
915 );
916
917 source.set_vcs_url("browser", "https://gitlab.com/example/repo");
918 assert_eq!(
919 source.vcs_browser(),
920 Some("https://gitlab.com/example/repo".to_string())
921 );
922 }
923
924 #[test]
925 fn test_extra_field_parsing() {
926 let mut editor = super::DebcargoEditor::new();
927 let mut source = editor.source();
928
929 source.set_extra_lines(vec!["Vcs-Svn: https://svn.example.com/repo".to_string()]);
931 assert_eq!(
932 source.get_extra_field("Vcs-Svn"),
933 Some("https://svn.example.com/repo".to_string())
934 );
935
936 source.set_extra_lines(vec!["Vcs-Bzr:https://bzr.example.com/repo".to_string()]);
938 assert_eq!(
939 source.get_extra_field("Vcs-Bzr"),
940 Some("https://bzr.example.com/repo".to_string())
941 );
942 }
943
944 #[test]
945 fn test_get_vcs_url() {
946 let mut editor = super::DebcargoEditor::new();
947 let mut source = editor.source();
948
949 source.set_vcs_git("https://github.com/example/repo.git");
951 source.set_vcs_browser("https://github.com/example/repo");
952 source.set_extra_field("Vcs-Svn", "https://svn.example.com/repo");
953 source.set_extra_field("Vcs-Bzr", "https://bzr.example.com/repo");
954
955 assert_eq!(
957 source.get_vcs_url("Git"),
958 Some("https://github.com/example/repo.git".to_string())
959 );
960 assert_eq!(
961 source.get_vcs_url("git"),
962 Some("https://github.com/example/repo.git".to_string())
963 );
964
965 assert_eq!(
967 source.get_vcs_url("Browser"),
968 Some("https://github.com/example/repo".to_string())
969 );
970 assert_eq!(
971 source.get_vcs_url("browser"),
972 Some("https://github.com/example/repo".to_string())
973 );
974
975 assert_eq!(
977 source.get_vcs_url("Svn"),
978 Some("https://svn.example.com/repo".to_string())
979 );
980 assert_eq!(
981 source.get_vcs_url("Bzr"),
982 Some("https://bzr.example.com/repo".to_string())
983 );
984
985 assert_eq!(source.get_vcs_url("Hg"), None);
987 }
988}