libgir/codegen/sys/
fields.rs

1use crate::{
2    analysis::{rust_type::*, types::*},
3    codegen::sys::{ffi_type::ffi_type, functions::function_signature},
4    env::Env,
5    library::*,
6    traits::{IntoString, MaybeRefAs},
7};
8
9pub struct Fields {
10    /// Name of union, class, or a record that contains the fields.
11    pub name: String,
12    /// Is this external type?
13    pub external: bool,
14    /// Reason for truncating the representation, if any.
15    pub truncated: Option<String>,
16    derives_copy: bool,
17    /// "struct" or "union"
18    pub kind: &'static str,
19    /// specified GObject cfg condition
20    pub cfg_condition: Option<String>,
21    pub fields: Vec<FieldInfo>,
22}
23
24pub struct FieldInfo {
25    /// Rust field name
26    pub name: String,
27    /// Rust type name
28    pub typ: String,
29    /// Does access to this field require unsafe block?
30    unsafe_access: bool,
31    /// Include this field in Debug impl?
32    pub debug: bool,
33}
34
35impl Fields {
36    /// List of derived traits
37    pub fn derived_traits(&self) -> Vec<&'static str> {
38        let mut traits = Vec::new();
39        if self.derives_copy {
40            traits.push("Copy");
41            traits.push("Clone");
42        }
43        traits
44    }
45}
46
47impl FieldInfo {
48    /// Generates a string that accesses the field in the context of &self
49    /// receiver.
50    pub fn access_str(&self) -> String {
51        let mut s = format!("&self.{}", self.name);
52        if self.unsafe_access {
53            s = format!("unsafe {{ {s} }}");
54        }
55        s
56    }
57}
58
59pub fn from_record(env: &Env, record: &Record) -> Fields {
60    let (fields, truncated) = analyze_fields(env, false, &record.fields);
61    let derives_copy = truncated.is_none() && record.derives_copy(&env.library);
62    Fields {
63        name: record.c_type.clone(),
64        external: record.is_external(&env.library),
65        truncated,
66        derives_copy,
67        kind: "struct",
68        cfg_condition: get_gobject_cfg_condition(env, &record.name),
69        fields,
70    }
71}
72
73pub fn from_class(env: &Env, klass: &Class) -> Fields {
74    let (fields, truncated) = analyze_fields(env, false, &klass.fields);
75    let derives_copy = truncated.is_none() && klass.derives_copy(&env.library);
76    Fields {
77        name: klass.c_type.clone(),
78        external: klass.is_external(&env.library),
79        truncated,
80        derives_copy,
81        kind: "struct",
82        cfg_condition: get_gobject_cfg_condition(env, &klass.name),
83        fields,
84    }
85}
86
87pub fn from_union(env: &Env, union: &Union) -> Fields {
88    let (fields, truncated) = analyze_fields(env, true, &union.fields);
89    let derives_copy = truncated.is_none() && union.derives_copy(&env.library);
90    Fields {
91        name: union.c_type.as_ref().unwrap().clone(),
92        external: union.is_external(&env.library),
93        truncated,
94        derives_copy,
95        kind: "union",
96        cfg_condition: None,
97        fields,
98    }
99}
100
101fn analyze_fields(
102    env: &Env,
103    unsafe_access: bool,
104    fields: &[Field],
105) -> (Vec<FieldInfo>, Option<String>) {
106    let mut truncated = None;
107    let mut infos = Vec::with_capacity(fields.len());
108
109    let mut is_bitfield = false;
110    for field in fields {
111        // See IsIncomplete for &[Field].
112        if is_bitfield && field.bits.is_some() {
113            truncated = Some(format!("field {} has incomplete type", &field.name));
114            break;
115        }
116        is_bitfield = field.bits.is_some();
117
118        let typ = match field_ffi_type(env, field) {
119            e @ Err(..) => {
120                truncated = Some(e.into_string());
121                break;
122            }
123            Ok(typ) => typ,
124        };
125        // Skip private fields from Debug impl. Ignore volatile as well,
126        // they are usually used as synchronization primites,
127        // so we wouldn't want to introduce additional reads.
128        let debug = !field.private && !field.is_volatile() && field.implements_debug(&env.library);
129
130        infos.push(FieldInfo {
131            name: field.name.clone(),
132            typ: typ.into_string(),
133            debug,
134            unsafe_access,
135        });
136    }
137
138    (infos, truncated)
139}
140
141fn field_ffi_type(env: &Env, field: &Field) -> Result {
142    if field.is_incomplete(&env.library) {
143        return Err(TypeError::Ignored(format!(
144            "field {} has incomplete type",
145            &field.name
146        )));
147    }
148    if let Some(ref c_type) = field.c_type {
149        ffi_type(env, field.typ, c_type)
150    } else if let Some(func) = env.library.type_(field.typ).maybe_ref_as::<Function>() {
151        let (failure, signature) = function_signature(env, func, true);
152        let signature = format!("Option<unsafe extern \"C\" fn{signature}>");
153        if failure {
154            Err(TypeError::Unimplemented(signature))
155        } else {
156            Ok(signature.into())
157        }
158    } else {
159        Err(TypeError::Ignored(format!(
160            "field {} has empty c:type",
161            &field.name
162        )))
163    }
164}
165
166fn get_gobject_cfg_condition(env: &Env, name: &str) -> Option<String> {
167    let full_name = format!("{}.{}", env.namespaces.main().name, name);
168    if let Some(obj) = env.config.objects.get(&full_name) {
169        obj.cfg_condition.clone()
170    } else {
171        None
172    }
173}