1pub mod case_map;
2pub mod control;
3pub mod errors;
4pub mod packages;
5pub mod release;
6
7pub use control::*;
8pub use packages::*;
9pub use release::*;
10
11use case_map::CaseMap;
12use errors::KVError;
13use regex::Regex;
14
15#[warn(clippy::all)]
16#[warn(clippy::correctness)]
17#[warn(clippy::suspicious)]
18#[warn(clippy::pedantic)]
19#[warn(clippy::style)]
20#[warn(clippy::complexity)]
21#[warn(clippy::perf)]
22
23pub fn parse_kv(raw_apt_data: &str) -> Result<CaseMap, KVError> {
25 let binding = raw_apt_data.replace("\r\n", "\n").replace('\0', "");
27 let apt_data = binding.trim().split('\n');
28
29 let mut fields = CaseMap::new();
30 let mut current_key = "";
31
32 let regex = match Regex::new(r"^(.*?):\s(.*)$") {
34 Ok(regex) => regex,
35 Err(_) => return Err(KVError),
36 };
37
38 for line in apt_data {
39 let line = line.trim();
40
41 if line.is_empty() {
42 continue;
43 }
44
45 if !regex.is_match(line) {
46 if line.ends_with(':') {
47 let mut chars = line.chars();
48 chars.next_back(); current_key = chars.as_str();
51 fields.insert(current_key, "");
52 continue;
53 }
54
55 if !current_key.is_empty() {
56 let existing_value = match fields.get(current_key) {
57 Some(value) => value,
58 None => "",
59 };
60
61 if line == "." {
63 let updated_key = format!("{existing_value}\n\n");
64 fields.insert(current_key, &updated_key);
65 } else {
66 let updated_key = match existing_value.ends_with('\n') {
67 true => format!("{existing_value}{line}"),
68 false => format!("{existing_value} {line}"),
69 };
70
71 fields.insert(current_key, &updated_key);
72 }
73
74 continue;
75 }
76 }
77
78 let captures = regex.captures(line);
79 if captures.is_none() {
80 return Err(KVError);
81 }
82
83 let captures = captures.unwrap();
84 let key = captures.get(1);
85 let value = captures.get(2);
86
87 if key.is_none() || value.is_none() {
88 return Err(KVError);
89 };
90
91 let key = key.unwrap().as_str();
92 let value = value.unwrap().as_str();
93
94 if fields.contains_key(key) {
95 continue;
96 }
97
98 if key.to_lowercase() == "description" && !value.is_empty() {
99 let format = format!("{value}\n");
100 fields.insert(key, &format);
101 } else {
102 if current_key.to_lowercase() == "description" {
103 let existing_value = match fields.get(current_key) {
104 Some(value) => value,
105 None => "",
106 };
107
108 if existing_value.ends_with('\n') {
109 let substring = existing_value
110 .chars()
111 .take(existing_value.len() - 1)
112 .collect::<String>();
113
114 fields.insert(current_key, &substring);
115 }
116 }
117
118 fields.insert(key, value);
119 }
120
121 current_key = key;
122 }
123
124 Ok(fields)
125}
126
127pub fn make_array(raw_data: Option<&String>) -> Option<Vec<String>> {
128 return match raw_data {
129 Some(raw_data) => {
130 let mut data = Vec::new();
131 for line in raw_data.split(',') {
132 data.push(line.trim().to_string());
133 }
134
135 Some(data)
136 }
137 None => None,
138 };
139}