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 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 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 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}