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