fl2rust/
gen.rs

1use crate::utils;
2use fluid_parser::ast::*;
3use std::fmt::Write;
4use std::sync::atomic;
5use std::sync::Mutex;
6
7static COUNTER: atomic::AtomicUsize = atomic::AtomicUsize::new(0);
8static I18N: atomic::AtomicBool = atomic::AtomicBool::new(false);
9static LAST_MENU: Mutex<String> = Mutex::new(String::new());
10
11pub const ALLOWS: &str = r#"// Automatically generated by fl2rust
12
13#![allow(unused_variables)]
14#![allow(unused_mut)]
15#![allow(unused_imports)]
16#![allow(dead_code)]
17#![allow(clippy::needless_update)]"#;
18
19const HEADER: &str = r#"
20use fltk::browser::*;
21use fltk::button::*;
22use fltk::dialog::*;
23use fltk::enums::*;
24use fltk::frame::*;
25use fltk::group::*;
26use fltk::image::*;
27use fltk::input::*;
28use fltk::menu::*;
29use fltk::misc::*;
30use fltk::output::*;
31use fltk::prelude::*;
32use fltk::table::*;
33use fltk::text::*;
34use fltk::tree::*;
35use fltk::valuator::*;
36use fltk::widget::*;
37use fltk::window::*;"#;
38
39fn i18nize(s: &str) -> String {
40    if I18N.load(atomic::Ordering::Relaxed) {
41        format!("&tr!(r#\"{}\"#)", s)
42    } else {
43        format!("r#\"{}\"#", s)
44    }
45}
46
47fn is_parent_type(typ: &str) -> bool {
48    matches!(
49        typ,
50        "Window"
51            | "Group"
52            | "Pack"
53            | "Tabs"
54            | "Scroll"
55            | "Table"
56            | "Tile"
57            | "Wizard"
58            | "MenuBar"
59            | "MenuButton"
60            | "Choice"
61            | "Flex"
62    )
63}
64
65fn is_menu_type(typ: &str) -> bool {
66    matches!(typ, "MenuBar" | "SysMenuBar" | "MenuButton" | "Choice")
67}
68
69fn add_menus(widgets: &[Widget], sub: &mut Vec<String>) -> String {
70    let mut wid = String::new();
71    let mut substyle = String::new();
72    for w in widgets {
73        if w.typ == "MenuItem" {
74            wid += "\tlet idx = ";
75            {
76                wid += &*LAST_MENU.lock().unwrap();
77            }
78            wid += ".add_choice(";
79            let mut temp = String::new();
80            temp += &sub.iter().map(|x| x.to_owned() + "/").collect::<String>();
81            temp += w.props.label.as_ref().unwrap_or(&String::new());
82            wid += &i18nize(&temp);
83            wid += ");\n";
84
85            let name = &format!("{}.at(idx).unwrap()", *LAST_MENU.lock().unwrap());
86            if let Some(v) = &w.props.shortcut {
87                writeln!(
88                    wid,
89                    "\t{}.set_shortcut(unsafe {{std::mem::transmute({})}});",
90                    name, v
91                )
92                .unwrap();
93            }
94            if let Some(v) = &w.props.typ {
95                writeln!(wid, "\t{}.set_flag(MenuFlag::{});", name, v).unwrap();
96            } else if w.props.divider.is_some() {
97                writeln!(wid, "\t{}.set_flag(MenuFlag::MenuDivider);", name).unwrap();
98            }
99            if let Some(v) = &w.props.callback {
100                writeln!(wid, "\t{}.set_callback({});", name, v).unwrap();
101            }
102            if let Some(v) = &w.props.labeltype {
103                let temp = utils::global_to_pascal(v);
104                let temp = if temp == "No" { "None" } else { temp.as_str() };
105                writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
106            }
107            if let Some(v) = &w.props.labelfont {
108                writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
109            }
110            if let Some(v) = &w.props.labelsize {
111                writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
112            }
113            if let Some(v) = &w.props.labelcolor {
114                writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
115            }
116        } else {
117            sub.push(w.props.label.as_ref().unwrap_or(&String::new()).to_string());
118            let name = &format!(
119                "{}.find_item(\"{}\").unwrap()",
120                *LAST_MENU.lock().unwrap(),
121                {
122                    let mut s = sub.iter().map(|x| x.to_owned() + "/").collect::<String>();
123                    s.pop();
124                    s
125                }
126            );
127            if let Some(v) = &w.props.labeltype {
128                let temp = utils::global_to_pascal(v);
129                let temp = if temp == "No" { "None" } else { temp.as_str() };
130                writeln!(substyle, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
131            }
132            if let Some(v) = &w.props.labelfont {
133                writeln!(
134                    substyle,
135                    "\t{}.set_label_font(Font::by_index({}));",
136                    name, v
137                )
138                .unwrap();
139            }
140            if let Some(v) = &w.props.labelsize {
141                writeln!(substyle, "\t{}.set_label_size({});", name, v).unwrap();
142            }
143            if let Some(v) = &w.props.labelcolor {
144                writeln!(
145                    substyle,
146                    "\t{}.set_label_color(Color::by_index({}));",
147                    name, v
148                )
149                .unwrap();
150            }
151        }
152        if !w.children.is_empty() {
153            wid += &add_menus(&w.children, sub);
154        }
155        if w.children.last().is_some() {
156            sub.pop();
157        }
158    }
159    wid += &substyle;
160    wid
161}
162
163fn add_widgets(
164    parent: Option<&str>,
165    widgets: &[Widget],
166    named: &mut Vec<(String, String)>,
167) -> String {
168    let mut wid = String::new();
169    let mut flex = String::new();
170    for w in widgets {
171        let mut name = String::new();
172        let mut refname = String::new();
173        let typ = if let Some(class) = &w.props.class {
174            class.to_owned()
175        } else {
176            utils::de_fl(&w.typ)
177        };
178        if typ != "MenuItem" && typ != "Submenu" {
179            if let Some(comment) = &w.props.comment {
180                wid += "\t// ";
181                wid += comment;
182                wid += "\n";
183            }
184            wid += "\tlet mut ";
185            if w.name.is_empty() {
186                let val = COUNTER.load(atomic::Ordering::Relaxed);
187                name += "fl2rust_widget_";
188                name += &val.to_string();
189                COUNTER.store(val + 1, atomic::Ordering::Relaxed);
190            } else {
191                name += &w.name;
192                named.push((name.clone(), typ.clone()));
193            }
194            if w.props.class.is_some() {
195                refname += "*";
196                refname += &name;
197            } else {
198                refname = name.clone();
199            }
200            wid += &name;
201            wid += " = ";
202            wid += &typ;
203            wid += "::new(";
204            for coord in w.props.xywh.split_ascii_whitespace() {
205                wid += coord;
206                wid += ", ";
207            }
208            wid += "None);\n";
209            if let Some(label) = &w.props.label {
210                wid += "\t";
211                wid += &name;
212                wid += ".set_label(";
213                wid += &i18nize(label);
214                wid += ");\n";
215            }
216
217            if let Some(v) = &w.props.typ {
218                let v = if typ == "Flex" {
219                    if v == "HORIZONTAL" {
220                        "Row"
221                    } else {
222                        "Column"
223                    }
224                } else {
225                    v
226                };
227                writeln!(
228                    wid,
229                    "\t{}.set_type({}Type::{});",
230                    name,
231                    utils::fix_type(&typ),
232                    utils::global_to_pascal(v)
233                )
234                .unwrap();
235            } else if typ == "Flex" {
236                writeln!(wid, "\t{}.set_type(FlexType::Column);", name,).unwrap();
237            }
238            if let Some(v) = &w.props.align {
239                writeln!(
240                    wid,
241                    "\t{}.set_align(unsafe {{std::mem::transmute({})}});",
242                    name, v
243                )
244                .unwrap();
245            }
246            if w.props.resizable.is_some() {
247                if parent.is_none() {
248                    writeln!(wid, "\t{}.make_resizable(true);", name).unwrap();
249                } else {
250                    writeln!(wid, "\t{}.resizable(&{});", parent.unwrap(), refname).unwrap();
251                }
252            }
253            if w.props.modal.is_some() {
254                writeln!(wid, "\t{}.make_modal(true);", name).unwrap();
255            }
256            if w.props.non_modal.is_some() {
257                writeln!(wid, "\t{}.make_modal(false);", name).unwrap();
258            }
259            if w.props.hide.is_some() {
260                writeln!(wid, "\t{}.hide();", name).unwrap();
261            }
262            if w.props.deactivate.is_some() {
263                writeln!(wid, "\t{}.deactivate();", name).unwrap();
264            }
265            if let Some(v) = &w.props.color {
266                writeln!(wid, "\t{}.set_color(Color::by_index({}));", name, v).unwrap();
267            }
268            if let Some(v) = &w.props.selection_color {
269                writeln!(
270                    wid,
271                    "\t{}.set_selection_color(Color::by_index({}));",
272                    name, v
273                )
274                .unwrap();
275            }
276            if let Some(v) = &w.props.tooltip {
277                writeln!(wid, "\t{}.set_tooltip({});", name, i18nize(v)).unwrap();
278            }
279            if let Some(v) = &w.props.xclass {
280                writeln!(wid, "\t{}.set_xclass({});", name, i18nize(v)).unwrap();
281            }
282            if w.props.noborder.is_some() {
283                writeln!(wid, "\t{}.set_border(false);", name).unwrap();
284            }
285            if let Some(v) = &w.props.image {
286                writeln!(wid, "\tlet image_data = &{:?};", utils::gen_image(v)).unwrap();
287                writeln!(wid, "\t{0}.set_image(Some({1}::from_data(image_data).expect(\"Could not load image: {2}\")));", name, utils::get_image_type(v), v).unwrap();
288            }
289            if let Some(v) = &w.props.deimage {
290                writeln!(wid, "\tlet image_data = &{:?};", utils::gen_image(v)).unwrap();
291                writeln!(wid, "\t{0}.set_deimage(Some({1}::from_data(image_data).expect(\"Could not load image: {2}\")));", name, utils::get_image_type(v), v).unwrap();
292            }
293            if let Some(v) = &w.props.r#box {
294                let temp = utils::global_to_pascal(v);
295                let temp = match temp.as_str() {
296                    "OflatBox" => "OFlatFrame",
297                    "OshadowBox" => "OShadowBox",
298                    "RflatBox" => "RFlatBox",
299                    "RshadowBox" => "RShadowBox",
300                    _ => temp.as_str(),
301                };
302                writeln!(wid, "\t{}.set_frame(FrameType::{});", name, temp).unwrap();
303            }
304            if let Some(v) = &w.props.down_box {
305                let temp = utils::global_to_pascal(v);
306                let temp = match temp.as_str() {
307                    "OflatBox" => "OFlatFrame",
308                    "OshadowBox" => "OShadowBox",
309                    "RflatBox" => "RFlatBox",
310                    "RshadowBox" => "RShadowBox",
311                    _ => temp.as_str(),
312                };
313                writeln!(wid, "\t{}.set_down_frame(FrameType::{});", name, temp).unwrap();
314            }
315            if let Some(v) = &w.props.labeltype {
316                let temp = utils::global_to_pascal(v);
317                let temp = if temp == "No" { "None" } else { temp.as_str() };
318                writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
319            }
320            if let Some(v) = &w.props.labelfont {
321                writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
322            }
323            if let Some(v) = &w.props.labelsize {
324                writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
325            }
326            if let Some(v) = &w.props.labelcolor {
327                writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
328            }
329            if let Some(v) = &w.props.when {
330                writeln!(
331                    wid,
332                    "\t{}.set_trigger(unsafe {{std::mem::transmute({})}});",
333                    name, v
334                )
335                .unwrap();
336            }
337            if let Some(v) = &w.props.textfont {
338                writeln!(wid, "\t{}.set_text_font(Font::by_index({}));", name, v).unwrap();
339            }
340            if let Some(v) = &w.props.textsize {
341                writeln!(wid, "\t{}.set_text_size({});", name, v).unwrap();
342            }
343            if let Some(v) = &w.props.textcolor {
344                writeln!(wid, "\t{}.set_text_color(Color::by_index({}));", name, v).unwrap();
345            }
346            if let Some(v) = &w.props.shortcut {
347                writeln!(
348                    wid,
349                    "\t{}.set_shortcut(unsafe {{std::mem::transmute({})}});",
350                    name, v
351                )
352                .unwrap();
353            }
354            if let Some(v) = &w.props.gap {
355                if v.contains(' ') {
356                    let count: Vec<_> = v.split_ascii_whitespace().collect();
357                    write!(wid, "\t{}.set_gap(", name).unwrap();
358                    for e in count {
359                        wid += e;
360                        wid += ", ";
361                    }
362                    wid += ");\n";
363                } else {
364                    writeln!(wid, "\t{}.set_pad({});", name, v).unwrap();
365                }
366            }
367            if let Some(v) = &w.props.minimum {
368                writeln!(wid, "\t{}.set_minimum({} as _);", name, v).unwrap();
369            }
370            if let Some(v) = &w.props.maximum {
371                writeln!(wid, "\t{}.set_maximum({} as _);", name, v).unwrap();
372            }
373            if let Some(v) = &w.props.size {
374                writeln!(wid, "\t{}.set_size({} as _);", name, v).unwrap();
375            }
376            if let Some(v) = &w.props.slider_size {
377                writeln!(wid, "\t{}.set_slider_size({} as _);", name, v).unwrap();
378            }
379            if let Some(v) = &w.props.step {
380                writeln!(wid, "\t{}.set_step({} as _, 1);", name, v).unwrap();
381            }
382            if let Some(v) = &w.props.user_data {
383                if let Some(stripped) = v.strip_prefix("id:") {
384                    writeln!(wid, "\t{}.set_id(\"{}\");", name, stripped).unwrap();
385                }
386            }
387            if let Some(v) = &w.props.value {
388                let val = if typ.contains("Button") {
389                    let b = v
390                        .parse::<i32>()
391                        .expect("Buttons should have integral values");
392                    if b != 0 {
393                        "true".to_string()
394                    } else {
395                        "false".to_string()
396                    }
397                } else if (typ.contains("Input") || typ.contains("Output"))
398                    && !typ.contains("Value")
399                {
400                    i18nize(v)
401                } else {
402                    format!("{} as _", v)
403                };
404                writeln!(wid, "\t{}.set_value({});", name, val).unwrap();
405            }
406            if let Some(v) = &w.props.code0 {
407                wid += "\t";
408                wid += v;
409                wid += "\n";
410            }
411            if let Some(v) = &w.props.code1 {
412                wid += "\t";
413                wid += v;
414                wid += "\n";
415            }
416            if let Some(v) = &w.props.code2 {
417                wid += "\t";
418                wid += v;
419                wid += "\n";
420            }
421            if let Some(v) = &w.props.code3 {
422                wid += "\t";
423                wid += v;
424                wid += "\n";
425            }
426            if let Some(v) = &w.props.extra_code {
427                wid += "\t";
428                wid += v;
429                wid += "\n";
430            }
431            if let Some(v) = &w.props.callback {
432                writeln!(wid, "\t{}.set_callback({});", name, v).unwrap();
433            }
434
435            if let Some(sizes) = &w.props.size_tuple {
436                let count: Vec<_> = sizes.split_ascii_whitespace().collect();
437                let count: Vec<_> = count.iter().skip(1).collect();
438                for e in count.chunks_exact(2) {
439                    let idx: usize = e[0].parse().unwrap();
440                    writeln!(
441                        flex,
442                        "\t{0}.fixed(&{0}.child({1}).unwrap(), {2});",
443                        name, idx, e[1]
444                    )
445                    .unwrap();
446                }
447                writeln!(flex, "\t{}.recalc();", name).unwrap();
448            }
449            if let Some(sizes) = &w.props.dimensions {
450                let count: Vec<_> = sizes.split_ascii_whitespace().collect();
451                write!(wid, "\t{0}.set_layout(", name).unwrap();
452                for e in count {
453                    wid += e;
454                    wid += ", ";
455                }
456                wid += ");\n";
457            }
458            if let Some(sizes) = &w.props.margins {
459                let count: Vec<_> = sizes.split_ascii_whitespace().collect();
460                write!(wid, "\t{0}.set_margins(", name).unwrap();
461                for e in count {
462                    wid += e;
463                    wid += ", ";
464                }
465                wid += ");\n";
466            }
467            if let Some(sizes) = &w.props.margin {
468                let count: Vec<_> = sizes.split_ascii_whitespace().collect();
469                if count.len() == 1 {
470                    write!(wid, "\t{0}.set_margin(", name).unwrap();
471                    for e in count {
472                        wid += e;
473                    }
474                    wid += ");\n";
475                } else {
476                    write!(wid, "\t{0}.set_margins(", name).unwrap();
477                    for e in count {
478                        wid += e;
479                        wid += ", ";
480                    }
481                    wid += ");\n";
482                }
483            }
484            if let Some(sizes) = &w.props.size_range {
485                let count: Vec<_> = sizes.split_ascii_whitespace().collect();
486                write!(wid, "\t{0}.size_range(", name).unwrap();
487                for e in count {
488                    wid += e;
489                    wid += ", ";
490                }
491                wid += ");\n";
492            }
493            if let Some(parent_props) = &w.props.parent_properties {
494                if let Some(loc) = &parent_props.location {
495                    let count: Vec<_> = loc.split_ascii_whitespace().collect();
496                    write!(wid, "\tlet mut p_grid = Grid::from_dyn_widget(&{}.parent().unwrap()).unwrap();\n", name,).unwrap();
497                    write!(wid, "\tp_grid.set_widget(&mut {}, ", name, ).unwrap();
498                    for e in count {
499                        wid += e;
500                        wid += ", ";
501                    }
502                    wid += ");\n";
503                }
504            }
505            if is_menu_type(&typ) {
506                {
507                    *LAST_MENU.lock().unwrap() = name.to_string();
508                }
509                let ch = add_menus(&w.children, &mut vec![]);
510                wid += &ch;
511            } else if !w.children.is_empty() {
512                let ch = add_widgets(Some(&name), &w.children, named);
513                wid += &ch;
514            }
515            if is_parent_type(&typ) {
516                wid += "\t";
517                wid += &name;
518                wid += ".end();\n";
519            }
520            if w.props.visible.is_some() {
521                writeln!(wid, "\t{}.show();", name).unwrap();
522            }
523        }
524    }
525    wid += &flex;
526    wid
527}
528
529fn add_funcs(functions: &[Function], free: bool, named: &mut Vec<(String, String)>) -> String {
530    let mut func = String::new();
531    for c in functions {
532        func += "\n    pub fn ";
533        func += &c.name;
534        if let Some(ret) = &c.props.return_type {
535            func += " -> ";
536            func += ret;
537        } else if !free && !c.name.contains("self") {
538            func += " -> Self";
539        }
540        func += " {\n";
541        if let Some(code) = &c.code {
542            func += "\t";
543            func += code;
544            func += "\n";
545        }
546        if !c.widgets.is_empty() {
547            func += &add_widgets(None, &c.widgets, named);
548        }
549        if free && c.props.return_type.is_none() {
550            func += "\t(\n";
551        } else if !c.name.contains("self") && !free {
552            func += "\tSelf {\n";
553        }
554        if !named.is_empty() && named.len() > 1 {
555            for n in named.iter() {
556                func += "\t    ";
557                func += &n.0;
558                func += ",\n";
559            }
560        } else if !named.is_empty() && named.len() == 1 {
561            func += "\t    ";
562            func += &named[0].0;
563            func += "\n";
564        }
565        if free {
566            named.clear();
567        }
568        if free && c.props.return_type.is_none() {
569            func += "\t)";
570        } else if !c.name.contains("self") && !free {
571            func += "\t}";
572        }
573        func += "\n    }";
574    }
575    func
576}
577
578fn add_widget_class_ctor(w: &Widget, named: &mut Vec<(String, String)>) -> String {
579    let mut wid = String::new();
580    wid += "\n    pub fn new<L: Into<Option<&'static str>>>(x: i32, y: i32, w: i32, h: i32, label: L) -> Self {\n";
581    wid += "\tlet mut base_group = Group::new(0, 0, ";
582    for coord in w.props.xywh.split_ascii_whitespace().skip(2) {
583        wid += coord;
584        wid += ", ";
585    }
586    wid += "label);\n";
587    let name = "base_group";
588    if w.props.resizable.is_some() {
589        writeln!(wid, "\t{}.make_resizable(true);", name).unwrap();
590    }
591    if let Some(v) = &w.props.labeltype {
592        let temp = utils::global_to_pascal(v);
593        let temp = if temp == "No" { "None" } else { temp.as_str() };
594        writeln!(wid, "\t{}.set_label_type(LabelType::{});", name, temp).unwrap();
595    }
596    if let Some(v) = &w.props.labelfont {
597        writeln!(wid, "\t{}.set_label_font(Font::by_index({}));", name, v).unwrap();
598    }
599    if let Some(v) = &w.props.labelsize {
600        writeln!(wid, "\t{}.set_label_size({});", name, v).unwrap();
601    }
602    if let Some(v) = &w.props.labelcolor {
603        writeln!(wid, "\t{}.set_label_color(Color::by_index({}));", name, v).unwrap();
604    }
605    if let Some(v) = &w.props.color {
606        writeln!(wid, "\t{}.set_color(Color::by_index({}));", name, v).unwrap();
607    }
608    if !w.children.is_empty() {
609        wid += &add_widgets(Some(name), &w.children, named);
610    }
611    wid += "\tbase_group.end();\n";
612    wid += "\tbase_group.resize(x, y, w, h);\n";
613    wid += "\tSelf {\n\t    base_group,\n";
614    if !named.is_empty() {
615        for n in named.iter() {
616            wid += "\t    ";
617            wid += &n.0;
618            wid += ",\n";
619        }
620    }
621    wid += "\t}";
622    wid += "\n    }";
623    wid
624}
625
626/// Generate the output Rust string/file
627fn generate_(ast: &Ast) -> String {
628    let mut s = String::new();
629    if let Some(i18n) = ast.i18n_type {
630        I18N.store(i18n, atomic::Ordering::Relaxed);
631    }
632    s += "\n";
633    let mut classes = vec![];
634    let mut widget_classes = vec![];
635    let mut funcs = vec![];
636    if !ast.decls.is_empty() {
637        for decl in &ast.decls {
638            s += &decl.decl;
639            s += "\n";
640        }
641        s += "\n";
642    }
643    if !ast.comments.is_empty() {
644        for comment in &ast.comments {
645            s += &comment.comment;
646            s += "\n";
647        }
648    }
649    if !ast.functions.is_empty() {
650        let mut local_named = vec![];
651        let func = add_funcs(&ast.functions, true, &mut local_named);
652        funcs.push(func);
653    }
654    if !ast.widget_classes.is_empty() {
655        let mut named: Vec<(String, String)> = vec![];
656        let mut class = String::new();
657        for c in &ast.widget_classes {
658            class += "#[derive(Debug, Clone)]\n";
659            class += "pub struct ";
660            class += &c.name;
661            class += " {\n";
662            class += "    pub base_group: Group,\n";
663            let fns = add_widget_class_ctor(c, &mut named);
664            if !named.is_empty() {
665                for n in &named {
666                    class += "    pub ";
667                    class += &n.0;
668                    class += ": ";
669                    class += &n.1;
670                    class += ",\n";
671                }
672            }
673            named.clear();
674            class += "}\n\n";
675            class += "impl ";
676            class += &c.name;
677            class += " {";
678            class += &fns;
679            class += "\n}\n\n";
680            class += "fltk::widget_extends!(";
681            class += &c.name;
682            class += ", Group, base_group);\n\n";
683        }
684        widget_classes.push(class);
685    }
686    if !ast.classes.is_empty() {
687        let mut named: Vec<(String, String)> = vec![];
688        let mut class = String::new();
689        for c in &ast.classes {
690            class += "#[derive(Debug, Clone)]\n";
691            class += "pub struct ";
692            class += &c.name;
693            class += " {\n";
694            let fns = add_funcs(&c.functions, false, &mut named);
695            if !named.is_empty() {
696                for n in &named {
697                    class += "    pub ";
698                    class += &n.0;
699                    class += ": ";
700                    class += &n.1;
701                    class += ",\n";
702                }
703            }
704            named.clear();
705            class += "}\n\n";
706            if !c.functions.is_empty() {
707                class += "impl ";
708                class += &c.name;
709                class += " {";
710                class += &fns;
711                class += "\n}\n\n";
712            }
713        }
714        classes.push(class);
715    }
716    for f in funcs {
717        s += &f;
718        s += "\n";
719    }
720    for c in widget_classes {
721        s += &c;
722        s += "\n";
723    }
724    for c in classes {
725        s += &c;
726        s += "\n";
727    }
728    s
729}
730
731/// Generate the output Rust string/file
732pub fn generate(ast: &Ast) -> String {
733    let s = generate_(ast);
734    format!("{}\n{}", HEADER, s)
735}
736
737/// Generate the output Rust string/file
738pub fn generate_with_directives_preamble(ast: &Ast) -> String {
739    let s = generate_(ast);
740    format!("{}\n{}\n{}", ALLOWS, HEADER, s)
741}