libgir/analysis/
record.rs1use std::ops::Deref;
2
3use log::info;
4
5use super::{imports::Imports, info_base::InfoBase, record_type::RecordType, *};
6use crate::{
7 config::{
8 derives::{Derive, Derives},
9 gobjects::GObject,
10 },
11 env::Env,
12 library,
13 nameutil::*,
14 traits::*,
15 version::Version,
16};
17
18#[derive(Debug, Default)]
19pub struct Info {
20 pub base: InfoBase,
21 pub glib_get_type: Option<(String, Option<Version>)>,
22 pub is_boxed: bool,
23 pub derives: Derives,
24 pub boxed_inline: bool,
25 pub init_function_expression: Option<String>,
26 pub copy_into_function_expression: Option<String>,
27 pub clear_function_expression: Option<String>,
28}
29
30impl Deref for Info {
31 type Target = InfoBase;
32
33 fn deref(&self) -> &InfoBase {
34 &self.base
35 }
36}
37
38impl Info {
39 pub fn type_<'a>(&self, library: &'a library::Library) -> &'a library::Record {
41 (library
42 .type_(self.type_id)
43 .maybe_ref()
44 .unwrap_or_else(|| panic!("{} is not a record.", self.full_name))) as _
45 }
46}
47
48fn filter_derives(derives: &[Derive], names: &[&str]) -> Derives {
49 derives
50 .iter()
51 .filter_map(|derive| {
52 let new_names = derive
53 .names
54 .iter()
55 .filter(|n| !names.contains(&n.as_str()))
56 .map(Clone::clone)
57 .collect::<Vec<_>>();
58
59 if !new_names.is_empty() {
60 Some(Derive {
61 names: new_names,
62 cfg_condition: derive.cfg_condition.clone(),
63 })
64 } else {
65 None
66 }
67 })
68 .collect()
69}
70
71pub fn new(env: &Env, obj: &GObject) -> Option<Info> {
72 info!("Analyzing record {}", obj.name);
73 let full_name = obj.name.clone();
74
75 let record_tid = env.library.find_type(0, &full_name)?;
76
77 let type_ = env.type_(record_tid);
78
79 let name: String = split_namespace_name(&full_name).1.into();
80
81 let record: &library::Record = type_.maybe_ref()?;
82
83 let is_boxed = matches!(
84 RecordType::of(record),
85 RecordType::Boxed | RecordType::AutoBoxed
86 );
87 let boxed_inline = obj.boxed_inline;
88
89 let mut imports = Imports::with_defined(&env.library, &name);
90 imports.add("crate::ffi");
91
92 let mut functions = functions::analyze(
93 env,
94 &record.functions,
95 Some(record_tid),
96 false,
97 is_boxed,
98 obj,
99 &mut imports,
100 None,
101 None,
102 );
103 let specials = special_functions::extract(&mut functions, type_, obj);
104
105 let version = obj.version.or(record.version);
106 let deprecated_version = record.deprecated_version;
107
108 let is_shared = specials.has_trait(special_functions::Type::Ref)
109 && specials.has_trait(special_functions::Type::Unref);
110 if is_shared {
111 special_functions::unhide(&mut functions, &specials, special_functions::Type::Copy);
113 };
114
115 let mut derives = if let Some(ref derives) = obj.derives {
116 if boxed_inline
117 && !derives.is_empty()
118 && !derives
119 .iter()
120 .all(|ds| ds.names.is_empty() || ds.names.iter().all(|n| n == "Debug"))
121 {
122 panic!("Can't automatically derive traits other than `Debug` for BoxedInline records");
123 }
124 derives.clone()
125 } else if !boxed_inline {
126 let derives = vec![Derive {
127 names: vec![
128 "Debug".into(),
129 "PartialEq".into(),
130 "Eq".into(),
131 "PartialOrd".into(),
132 "Ord".into(),
133 "Hash".into(),
134 ],
135 cfg_condition: None,
136 }];
137
138 derives
139 } else {
140 vec![]
142 };
143
144 for special in specials.traits().keys() {
145 match special {
146 special_functions::Type::Compare => {
147 derives = filter_derives(&derives, &["PartialOrd", "Ord", "PartialEq", "Eq"]);
148 }
149 special_functions::Type::Equal => {
150 derives = filter_derives(&derives, &["PartialEq", "Eq"]);
151 }
152 special_functions::Type::Hash => {
153 derives = filter_derives(&derives, &["Hash"]);
154 }
155 _ => (),
156 }
157 }
158
159 special_functions::analyze_imports(&specials, &mut imports);
160
161 let glib_get_type = if let Some(ref glib_get_type) = record.glib_get_type {
162 let configured_functions = obj.functions.matched("get_type");
163 let get_type_version = configured_functions
164 .iter()
165 .map(|f| f.version)
166 .max()
167 .flatten();
168
169 Some((glib_get_type.clone(), get_type_version))
170 } else {
171 None
172 };
173
174 if !is_shared
177 && (!specials.has_trait(special_functions::Type::Copy)
178 || !specials.has_trait(special_functions::Type::Free))
179 {
180 if let Some((_, get_type_version)) = glib_get_type {
181 assert!(
190 get_type_version <= version,
191 "Have to use get_type function for {full_name} but version is higher than for the type ({get_type_version:?} > {version:?})"
192 );
193 } else {
194 error!("Missing memory management functions for {full_name}");
195 }
196 }
197
198 let base = InfoBase {
199 full_name,
200 type_id: record_tid,
201 name,
202 functions,
203 specials,
204 imports,
205 version,
206 deprecated_version,
207 cfg_condition: obj.cfg_condition.clone(),
208 concurrency: obj.concurrency,
209 visibility: obj.visibility,
210 };
211
212 let info = Info {
213 base,
214 glib_get_type,
215 derives,
216 is_boxed,
217 boxed_inline,
218 init_function_expression: obj.init_function_expression.clone(),
219 copy_into_function_expression: obj.copy_into_function_expression.clone(),
220 clear_function_expression: obj.clear_function_expression.clone(),
221 };
222
223 Some(info)
224}