aboutsummaryrefslogtreecommitdiff
path: root/src/makefile/inference_rules.rs
blob: 368d72bb2839d6e5e8a61b87ff0e6f67d42190c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use std::fmt;

use eyre::{eyre, Result};
use regex::Captures;

use super::command_line::CommandLine;
use super::pattern::r#match;
use super::ItemSource;

#[derive(PartialEq, Eq, Clone, Debug)]
pub struct InferenceRule {
    pub source: ItemSource,
    pub products: Vec<String>,
    pub prerequisites: Vec<String>,
    pub commands: Vec<CommandLine>,
}

impl InferenceRule {
    /// s1 is the product, s2 is the prereq
    pub fn new_suffix(
        source: ItemSource,
        s1: String,
        s2: String,
        commands: Vec<CommandLine>,
    ) -> Self {
        Self {
            source,
            products: vec![format!("%{}", s1)],
            prerequisites: vec![format!("%{}", s2)],
            commands,
        }
    }

    pub fn first_match<'s, 't: 's>(&'s self, target_name: &'t str) -> Result<Option<Captures<'t>>> {
        self.products
            .iter()
            .map(|pattern| r#match(pattern, target_name))
            .try_fold(None, |x, y| y.map(|y| x.or(y)))
    }

    pub fn matches(&self, target_name: &str) -> Result<bool> {
        self.first_match(target_name).map(|x| x.is_some())
    }

    pub fn prereqs<'s>(
        &'s self,
        target_name: &'s str,
    ) -> Result<impl Iterator<Item = String> + 's> {
        let capture = self
            .first_match(target_name)?
            .ok_or_else(|| eyre!("asked non-matching inference rule for prerequisites"))?;
        let percent_expansion = capture.get(1).expect("should've matched the %").as_str();
        Ok(self
            .prerequisites
            .iter()
            .map(move |p| p.replace('%', percent_expansion)))
    }
}

impl fmt::Display for InferenceRule {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(
            f,
            "{}: {}",
            self.products.join(" "),
            self.prerequisites.join(" ")
        )?;
        for command in &self.commands {
            writeln!(f, "\t{}", command)?;
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    type R = eyre::Result<()>;

    #[test]
    fn suffix_match() -> R {
        let rule = InferenceRule::new_suffix(
            ItemSource::Builtin,
            ".o".to_owned(),
            ".c".to_owned(),
            vec![],
        );
        assert!(rule.matches("foo.o")?);
        assert!(rule.matches("dir/foo.o")?);
        Ok(())
    }

    #[cfg(feature = "full")]
    #[test]
    fn percent_match() -> R {
        // thanks, SPDX License List
        let rule = InferenceRule {
            source: ItemSource::Builtin,
            products: vec!["licenseListPublisher-%.jar-valid".to_owned()],
            prerequisites: vec![
                "licenseListPublisher-%.jar.asc".to_owned(),
                "licenseListPublisher-%.jar".to_owned(),
                "goneall.gpg".to_owned(),
            ],
            commands: vec![],
        };
        assert!(rule.matches("licenseListPublisher-2.2.1.jar-valid")?);
        Ok(())
    }
}