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
//! Helper methods for [`glib::KeyFile`].

use glib::GString;
use ostree::glib;

/// Helper methods for [`glib::KeyFile`].
pub trait KeyFileExt {
    /// Get a string value, but return `None` if the key does not exist.
    fn optional_string(&self, group: &str, key: &str) -> Result<Option<GString>, glib::Error>;
    /// Get a boolean value, but return `None` if the key does not exist.
    fn optional_bool(&self, group: &str, key: &str) -> Result<Option<bool>, glib::Error>;
    /// Get a string list value, but return `None` if the key does not exist.
    fn optional_string_list(
        &self,
        group: &str,
        key: &str,
    ) -> Result<Option<Vec<GString>>, glib::Error>;
}

/// Consume a keyfile error, mapping the case where group or key is not found to `Ok(None)`.
pub fn map_keyfile_optional<T>(res: Result<T, glib::Error>) -> Result<Option<T>, glib::Error> {
    match res {
        Ok(v) => Ok(Some(v)),
        Err(e) => {
            if let Some(t) = e.kind::<glib::KeyFileError>() {
                match t {
                    glib::KeyFileError::GroupNotFound | glib::KeyFileError::KeyNotFound => Ok(None),
                    _ => Err(e),
                }
            } else {
                Err(e)
            }
        }
    }
}

impl KeyFileExt for glib::KeyFile {
    fn optional_string(&self, group: &str, key: &str) -> Result<Option<GString>, glib::Error> {
        map_keyfile_optional(self.string(group, key))
    }

    fn optional_bool(&self, group: &str, key: &str) -> Result<Option<bool>, glib::Error> {
        map_keyfile_optional(self.boolean(group, key))
    }

    fn optional_string_list(
        &self,
        group: &str,
        key: &str,
    ) -> Result<Option<Vec<GString>>, glib::Error> {
        map_keyfile_optional(self.string_list(group, key))
    }
}

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

    #[test]
    fn test_optional() {
        let kf = glib::KeyFile::new();
        assert_eq!(kf.optional_string("foo", "bar").unwrap(), None);
        kf.set_string("foo", "baz", "someval");
        assert_eq!(kf.optional_string("foo", "bar").unwrap(), None);
        assert_eq!(
            kf.optional_string("foo", "baz").unwrap().unwrap(),
            "someval"
        );

        assert!(kf.optional_bool("foo", "baz").is_err());
        assert_eq!(kf.optional_bool("foo", "bar").unwrap(), None);
        kf.set_boolean("foo", "somebool", false);
        assert_eq!(kf.optional_bool("foo", "somebool").unwrap(), Some(false));

        assert_eq!(kf.optional_string_list("foo", "bar").unwrap(), None);
        kf.set_string("foo", "somelist", "one;two;three");
        assert_eq!(
            kf.optional_string_list("foo", "somelist").unwrap(),
            Some(
                vec!["one", "two", "three"]
                    .iter()
                    .map(|&v| GString::from(v))
                    .collect()
            )
        );
    }
}