libgir/codegen/
signal.rs

1use std::io::{Result, Write};
2
3use super::{
4    general::{cfg_deprecated, doc_alias, doc_hidden, version_condition},
5    signal_body,
6    trampoline::{self, func_string},
7};
8use crate::{
9    analysis,
10    chunk::Chunk,
11    env::Env,
12    writer::{primitives::tabs, ToCode},
13};
14
15pub fn generate(
16    w: &mut dyn Write,
17    env: &Env,
18    analysis: &analysis::signals::Info,
19    in_trait: bool,
20    only_declaration: bool,
21    indent: usize,
22) -> Result<()> {
23    let commented = analysis.trampoline.is_err();
24    let comment_prefix = if commented { "//" } else { "" };
25    let pub_prefix = if in_trait { "" } else { "pub " };
26
27    let function_type = function_type_string(env, analysis, true);
28    let declaration = declaration(analysis, &function_type);
29    let suffix = if only_declaration { ";" } else { " {" };
30
31    writeln!(w)?;
32    cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
33    version_condition(w, env, None, analysis.version, commented, indent)?;
34    doc_hidden(w, analysis.doc_hidden, comment_prefix, indent)?;
35    // Strip the "prefix" from "prefix::prop-name", if any.
36    // Ex.: "notify::is-locked".
37    doc_alias(
38        w,
39        analysis.signal_name.splitn(2, "::").last().unwrap(),
40        comment_prefix,
41        indent,
42    )?;
43    writeln!(
44        w,
45        "{}{}{}{}{}",
46        tabs(indent),
47        comment_prefix,
48        pub_prefix,
49        declaration,
50        suffix
51    )?;
52
53    if !only_declaration {
54        if !commented {
55            if let Ok(ref trampoline) = analysis.trampoline {
56                trampoline::generate(w, env, trampoline, in_trait, 2)?;
57            }
58        }
59        match function_type {
60            Some(_) => {
61                let body = body(analysis, in_trait).to_code(env);
62                for s in body {
63                    writeln!(w, "{}{}", tabs(indent), s)?;
64                }
65            }
66            _ => {
67                if let Err(ref errors) = analysis.trampoline {
68                    for error in errors {
69                        writeln!(w, "{}{}\t{}", tabs(indent), comment_prefix, error)?;
70                    }
71                    writeln!(w, "{}{}}}", tabs(indent), comment_prefix)?;
72                } else {
73                    writeln!(
74                        w,
75                        "{}{}\tTODO: connect to trampoline\n{0}{1}}}",
76                        tabs(indent),
77                        comment_prefix
78                    )?;
79                }
80            }
81        }
82    }
83
84    if function_type.is_none() {
85        // Signal incomplete, can't generate emit
86        return Ok(());
87    }
88
89    if let Some(ref emit_name) = analysis.action_emit_name {
90        writeln!(w)?;
91        if !in_trait || only_declaration {
92            cfg_deprecated(w, env, None, analysis.deprecated_version, commented, indent)?;
93        }
94        version_condition(w, env, None, analysis.version, commented, indent)?;
95
96        let function_type = function_type_string(env, analysis, false);
97
98        writeln!(
99            w,
100            "{}{}{}fn {}{}{}",
101            tabs(indent),
102            comment_prefix,
103            pub_prefix,
104            emit_name,
105            function_type.unwrap(),
106            suffix
107        )?;
108
109        if !only_declaration {
110            let trampoline = analysis.trampoline.as_ref().unwrap_or_else(|_| {
111                panic!(
112                    "Internal error: can't find trampoline for signal '{}'",
113                    analysis.signal_name,
114                )
115            });
116            let mut args = String::with_capacity(100);
117
118            for (pos, par) in trampoline.parameters.rust_parameters.iter().enumerate() {
119                // Skip the self parameter
120                if pos == 0 {
121                    continue;
122                }
123
124                if pos > 1 {
125                    args.push_str(", ");
126                }
127                args.push('&');
128                args.push_str(&par.name);
129            }
130
131            if trampoline.ret.typ != Default::default() {
132                writeln!(
133                    w,
134                    "{}self.emit_by_name(\"{}\", &[{}])",
135                    tabs(indent + 1),
136                    analysis.signal_name,
137                    args,
138                )?;
139            } else {
140                writeln!(
141                    w,
142                    "{}self.emit_by_name::<()>(\"{}\", &[{}]);",
143                    tabs(indent + 1),
144                    analysis.signal_name,
145                    args,
146                )?;
147            }
148            writeln!(w, "{}}}", tabs(indent))?;
149        }
150    }
151
152    Ok(())
153}
154
155fn function_type_string(
156    env: &Env,
157    analysis: &analysis::signals::Info,
158    closure: bool,
159) -> Option<String> {
160    analysis.trampoline.as_ref().ok()?;
161
162    let trampoline = analysis.trampoline.as_ref().unwrap_or_else(|_| {
163        panic!(
164            "Internal error: can't find trampoline for signal '{}'",
165            analysis.signal_name
166        )
167    });
168
169    let type_ = func_string(
170        env,
171        trampoline,
172        Some(if closure { "Self" } else { "self" }),
173        closure,
174    );
175    Some(type_)
176}
177
178fn declaration(analysis: &analysis::signals::Info, function_type: &Option<String>) -> String {
179    let bounds = bounds(function_type);
180    let param_str = if !analysis.is_detailed {
181        "&self, f: F"
182    } else {
183        "&self, detail: Option<&str>, f: F"
184    };
185    let return_str = " -> SignalHandlerId";
186    format!(
187        "fn {}<{}>({}){}",
188        analysis.connect_name, bounds, param_str, return_str
189    )
190}
191
192fn bounds(function_type: &Option<String>) -> String {
193    match function_type {
194        Some(type_) => format!("F: {type_}"),
195        _ => "Unsupported or ignored types".to_owned(),
196    }
197}
198
199fn body(analysis: &analysis::signals::Info, in_trait: bool) -> Chunk {
200    let mut builder = signal_body::Builder::new();
201
202    builder
203        .signal_name(&analysis.signal_name)
204        .trampoline_name(&analysis.trampoline.as_ref().unwrap().name)
205        .in_trait(in_trait)
206        .is_detailed(analysis.is_detailed);
207
208    builder.generate()
209}