oo_bindgen/model/builder/
structs.rs

1use std::collections::HashSet;
2use std::rc::Rc;
3
4use crate::model::*;
5
6pub type FunctionReturnStructBuilder<'a> = StructFieldBuilder<'a, FunctionReturnStructField>;
7pub type CallbackArgStructBuilder<'a> = StructFieldBuilder<'a, CallbackArgStructField>;
8pub type FunctionArgStructBuilder<'a> = StructFieldBuilder<'a, FunctionArgStructField>;
9pub type UniversalStructBuilder<'a> = StructFieldBuilder<'a, UniversalStructField>;
10
11pub struct StructFieldBuilder<'a, F>
12where
13    F: StructFieldType,
14{
15    lib: &'a mut LibraryBuilder,
16    visibility: Visibility,
17    declaration: TypedStructDeclaration<F>,
18    fields: Vec<StructField<F, Unvalidated>>,
19    field_names: HashSet<String>,
20    doc: Option<Doc<Unvalidated>>,
21}
22
23impl<'a, F> StructFieldBuilder<'a, F>
24where
25    F: StructFieldType,
26{
27    pub(crate) fn new(lib: &'a mut LibraryBuilder, declaration: TypedStructDeclaration<F>) -> Self {
28        Self::new_impl(lib, declaration, Visibility::Public)
29    }
30
31    pub(crate) fn opaque(
32        lib: &'a mut LibraryBuilder,
33        declaration: TypedStructDeclaration<F>,
34    ) -> Self {
35        Self::new_impl(lib, declaration, Visibility::Private)
36    }
37
38    fn new_impl(
39        lib: &'a mut LibraryBuilder,
40        declaration: TypedStructDeclaration<F>,
41        visibility: Visibility,
42    ) -> Self {
43        Self {
44            lib,
45            visibility,
46            declaration,
47            fields: Vec::new(),
48            field_names: HashSet::new(),
49            doc: None,
50        }
51    }
52
53    pub fn add<S: IntoName, V: Into<F>, D: Into<Doc<Unvalidated>>>(
54        mut self,
55        name: S,
56        field_type: V,
57        doc: D,
58    ) -> BindResult<Self> {
59        let name = name.into_name()?;
60        let field_type = field_type.into();
61
62        if self.field_names.insert(name.to_string()) {
63            self.fields.push(StructField {
64                name,
65                field_type,
66                doc: doc.into(),
67            });
68            Ok(self)
69        } else {
70            Err(BindingErrorVariant::StructFieldDuplicateName {
71                handle: self.declaration.inner.clone(),
72                field_name: name,
73            }
74            .into())
75        }
76    }
77
78    pub fn doc<D: Into<Doc<Unvalidated>>>(mut self, doc: D) -> BindResult<Self> {
79        match self.doc {
80            None => {
81                self.doc = Some(doc.into());
82                Ok(self)
83            }
84            Some(_) => Err(BindingErrorVariant::DocAlreadyDefined {
85                symbol_name: self.declaration.name().clone(),
86            }
87            .into()),
88        }
89    }
90
91    pub fn end_fields(self) -> BindResult<StructMethodBuilder<'a, F>> {
92        let doc = match self.doc {
93            Some(doc) => doc,
94            None => {
95                return Err(BindingErrorVariant::DocNotDefined {
96                    symbol_name: self.declaration.name().clone(),
97                }
98                .into())
99            }
100        };
101
102        Ok(StructMethodBuilder {
103            lib: self.lib,
104            visibility: self.visibility,
105            declaration: self.declaration,
106            fields: self.fields,
107            initializers: Vec::new(),
108            doc,
109        })
110    }
111}
112
113pub struct StructInitializerBuilder<'a, F>
114where
115    F: StructFieldType,
116{
117    name: Name,
118    initializer_type: InitializerType,
119    builder: StructMethodBuilder<'a, F>,
120    fields: Vec<InitializedValue>,
121    doc: Doc<Unvalidated>,
122}
123
124pub struct StructMethodBuilder<'a, F>
125where
126    F: StructFieldType,
127{
128    lib: &'a mut LibraryBuilder,
129    visibility: Visibility,
130    declaration: TypedStructDeclaration<F>,
131    fields: Vec<StructField<F, Unvalidated>>,
132    initializers: Vec<Handle<Initializer<Unvalidated>>>,
133    doc: Doc<Unvalidated>,
134}
135
136impl<'a, F> StructMethodBuilder<'a, F>
137where
138    F: StructFieldType,
139{
140    pub fn begin_initializer<D: Into<Doc<Unvalidated>>, S: IntoName>(
141        self,
142        name: S,
143        initializer_type: InitializerType,
144        doc: D,
145    ) -> BindResult<StructInitializerBuilder<'a, F>> {
146        let name = name.into_name()?;
147
148        // check that we don't have any other field or initializer with this name
149        if self.fields.iter().any(|field| name == field.name)
150            || self.initializers.iter().any(|c| name == c.name)
151        {
152            return Err(BindingErrorVariant::StructInitializerDuplicateName {
153                struct_name: self.declaration.name().clone(),
154                initializer_name: name,
155            }
156            .into());
157        }
158
159        Ok(StructInitializerBuilder {
160            name,
161            initializer_type,
162            builder: self,
163            fields: Vec::new(),
164            doc: doc.into(),
165        })
166    }
167
168    pub fn add_full_initializer<S: IntoName>(self, name: S) -> BindResult<Self> {
169        let name = name.into_name()?;
170        let struct_name = self.declaration.name().clone();
171        self.begin_initializer(
172            name,
173            InitializerType::Normal,
174            format!("Fully construct {{struct:{struct_name}}} specifying the value of each field"),
175        )?
176        .end_initializer()
177    }
178
179    pub fn build(self) -> BindResult<Handle<Struct<F, Unvalidated>>> {
180        let handle = Handle::new(Struct {
181            visibility: self.visibility,
182            declaration: self.declaration.clone(),
183            fields: self.fields,
184            initializers: self.initializers,
185            doc: self.doc,
186        });
187
188        self.lib
189            .add_statement(Statement::StructDefinition(F::create_struct_type(
190                handle.clone(),
191            )))?;
192
193        Ok(handle)
194    }
195}
196
197impl<'a, F> StructInitializerBuilder<'a, F>
198where
199    F: StructFieldType,
200{
201    pub fn default<D: Into<InitializerDefault>>(
202        mut self,
203        name: &Name,
204        value: D,
205    ) -> BindResult<Self> {
206        let value = value.into();
207
208        // check that we haven't already defined this field
209        if self.fields.iter().any(|f| f.name == *name) {
210            return Err(BindingErrorVariant::StructInitializerDuplicateField {
211                struct_name: self.builder.declaration.name().clone(),
212                field_name: name.clone(),
213            }
214            .into());
215        }
216
217        // find the field and validate it
218        let value = match self.builder.fields.iter().find(|f| f.name == *name) {
219            Some(x) => x.field_type.validate_default_value(&value)?,
220            None => {
221                return Err(BindingErrorVariant::StructInitializerUnknownField {
222                    struct_name: self.builder.declaration.name().clone(),
223                    field_name: name.clone(),
224                }
225                .into());
226            }
227        };
228
229        self.fields.push(InitializedValue {
230            name: name.clone(),
231            value,
232        });
233
234        Ok(self)
235    }
236
237    pub fn default_struct(self, name: &Name) -> BindResult<Self> {
238        self.default(name, InitializerDefault::DefaultStruct)
239    }
240
241    pub fn default_variant<S: Into<String>>(self, name: &Name, variant: S) -> BindResult<Self> {
242        self.default(name, InitializerDefault::Enum(variant.into()))
243    }
244
245    pub fn default_string<S: Into<String>>(self, name: &Name, value: S) -> BindResult<Self> {
246        self.default(name, InitializerDefault::String(value.into()))
247    }
248
249    pub fn end_initializer(mut self) -> BindResult<StructMethodBuilder<'a, F>> {
250        let initializer = Handle::new(Initializer {
251            name: self.name,
252            initializer_type: self.initializer_type,
253            values: Rc::new(self.fields),
254            doc: self.doc,
255        });
256
257        if let Some(x) = self
258            .builder
259            .initializers
260            .iter()
261            .find(|other| initializer.collides_with(other))
262        {
263            return Err(BindingErrorVariant::StructDuplicateInitializerArgs {
264                struct_name: self.builder.declaration.name().clone(),
265                this_initializer: initializer.name.clone(),
266                other_initializer: x.name.clone(),
267            }
268            .into());
269        }
270
271        self.builder.initializers.push(initializer);
272        Ok(self.builder)
273    }
274}