debian_analyzer/
rules.rs

1//! This module provides functions to manipulate debian/rules file.
2
3/// Add a particular value to a with argument.
4pub fn dh_invoke_add_with(line: &str, with_argument: &str) -> String {
5    if line.contains(with_argument) {
6        return line.to_owned();
7    }
8    if !line.contains(" --with") {
9        return format!("{} --with={}", line, with_argument);
10    }
11
12    lazy_regex::regex_replace!(
13        r"([ \t])--with([ =])([^ \t]+)",
14        line,
15        |_, head, _with, tail| format!("{}--with={},{}", head, with_argument, tail)
16    )
17    .to_string()
18}
19
20/// Obtain the value of a with argument.
21pub fn dh_invoke_get_with(line: &str) -> Vec<String> {
22    let mut ret = Vec::new();
23    for cap in lazy_regex::regex!("[ \t]--with[ =]([^ \t]+)").captures_iter(line) {
24        if let Some(m) = cap.get(1) {
25            ret.extend(m.as_str().split(',').map(|s| s.to_owned()));
26        }
27    }
28    ret
29}
30
31/// Drop a particular value from a with argument.
32///
33/// # Arguments
34/// * `line` - The command line to modify
35/// * `with_argument` - The with argument to remove
36///
37/// # Returns
38/// The modified line with the argument removed
39///
40/// # Examples
41/// ```rust
42/// use debian_analyzer::rules::dh_invoke_drop_with;
43/// assert_eq!(
44///     dh_invoke_drop_with("dh $@ --with=foo,bar", "foo"),
45///     "dh $@ --with=bar"
46/// );
47/// assert_eq!(
48///     dh_invoke_drop_with("dh $@ --with=foo", "foo"),
49///     "dh $@"
50/// );
51/// ```
52pub fn dh_invoke_drop_with(line: &str, with_argument: &str) -> String {
53    if !line.contains(with_argument) {
54        return line.to_owned();
55    }
56
57    let mut result = line.to_owned();
58    let escaped = regex::escape(with_argument);
59
60    // It's the only with argument
61    if let Ok(re) = regex::Regex::new(&format!(r"[ \t]--with[ =]{}( .+|)$", escaped)) {
62        result = re.replace(&result, "$1").to_string();
63    }
64
65    // It's at the beginning
66    if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =]){},", escaped)) {
67        result = re.replace(&result, "${1}--with${2}").to_string();
68    }
69
70    // It's in the middle or end
71    if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =])(.+),{}([ ,])", escaped)) {
72        result = re.replace(&result, "${1}--with${2}${3}${4}").to_string();
73    }
74
75    // It's at the end
76    if let Ok(re) = regex::Regex::new(&format!(r"([ \t])--with([ =])(.+),{}$", escaped)) {
77        result = re.replace(&result, "${1}--with${2}${3}").to_string();
78    }
79
80    result
81}
82
83/// Drop a particular argument from a dh invocation.
84///
85/// # Arguments
86/// * `line` - The command line to modify
87/// * `argument` - The argument to remove
88///
89/// # Returns
90/// The modified line with the argument removed
91///
92/// # Examples
93/// ```rust
94/// use debian_analyzer::rules::dh_invoke_drop_argument;
95/// assert_eq!(
96///     dh_invoke_drop_argument("dh $@ --foo --bar", "--foo"),
97///     "dh $@ --bar"
98/// );
99/// ```
100pub fn dh_invoke_drop_argument(line: &str, argument: &str) -> String {
101    if !line.contains(argument) {
102        return line.to_owned();
103    }
104
105    let mut result = line.to_owned();
106    let escaped = regex::escape(argument);
107
108    // At the end
109    if let Ok(re) = regex::Regex::new(&format!(r"[ \t]+{}$", escaped)) {
110        result = re.replace(&result, "").to_string();
111    }
112
113    // In the middle
114    if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}[ \t]", escaped)) {
115        result = re.replace(&result, "$1").to_string();
116    }
117
118    result
119}
120
121/// Replace one argument with another in a dh invocation.
122///
123/// # Arguments
124/// * `line` - The command line to modify
125/// * `old` - The argument to replace
126/// * `new` - The new argument value
127///
128/// # Returns
129/// The modified line with the argument replaced
130///
131/// # Examples
132/// ```rust
133/// use debian_analyzer::rules::dh_invoke_replace_argument;
134/// assert_eq!(
135///     dh_invoke_replace_argument("dh $@ --foo", "--foo", "--bar"),
136///     "dh $@ --bar"
137/// );
138/// ```
139pub fn dh_invoke_replace_argument(line: &str, old: &str, new: &str) -> String {
140    if !line.contains(old) {
141        return line.to_owned();
142    }
143
144    let mut result = line.to_owned();
145    let escaped = regex::escape(old);
146
147    // At the end
148    if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}$", escaped)) {
149        result = re.replace(&result, format!("$1{}", new)).to_string();
150    }
151
152    // In the middle
153    if let Ok(re) = regex::Regex::new(&format!(r"([ \t]){}([ \t])", escaped)) {
154        result = re.replace(&result, format!("$1{}$2", new)).to_string();
155    }
156
157    result
158}
159
160/// Check if a debian/rules file uses CDBS.
161///
162/// # Arguments
163/// * `path` - Path to the debian/rules file
164///
165/// # Returns
166/// true if the file includes CDBS, false otherwise
167///
168/// # Examples
169/// ```rust,no_run
170/// use debian_analyzer::rules::check_cdbs;
171/// use std::path::Path;
172/// assert!(!check_cdbs(Path::new("debian/rules")));
173/// ```
174pub fn check_cdbs(path: &std::path::Path) -> bool {
175    let Ok(contents) = std::fs::read(path) else {
176        return false;
177    };
178
179    for line in contents.split(|&b| b == b'\n') {
180        let trimmed = line.strip_prefix(b"-").unwrap_or(line);
181        if trimmed.starts_with(b"include /usr/share/cdbs/") {
182            return true;
183        }
184    }
185    false
186}
187
188#[cfg(test)]
189mod tests {
190    use super::*;
191
192    #[test]
193    fn test_dh_invoke_add_with() {
194        assert_eq!(dh_invoke_add_with("dh", "blah"), "dh --with=blah");
195        assert_eq!(
196            dh_invoke_add_with("dh --with=foo", "blah"),
197            "dh --with=blah,foo"
198        );
199        assert_eq!(
200            dh_invoke_add_with("dh --with=foo --other", "blah"),
201            "dh --with=blah,foo --other"
202        );
203    }
204
205    #[test]
206    fn test_dh_invoke_get_with() {
207        assert_eq!(dh_invoke_get_with("dh --with=blah --foo"), vec!["blah"]);
208        assert_eq!(dh_invoke_get_with("dh --with=blah"), vec!["blah"]);
209        assert_eq!(
210            dh_invoke_get_with("dh --with=blah,blie"),
211            vec!["blah", "blie"]
212        );
213    }
214
215    #[test]
216    fn test_dh_invoke_drop_with() {
217        assert_eq!(dh_invoke_drop_with("dh --with=blah", "blah"), "dh");
218        assert_eq!(
219            dh_invoke_drop_with("dh --with=blah,foo", "blah"),
220            "dh --with=foo"
221        );
222        assert_eq!(
223            dh_invoke_drop_with("dh --with=blah,foo --other", "blah"),
224            "dh --with=foo --other"
225        );
226        assert_eq!(dh_invoke_drop_with("dh --with=blah", "blah"), "dh");
227        assert_eq!(
228            dh_invoke_drop_with("dh --with=foo,blah", "blah"),
229            "dh --with=foo"
230        );
231        assert_eq!(
232            dh_invoke_drop_with(
233                "dh $@ --verbose --with autoreconf,systemd,cme-upgrade",
234                "systemd"
235            ),
236            "dh $@ --verbose --with autoreconf,cme-upgrade"
237        );
238        assert_eq!(
239            dh_invoke_drop_with(
240                "dh $@ --with gir,python3,sphinxdoc,systemd --without autoreconf --buildsystem=cmake",
241                "systemd"
242            ),
243            "dh $@ --with gir,python3,sphinxdoc --without autoreconf --buildsystem=cmake"
244        );
245        assert_eq!(
246            dh_invoke_drop_with("dh $@ --with systemd", "systemd"),
247            "dh $@"
248        );
249    }
250
251    #[test]
252    fn test_dh_invoke_drop_argument() {
253        assert_eq!(
254            dh_invoke_drop_argument("dh $@ --foo --bar", "--foo"),
255            "dh $@ --bar"
256        );
257        assert_eq!(
258            dh_invoke_drop_argument("dh $@ --foo --bar", "--bar"),
259            "dh $@ --foo"
260        );
261        assert_eq!(dh_invoke_drop_argument("dh $@ --foo", "--foo"), "dh $@");
262    }
263
264    #[test]
265    fn test_dh_invoke_replace_argument() {
266        assert_eq!(
267            dh_invoke_replace_argument("dh $@ --foo", "--foo", "--bar"),
268            "dh $@ --bar"
269        );
270        assert_eq!(
271            dh_invoke_replace_argument("dh $@ --foo --baz", "--foo", "--bar"),
272            "dh $@ --bar --baz"
273        );
274    }
275}