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