Skip to main content

libgir/config/
signals.rs

1use std::str::FromStr;
2
3use log::error;
4use toml::Value;
5
6use super::{
7    error::TomlHelper,
8    functions::Return,
9    gobjects::GStatus,
10    ident::Ident,
11    parameter_matchable::Functionlike,
12    parsable::{Parsable, Parse},
13};
14use crate::{library, version::Version};
15
16#[derive(Clone, Copy, Debug)]
17pub enum TransformationType {
18    None,
19    Borrow, // replace from_glib_none to from_glib_borrow
20    // TODO: configure
21    TreePath, // convert string to TreePath
22}
23
24impl FromStr for TransformationType {
25    type Err = String;
26    fn from_str(s: &str) -> Result<Self, Self::Err> {
27        match s {
28            "none" => Ok(Self::None),
29            "borrow" => Ok(Self::Borrow),
30            "treepath" => Ok(Self::TreePath),
31            _ => Err(format!("Wrong transformation \"{s}\"")),
32        }
33    }
34}
35
36#[derive(Clone, Debug)]
37pub struct Parameter {
38    pub ident: Ident,
39    pub nullable: Option<bool>,
40    pub transformation: Option<TransformationType>,
41    pub new_name: Option<String>,
42}
43
44impl Parse for Parameter {
45    fn parse(toml: &Value, object_name: &str) -> Option<Self> {
46        let Some(ident) = Ident::parse(toml, object_name, "signal parameter") else {
47            error!("No 'name' or 'pattern' given for parameter for object {object_name}");
48            return None;
49        };
50        toml.check_unwanted(
51            &["nullable", "transformation", "new_name", "name", "pattern"],
52            &format!("parameter {object_name}"),
53        );
54
55        let nullable = toml.lookup("nullable").and_then(Value::as_bool);
56        let transformation = toml
57            .lookup("transformation")
58            .and_then(Value::as_str)
59            .and_then(|s| {
60                TransformationType::from_str(s)
61                    .map_err(|err| {
62                        error!("{err}");
63                        err
64                    })
65                    .ok()
66            });
67        let new_name = toml
68            .lookup("new_name")
69            .and_then(Value::as_str)
70            .map(ToOwned::to_owned);
71
72        Some(Self {
73            ident,
74            nullable,
75            transformation,
76            new_name,
77        })
78    }
79}
80
81impl AsRef<Ident> for Parameter {
82    fn as_ref(&self) -> &Ident {
83        &self.ident
84    }
85}
86
87pub type Parameters = Vec<Parameter>;
88
89#[derive(Clone, Debug)]
90pub struct Signal {
91    pub ident: Ident,
92    pub status: GStatus,
93    pub inhibit: bool,
94    pub version: Option<Version>,
95    pub parameters: Parameters,
96    pub ret: Return,
97    pub concurrency: library::Concurrency,
98    pub doc_hidden: bool,
99    pub doc_trait_name: Option<String>,
100    pub generate_doc: bool,
101    pub cfg_condition: Option<String>,
102}
103
104impl Signal {
105    pub fn parse(
106        toml: &Value,
107        object_name: &str,
108        concurrency: library::Concurrency,
109    ) -> Option<Self> {
110        let Some(ident) = Ident::parse(toml, object_name, "signal") else {
111            error!("No 'name' or 'pattern' given for signal for object {object_name}");
112            return None;
113        };
114        toml.check_unwanted(
115            &[
116                "ignore",
117                "manual",
118                "inhibit",
119                "version",
120                "cfg_condition",
121                "parameter",
122                "return",
123                "doc_hidden",
124                "name",
125                "pattern",
126                "concurrency",
127                "doc_trait_name",
128                "generate_doc",
129            ],
130            &format!("signal {object_name}"),
131        );
132
133        let status = {
134            if toml
135                .lookup("ignore")
136                .and_then(Value::as_bool)
137                .unwrap_or(false)
138            {
139                GStatus::Ignore
140            } else if toml
141                .lookup("manual")
142                .and_then(Value::as_bool)
143                .unwrap_or(false)
144            {
145                GStatus::Manual
146            } else {
147                GStatus::Generate
148            }
149        };
150
151        let inhibit = toml
152            .lookup("inhibit")
153            .and_then(Value::as_bool)
154            .unwrap_or(false);
155        let version = toml
156            .lookup("version")
157            .and_then(Value::as_str)
158            .and_then(|s| s.parse().ok());
159        let cfg_condition = toml
160            .lookup("cfg_condition")
161            .and_then(Value::as_str)
162            .map(ToOwned::to_owned);
163        let parameters = Parameters::parse(toml.lookup("parameter"), object_name);
164        let ret = Return::parse(toml.lookup("return"), object_name);
165
166        let concurrency = toml
167            .lookup("concurrency")
168            .and_then(Value::as_str)
169            .and_then(|v| v.parse().ok())
170            .unwrap_or(concurrency);
171
172        let doc_hidden = toml
173            .lookup("doc_hidden")
174            .and_then(Value::as_bool)
175            .unwrap_or(false);
176        let doc_trait_name = toml
177            .lookup("doc_trait_name")
178            .and_then(Value::as_str)
179            .map(ToOwned::to_owned);
180        let generate_doc = toml
181            .lookup("generate_doc")
182            .and_then(Value::as_bool)
183            .unwrap_or(true);
184
185        Some(Self {
186            ident,
187            status,
188            inhibit,
189            version,
190            parameters,
191            ret,
192            concurrency,
193            doc_hidden,
194            doc_trait_name,
195            generate_doc,
196            cfg_condition,
197        })
198    }
199}
200
201impl Functionlike for Signal {
202    type Parameter = self::Parameter;
203
204    fn parameters(&self) -> &[Self::Parameter] {
205        &self.parameters
206    }
207}
208
209impl AsRef<Ident> for Signal {
210    fn as_ref(&self) -> &Ident {
211        &self.ident
212    }
213}
214
215pub type Signals = Vec<Signal>;
216
217#[cfg(test)]
218mod tests {
219    use super::{super::ident::Ident, *};
220
221    fn toml(input: &str) -> ::toml::Value {
222        let value = toml::from_str(input);
223        assert!(value.is_ok());
224        value.unwrap()
225    }
226
227    #[test]
228    fn signal_parse_default() {
229        let toml = toml(
230            r#"
231name = "signal1"
232"#,
233        );
234        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
235        assert_eq!(f.ident, Ident::Name("signal1".into()));
236        assert!(f.status.need_generate());
237    }
238
239    #[test]
240    fn signal_parse_ignore() {
241        let toml = toml(
242            r#"
243name = "signal1"
244ignore = true
245"#,
246        );
247        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
248        assert!(f.status.ignored());
249    }
250
251    #[test]
252    fn signal_parse_manual() {
253        let toml = toml(
254            r#"
255name = "signal1"
256manual = true
257"#,
258        );
259        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
260        assert!(f.status.manual());
261    }
262
263    #[test]
264    fn signal_parse_generate_doc() {
265        let r = toml(
266            r#"
267name = "signal1"
268generate_doc = false
269"#,
270        );
271        let f = Signal::parse(&r, "a", Default::default()).unwrap();
272        assert!(!f.generate_doc);
273
274        // Ensure that the default value is "true".
275        let r = toml(
276            r#"
277name = "prop"
278"#,
279        );
280        let f = Signal::parse(&r, "a", Default::default()).unwrap();
281        assert!(f.generate_doc);
282    }
283}