1use url::Url;
5
6pub const LAUNCHPAD_PPA_URL: &str = "https://ppa.launchpadcontent.net";
8
9pub const VALID_PPA_COMPONENTS: &[&str] = &["main", "main/debug"];
11
12#[derive(Debug, Clone)]
14pub struct PpaInfo {
15 pub user: String,
17 pub name: String,
19}
20
21impl PpaInfo {
22 pub fn parse(ppa_spec: &str) -> Result<PpaInfo, String> {
24 if !ppa_spec.starts_with("ppa:") {
25 return Err("Not a PPA format".to_string());
26 }
27
28 let ppa_part = &ppa_spec[4..];
29 let parts: Vec<&str> = ppa_part.split('/').collect();
30
31 if parts.len() != 2 {
32 return Err("Invalid PPA format. Expected ppa:user/ppa-name".to_string());
33 }
34
35 Ok(PpaInfo {
36 user: parts[0].to_string(),
37 name: parts[1].to_string(),
38 })
39 }
40
41 pub fn repository_url(&self, _codename: &str) -> Result<Url, String> {
43 Url::parse(&format!(
44 "{}/{}/{}/ubuntu",
45 LAUNCHPAD_PPA_URL, self.user, self.name
46 ))
47 .map_err(|e| format!("Failed to construct PPA URL: {}", e))
48 }
49
50 pub fn filename(&self, extension: &str) -> String {
52 format!("{}-ubuntu-{}.{}", self.user, self.name, extension)
53 }
54}
55
56pub fn validate_ppa_components(components: &[String]) -> Result<(), String> {
58 for component in components {
59 if !VALID_PPA_COMPONENTS.contains(&component.as_str()) {
60 return Err(format!(
61 "Invalid component '{}' for PPA.\n\
62 Valid components are: {}\n\
63 Suggestion: Use 'main' for regular packages or 'main/debug' for debug symbols.",
64 component,
65 VALID_PPA_COMPONENTS.join(", ")
66 ));
67 }
68 }
69 Ok(())
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test_parse_ppa_format() {
78 let ppa = PpaInfo::parse("ppa:user/repo").unwrap();
80 assert_eq!(ppa.user, "user");
81 assert_eq!(ppa.name, "repo");
82
83 assert!(PpaInfo::parse("not-a-ppa").is_err());
85 assert!(PpaInfo::parse("ppa:invalid").is_err());
86 assert!(PpaInfo::parse("ppa:too/many/parts").is_err());
87 }
88
89 #[test]
90 fn test_validate_ppa_components() {
91 assert!(validate_ppa_components(&["main".to_string()]).is_ok());
92 assert!(validate_ppa_components(&["main/debug".to_string()]).is_ok());
93 assert!(validate_ppa_components(&["invalid".to_string()]).is_err());
94 }
95
96 #[test]
97 fn test_ppa_filename() {
98 let ppa = PpaInfo {
99 user: "test-user".to_string(),
100 name: "test-repo".to_string(),
101 };
102
103 assert_eq!(ppa.filename("list"), "test-user-ubuntu-test-repo.list");
104 assert_eq!(
105 ppa.filename("sources"),
106 "test-user-ubuntu-test-repo.sources"
107 );
108
109 assert_eq!(ppa.filename(""), "test-user-ubuntu-test-repo.");
111
112 let ppa_special = PpaInfo {
114 user: "user_123".to_string(),
115 name: "repo-name".to_string(),
116 };
117 assert_eq!(
118 ppa_special.filename("list"),
119 "user_123-ubuntu-repo-name.list"
120 );
121 }
122}