summaryrefslogtreecommitdiff
path: root/crates/tor-config/src/err.rs
blob: 6114a7a66ca42612fd08fcfd4147f77be06f1a1a (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
112
113
114
115
116
117
118
//! Declare an error type.

/// An error related to an option passed to Arti via a configuration
/// builder.
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
pub enum ConfigBuildError {
    /// A mandatory field was not present.
    #[error("Field was not provided: {field}")]
    MissingField {
        /// The name of the missing field.
        field: String,
    },
    /// A single field had a value that proved to be unusable.
    #[error("Value of {field} was incorrect: {problem}")]
    Invalid {
        /// The name of the invalid field
        field: String,
        /// A description of the problem.
        problem: String,
    },
    /// Multiple fields are inconsistent.
    #[error("Fields {fields:?} are inconsistent: {problem}")]
    Inconsistent {
        /// The names of the inconsistent fields
        fields: Vec<String>,
        /// The problem that makes them inconsistent
        problem: String,
    },
}

impl From<derive_builder::UninitializedFieldError> for ConfigBuildError {
    fn from(val: derive_builder::UninitializedFieldError) -> Self {
        ConfigBuildError::MissingField {
            field: val.field_name().to_string(),
        }
    }
}

impl ConfigBuildError {
    /// Return a new ConfigBuildError that prefixes its field name with
    /// `prefix` and a dot.
    pub fn within(&self, prefix: &str) -> Self {
        use ConfigBuildError::*;
        match self {
            MissingField { field } => MissingField {
                field: format!("{}.{}", prefix, field),
            },
            Invalid { field, problem } => Invalid {
                field: format!("{}.{}", prefix, field),
                problem: problem.clone(),
            },
            Inconsistent { fields, problem } => Inconsistent {
                fields: fields.iter().map(|f| format!("{}.{}", prefix, f)).collect(),
                problem: problem.clone(),
            },
        }
    }
}

#[cfg(test)]
mod test {
    #![allow(clippy::unwrap_used)]
    use super::*;

    #[test]
    fn within() {
        let e1 = ConfigBuildError::MissingField {
            field: "lettuce".to_owned(),
        };
        let e2 = ConfigBuildError::Invalid {
            field: "tomato".to_owned(),
            problem: "too crunchy".to_owned(),
        };
        let e3 = ConfigBuildError::Inconsistent {
            fields: vec!["mayo".to_owned(), "avocado".to_owned()],
            problem: "pick one".to_owned(),
        };

        assert_eq!(
            &e1.within("sandwich").to_string(),
            "Field was not provided: sandwich.lettuce"
        );
        assert_eq!(
            &e2.within("sandwich").to_string(),
            "Value of sandwich.tomato was incorrect: too crunchy"
        );
        assert_eq!(
            &e3.within("sandwich").to_string(),
            r#"Fields ["sandwich.mayo", "sandwich.avocado"] are inconsistent: pick one"#
        );
    }

    #[derive(derive_builder::Builder, Debug)]
    #[builder(build_fn(error = "ConfigBuildError"))]
    #[allow(dead_code)]
    struct Cephalopod {
        // arms have suction cups for their whole length
        arms: u8,
        // Tentacles have suction cups at the ends
        tentacles: u8,
    }

    #[test]
    fn build_err() {
        let squid = CephalopodBuilder::default().arms(8).tentacles(2).build();
        let octopus = CephalopodBuilder::default().arms(8).build();
        assert!(squid.is_ok());
        let squid = squid.unwrap();
        assert_eq!(squid.arms, 8);
        assert_eq!(squid.tentacles, 2);
        assert!(octopus.is_err());
        assert_eq!(
            &octopus.unwrap_err().to_string(),
            "Field was not provided: tentacles"
        );
    }
}