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!(
51                "No 'name' or 'pattern' given for parameter for object {}",
52                object_name
53            );
54            return None;
55        };
56        toml.check_unwanted(
57            &["nullable", "transformation", "new_name", "name", "pattern"],
58            &format!("parameter {object_name}"),
59        );
60
61        let nullable = toml
62            .lookup("nullable")
63            .and_then(Value::as_bool)
64            .map(Nullable);
65        let transformation = toml
66            .lookup("transformation")
67            .and_then(Value::as_str)
68            .and_then(|s| {
69                TransformationType::from_str(s)
70                    .map_err(|err| {
71                        error!("{0}", err);
72                        err
73                    })
74                    .ok()
75            });
76        let new_name = toml
77            .lookup("new_name")
78            .and_then(Value::as_str)
79            .map(ToOwned::to_owned);
80
81        Some(Self {
82            ident,
83            nullable,
84            transformation,
85            new_name,
86        })
87    }
88}
89
90impl AsRef<Ident> for Parameter {
91    fn as_ref(&self) -> &Ident {
92        &self.ident
93    }
94}
95
96pub type Parameters = Vec<Parameter>;
97
98#[derive(Clone, Debug)]
99pub struct Signal {
100    pub ident: Ident,
101    pub status: GStatus,
102    pub inhibit: bool,
103    pub version: Option<Version>,
104    pub parameters: Parameters,
105    pub ret: Return,
106    pub concurrency: library::Concurrency,
107    pub doc_hidden: bool,
108    pub doc_trait_name: Option<String>,
109    pub generate_doc: bool,
110}
111
112impl Signal {
113    pub fn parse(
114        toml: &Value,
115        object_name: &str,
116        concurrency: library::Concurrency,
117    ) -> Option<Self> {
118        let Some(ident) = Ident::parse(toml, object_name, "signal") else {
119            error!(
120                "No 'name' or 'pattern' given for signal for object {}",
121                object_name
122            );
123            return None;
124        };
125        toml.check_unwanted(
126            &[
127                "ignore",
128                "manual",
129                "inhibit",
130                "version",
131                "parameter",
132                "return",
133                "doc_hidden",
134                "name",
135                "pattern",
136                "concurrency",
137                "doc_trait_name",
138                "generate_doc",
139            ],
140            &format!("signal {object_name}"),
141        );
142
143        let status = {
144            if toml
145                .lookup("ignore")
146                .and_then(Value::as_bool)
147                .unwrap_or(false)
148            {
149                GStatus::Ignore
150            } else if toml
151                .lookup("manual")
152                .and_then(Value::as_bool)
153                .unwrap_or(false)
154            {
155                GStatus::Manual
156            } else {
157                GStatus::Generate
158            }
159        };
160
161        let inhibit = toml
162            .lookup("inhibit")
163            .and_then(Value::as_bool)
164            .unwrap_or(false);
165        let version = toml
166            .lookup("version")
167            .and_then(Value::as_str)
168            .and_then(|s| s.parse().ok());
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        })
203    }
204}
205
206impl Functionlike for Signal {
207    type Parameter = self::Parameter;
208
209    fn parameters(&self) -> &[Self::Parameter] {
210        &self.parameters
211    }
212}
213
214impl AsRef<Ident> for Signal {
215    fn as_ref(&self) -> &Ident {
216        &self.ident
217    }
218}
219
220pub type Signals = Vec<Signal>;
221
222#[cfg(test)]
223mod tests {
224    use super::{super::ident::Ident, *};
225
226    fn toml(input: &str) -> ::toml::Value {
227        let value = input.parse::<::toml::Value>();
228        assert!(value.is_ok());
229        value.unwrap()
230    }
231
232    #[test]
233    fn signal_parse_default() {
234        let toml = toml(
235            r#"
236name = "signal1"
237"#,
238        );
239        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
240        assert_eq!(f.ident, Ident::Name("signal1".into()));
241        assert!(f.status.need_generate());
242    }
243
244    #[test]
245    fn signal_parse_ignore() {
246        let toml = toml(
247            r#"
248name = "signal1"
249ignore = true
250"#,
251        );
252        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
253        assert!(f.status.ignored());
254    }
255
256    #[test]
257    fn signal_parse_manual() {
258        let toml = toml(
259            r#"
260name = "signal1"
261manual = true
262"#,
263        );
264        let f = Signal::parse(&toml, "a", Default::default()).unwrap();
265        assert!(f.status.manual());
266    }
267
268    #[test]
269    fn signal_parse_generate_doc() {
270        let r = toml(
271            r#"
272name = "signal1"
273generate_doc = false
274"#,
275        );
276        let f = Signal::parse(&r, "a", Default::default()).unwrap();
277        assert!(!f.generate_doc);
278
279        // Ensure that the default value is "true".
280        let r = toml(
281            r#"
282name = "prop"
283"#,
284        );
285        let f = Signal::parse(&r, "a", Default::default()).unwrap();
286        assert!(f.generate_doc);
287    }
288}