libgir/codegen/
child_properties.rs

1use std::io::{Result, Write};
2
3use super::{
4    general::{doc_alias, doc_hidden},
5    property_body,
6};
7use crate::{
8    analysis::{child_properties::ChildProperty, rust_type::RustType},
9    chunk::Chunk,
10    env::Env,
11    library,
12    nameutil::use_gtk_type,
13    traits::IntoString,
14    writer::{primitives::tabs, ToCode},
15};
16
17pub fn generate(
18    w: &mut dyn Write,
19    env: &Env,
20    prop: &ChildProperty,
21    in_trait: bool,
22    only_declaration: bool,
23    indent: usize,
24) -> Result<()> {
25    generate_func(w, env, prop, in_trait, only_declaration, indent, true)?;
26    generate_func(w, env, prop, in_trait, only_declaration, indent, false)?;
27
28    Ok(())
29}
30
31fn generate_func(
32    w: &mut dyn Write,
33    env: &Env,
34    prop: &ChildProperty,
35    in_trait: bool,
36    only_declaration: bool,
37    indent: usize,
38    is_get: bool,
39) -> Result<()> {
40    let pub_prefix = if in_trait { "" } else { "pub " };
41    let decl_suffix = if only_declaration { ";" } else { " {" };
42    let type_string = RustType::try_new(env, prop.typ);
43    let comment_prefix = if type_string.is_err() { "//" } else { "" };
44
45    writeln!(w)?;
46
47    doc_hidden(w, prop.doc_hidden, comment_prefix, indent)?;
48    let decl = declaration(env, prop, is_get);
49    let add_doc_alias = if is_get {
50        prop.name != prop.getter_name && prop.name != prop.prop_name
51    } else {
52        prop.name != prop.prop_name
53    };
54    if add_doc_alias {
55        doc_alias(
56            w,
57            &format!("{}.{}", &prop.child_name, &prop.name),
58            comment_prefix,
59            indent,
60        )?;
61    }
62    writeln!(
63        w,
64        "{}{}{}{}{}",
65        tabs(indent),
66        comment_prefix,
67        pub_prefix,
68        decl,
69        decl_suffix
70    )?;
71
72    if !only_declaration {
73        let body = body(env, prop, in_trait, is_get).to_code(env);
74        for s in body {
75            writeln!(w, "{}{}{}", tabs(indent), comment_prefix, s)?;
76        }
77    }
78
79    Ok(())
80}
81
82fn declaration(env: &Env, prop: &ChildProperty, is_get: bool) -> String {
83    let func_name = if is_get {
84        format!("{}_{}", prop.child_name, prop.getter_name)
85    } else {
86        format!("set_{}_{}", prop.child_name, prop.prop_name)
87    };
88    let mut bounds = if let Some(typ) = prop.child_type {
89        let child_type = RustType::try_new(env, typ).into_string();
90        format!("T: IsA<{child_type}>")
91    } else {
92        format!("T: IsA<{}>", use_gtk_type(env, "Widget"))
93    };
94    if !is_get && !prop.bounds.is_empty() {
95        bounds = format!("{}, {}", prop.bounds, bounds);
96    }
97    let return_str = if is_get {
98        let dir = library::ParameterDirection::Return;
99        let ret_type = RustType::builder(env, prop.typ)
100            .direction(dir)
101            .nullable(prop.nullable)
102            .ref_mode(prop.get_out_ref_mode)
103            .try_build_param()
104            .into_string();
105        format!(" -> {ret_type}")
106    } else {
107        String::new()
108    };
109    format!(
110        "fn {}<{}>(&self, item: &T{}){}",
111        func_name,
112        bounds,
113        if is_get {
114            String::new()
115        } else {
116            format!(", {}", prop.set_params)
117        },
118        return_str
119    )
120}
121
122fn body(env: &Env, prop: &ChildProperty, in_trait: bool, is_get: bool) -> Chunk {
123    let mut builder = property_body::Builder::new_for_child_property(env);
124    builder
125        .name(&prop.name)
126        .in_trait(in_trait)
127        .var_name(&prop.prop_name)
128        .is_get(is_get);
129
130    if let Ok(type_) = RustType::try_new(env, prop.typ) {
131        builder.type_(type_.as_str());
132    } else {
133        builder.type_("/*Unknown type*/");
134    }
135
136    builder.generate()
137}