ritual/
database.rs

1use crate::cpp_data::CppBaseSpecifier;
2use crate::cpp_data::CppClassField;
3use crate::cpp_data::CppEnumValue;
4use crate::cpp_data::CppOriginLocation;
5use crate::cpp_data::CppTypeData;
6use crate::cpp_data::CppTypeDataKind;
7use ritual_common::target::Target;
8
9use crate::cpp_data::CppVisibility;
10use crate::cpp_function::CppFunction;
11
12use crate::cpp_data::CppPath;
13use crate::cpp_data::CppTemplateInstantiation;
14use crate::cpp_ffi_data::CppFfiFunction;
15use crate::cpp_ffi_data::QtSlotWrapper;
16use crate::cpp_type::CppType;
17use crate::rust_type::RustPath;
18use itertools::Itertools;
19use serde_derive::{Deserialize, Serialize};
20use std::fmt::Display;
21use std::fmt::Formatter;
22
23#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
24pub struct CppCheckerEnv {
25    pub target: Target,
26    pub cpp_library_version: Option<String>,
27}
28
29impl CppCheckerEnv {
30    pub fn short_text(&self) -> String {
31        format!(
32            "{}/{:?}-{:?}-{:?}-{:?}",
33            self.cpp_library_version
34                .as_ref()
35                .map(|s| s.as_str())
36                .unwrap_or("None"),
37            self.target.arch,
38            self.target.os,
39            self.target.family,
40            self.target.env
41        )
42    }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub enum DatabaseItemSource {
47    CppParser {
48        /// File name of the include file (without full path)
49        include_file: String,
50        /// Exact location of the declaration
51        origin_location: CppOriginLocation,
52    },
53    ImplicitDestructor,
54    TemplateInstantiation,
55    NamespaceInfering,
56    QtSignalArguments,
57}
58
59impl DatabaseItemSource {
60    pub fn is_parser(&self) -> bool {
61        match *self {
62            DatabaseItemSource::CppParser { .. } => true,
63            _ => false,
64        }
65    }
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
69pub struct CppCheckerInfo {
70    pub env: CppCheckerEnv,
71    pub error: Option<String>,
72}
73
74#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
75pub struct CppCheckerInfoList {
76    pub items: Vec<CppCheckerInfo>,
77}
78
79pub enum CppCheckerAddResult {
80    Added,
81    Changed { old: Option<String> },
82    Unchanged,
83}
84
85impl CppCheckerInfoList {
86    pub fn add(&mut self, env: &CppCheckerEnv, error: Option<String>) -> CppCheckerAddResult {
87        if let Some(item) = self.items.iter_mut().find(|i| &i.env == env) {
88            let r = if item.error == error {
89                CppCheckerAddResult::Unchanged
90            } else {
91                CppCheckerAddResult::Changed {
92                    old: item.error.clone(),
93                }
94            };
95            item.error = error;
96            return r;
97        }
98        self.items.push(CppCheckerInfo {
99            env: env.clone(),
100            error,
101        });
102        CppCheckerAddResult::Added
103    }
104}
105
106#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
107#[allow(clippy::large_enum_variant)]
108pub enum CppItemData {
109    Namespace(CppPath),
110    Type(CppTypeData),
111    EnumValue(CppEnumValue),
112    Function(CppFunction),
113    ClassField(CppClassField),
114    ClassBase(CppBaseSpecifier),
115    TemplateInstantiation(CppTemplateInstantiation),
116    QtSignalArguments(Vec<CppType>),
117}
118
119impl CppItemData {
120    pub fn is_same(&self, other: &CppItemData) -> bool {
121        match (self, other) {
122            (&CppItemData::Type(ref v), &CppItemData::Type(ref v2)) => v.is_same(v2),
123            (&CppItemData::EnumValue(ref v), &CppItemData::EnumValue(ref v2)) => v.is_same(v2),
124            (&CppItemData::Function(ref v), &CppItemData::Function(ref v2)) => v.is_same(v2),
125            (&CppItemData::ClassField(ref v), &CppItemData::ClassField(ref v2)) => v.is_same(v2),
126            (&CppItemData::ClassBase(ref v), &CppItemData::ClassBase(ref v2)) => v == v2,
127            (
128                &CppItemData::TemplateInstantiation(ref v),
129                &CppItemData::TemplateInstantiation(ref v2),
130            ) => v == v2,
131            (&CppItemData::QtSignalArguments(ref v), &CppItemData::QtSignalArguments(ref v2)) => {
132                v == v2
133            }
134            _ => false,
135        }
136    }
137
138    pub fn all_involved_types(&self) -> Vec<CppType> {
139        match *self {
140            CppItemData::Type(ref t) => match t.kind {
141                CppTypeDataKind::Enum => vec![CppType::Enum {
142                    path: t.path.clone(),
143                }],
144                CppTypeDataKind::Class => vec![CppType::Class(t.path.clone())],
145            },
146            CppItemData::EnumValue(ref enum_value) => vec![CppType::Enum {
147                path: enum_value.enum_path.clone(),
148            }],
149            CppItemData::Namespace(_) => Vec::new(),
150            CppItemData::Function(ref function) => function.all_involved_types(),
151            CppItemData::ClassField(ref field) => {
152                let class_type = CppType::Class(field.class_type.clone());
153                vec![class_type, field.field_type.clone()]
154            }
155            CppItemData::ClassBase(ref base) => vec![
156                CppType::Class(base.base_class_type.clone()),
157                CppType::Class(base.derived_class_type.clone()),
158            ],
159            CppItemData::QtSignalArguments(ref args) => args.clone(),
160            CppItemData::TemplateInstantiation(ref data) => data.template_arguments.clone(),
161        }
162    }
163
164    pub fn as_namespace_ref(&self) -> Option<&CppPath> {
165        if let CppItemData::Namespace(ref data) = *self {
166            Some(data)
167        } else {
168            None
169        }
170    }
171    pub fn as_function_ref(&self) -> Option<&CppFunction> {
172        if let CppItemData::Function(ref data) = *self {
173            Some(data)
174        } else {
175            None
176        }
177    }
178    pub fn as_field_ref(&self) -> Option<&CppClassField> {
179        if let CppItemData::ClassField(ref data) = *self {
180            Some(data)
181        } else {
182            None
183        }
184    }
185    pub fn as_enum_value_ref(&self) -> Option<&CppEnumValue> {
186        if let CppItemData::EnumValue(ref data) = *self {
187            Some(data)
188        } else {
189            None
190        }
191    }
192    pub fn as_base_ref(&self) -> Option<&CppBaseSpecifier> {
193        if let CppItemData::ClassBase(ref data) = *self {
194            Some(data)
195        } else {
196            None
197        }
198    }
199    pub fn as_type_ref(&self) -> Option<&CppTypeData> {
200        if let CppItemData::Type(ref data) = *self {
201            Some(data)
202        } else {
203            None
204        }
205    }
206
207    pub fn as_template_instantiation_ref(&self) -> Option<&CppTemplateInstantiation> {
208        if let CppItemData::TemplateInstantiation(ref data) = *self {
209            Some(data)
210        } else {
211            None
212        }
213    }
214    pub fn as_signal_arguments_ref(&self) -> Option<&[CppType]> {
215        if let CppItemData::QtSignalArguments(ref data) = *self {
216            Some(data)
217        } else {
218            None
219        }
220    }
221
222    /*pub fn path(&self) -> Option<String> {
223        unimplemented!()
224    }*/
225}
226
227impl Display for CppItemData {
228    fn fmt(&self, f: &mut Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
229        let s = match *self {
230            CppItemData::Namespace(ref path) => format!("namespace {}", path),
231            CppItemData::Type(ref type1) => match type1.kind {
232                CppTypeDataKind::Enum => format!("enum {}", type1.path.to_cpp_pseudo_code()),
233                CppTypeDataKind::Class => format!("class {}", type1.path.to_cpp_pseudo_code()),
234            },
235            CppItemData::Function(ref method) => method.short_text(),
236            CppItemData::EnumValue(ref value) => format!(
237                "enum {} {{ {} = {}, ... }}",
238                value.enum_path, value.name, value.value
239            ),
240            CppItemData::ClassField(ref field) => field.short_text(),
241            CppItemData::ClassBase(ref class_base) => {
242                let virtual_text = if class_base.is_virtual {
243                    "virtual "
244                } else {
245                    ""
246                };
247                let visibility_text = match class_base.visibility {
248                    CppVisibility::Public => "public",
249                    CppVisibility::Protected => "protected",
250                    CppVisibility::Private => "private",
251                };
252                let index_text = if class_base.base_index > 0 {
253                    format!(" (index: {}", class_base.base_index)
254                } else {
255                    String::new()
256                };
257                format!(
258                    "class {} : {}{} {}{}",
259                    class_base.derived_class_type.to_cpp_pseudo_code(),
260                    virtual_text,
261                    visibility_text,
262                    class_base.base_class_type.to_cpp_pseudo_code(),
263                    index_text
264                )
265            }
266            CppItemData::QtSignalArguments(ref args) => format!(
267                "Qt signal args ({})",
268                args.iter().map(|arg| arg.to_cpp_pseudo_code()).join(", ")
269            ),
270            CppItemData::TemplateInstantiation(ref data) => format!(
271                "template instantiation: {}<{}>",
272                data.class_name,
273                data.template_arguments
274                    .iter()
275                    .map(|arg| arg.to_cpp_pseudo_code())
276                    .join(", ")
277            ),
278        };
279
280        f.write_str(&s)
281    }
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct RustPathScope {
286    path: RustPath,
287    prefix: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub enum RustClassPath {
292    TemplateClass {
293        instantiated: RustPathScope,
294    },
295    ConcreteClass {
296        path: RustPath,
297        nested: RustPathScope,
298    },
299}
300
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub enum RustFunctionPath {
303    Inherent(RustPath),
304    Free(RustPath),
305    TraitImpl,
306}
307
308impl RustFunctionPath {
309    fn rust_path(&self) -> Option<&RustPath> {
310        match *self {
311            RustFunctionPath::Inherent(ref path) => Some(path),
312            RustFunctionPath::Free(ref path) => Some(path),
313            RustFunctionPath::TraitImpl => None,
314        }
315    }
316}
317
318#[allow(clippy::large_enum_variant)]
319#[derive(Debug, Clone, Serialize, Deserialize)]
320pub enum RustItem {
321    Function {
322        cpp_ffi_function: CppFfiFunction,
323        checks: CppCheckerInfoList,
324        rust_path: Option<RustFunctionPath>,
325    },
326    Enum {
327        rust_path: Option<RustPath>,
328    },
329    EnumValue {
330        rust_path: Option<RustPath>,
331    },
332    Class {
333        qt_slot_wrapper: Option<QtSlotWrapper>,
334        checks: CppCheckerInfoList,
335        rust_path: Option<RustClassPath>,
336    },
337    Namespace {
338        rust_path: Option<RustPathScope>,
339    },
340    QtPublicSlotWrapper {
341        some_things: (),
342        rust_path: Option<RustPath>,
343    },
344}
345
346impl RustItem {
347    pub fn from_function(cpp_ffi_function: CppFfiFunction) -> Self {
348        RustItem::Function {
349            cpp_ffi_function,
350            checks: Default::default(),
351            rust_path: None,
352        }
353    }
354
355    pub fn from_qt_slot_wrapper(wrapper: QtSlotWrapper) -> Self {
356        RustItem::Class {
357            qt_slot_wrapper: Some(wrapper),
358            checks: Default::default(),
359            rust_path: None,
360        }
361    }
362
363    pub fn has_rust_path_resolved(&self) -> bool {
364        match *self {
365            RustItem::Function { ref rust_path, .. } => rust_path.is_some(),
366            RustItem::Enum { ref rust_path, .. } => rust_path.is_some(),
367            RustItem::EnumValue { ref rust_path, .. } => rust_path.is_some(),
368            RustItem::Class { ref rust_path, .. } => rust_path.is_some(),
369            RustItem::Namespace { ref rust_path, .. } => rust_path.is_some(),
370            RustItem::QtPublicSlotWrapper { ref rust_path, .. } => rust_path.is_some(),
371        }
372    }
373
374    pub fn checks_mut(&mut self) -> Option<&mut CppCheckerInfoList> {
375        match *self {
376            RustItem::Function { ref mut checks, .. } | RustItem::Class { ref mut checks, .. } => {
377                Some(checks)
378            }
379            _ => None,
380        }
381    }
382
383    pub fn rust_path(&self) -> Option<&RustPath> {
384        match *self {
385            RustItem::Function { ref rust_path, .. } => rust_path
386                .as_ref()
387                .and_then(|function_path| function_path.rust_path()),
388            RustItem::Enum { ref rust_path, .. } => rust_path.as_ref(),
389            RustItem::EnumValue { ref rust_path, .. } => rust_path.as_ref(),
390            RustItem::Class { ref rust_path, .. } => {
391                if let Some(RustClassPath::ConcreteClass { ref path, .. }) = rust_path {
392                    Some(path)
393                } else {
394                    None
395                }
396            }
397            RustItem::Namespace { .. } => None,
398            RustItem::QtPublicSlotWrapper { ref rust_path, .. } => rust_path.as_ref(),
399        }
400    }
401}
402
403#[derive(Debug, Clone, Serialize, Deserialize)]
404pub struct DatabaseItem {
405    pub cpp_data: CppItemData,
406
407    pub source: DatabaseItemSource,
408    pub rust_items: Option<Vec<RustItem>>, // TODO: remove Option if all database items have rust items
409}
410
411/// Represents all collected data related to a crate.
412#[derive(Debug, Serialize, Deserialize)]
413pub struct Database {
414    pub crate_name: String,
415    pub crate_version: String,
416    pub items: Vec<DatabaseItem>,
417    pub environments: Vec<CppCheckerEnv>,
418    pub next_ffi_id: u64,
419}
420
421impl Database {
422    pub fn empty(crate_name: impl Into<String>) -> Database {
423        Database {
424            crate_name: crate_name.into(),
425            crate_version: "0.0.0".into(),
426            items: Vec::new(),
427            environments: Vec::new(),
428            next_ffi_id: 0,
429        }
430    }
431
432    pub fn items(&self) -> &[DatabaseItem] {
433        &self.items
434    }
435
436    pub fn clear(&mut self) {
437        self.items.clear();
438        self.environments.clear();
439        self.next_ffi_id = 0;
440    }
441
442    pub fn crate_name(&self) -> &str {
443        &self.crate_name
444    }
445
446    pub fn add_cpp_data(&mut self, source: DatabaseItemSource, data: CppItemData) -> bool {
447        if let Some(item) = self
448            .items
449            .iter_mut()
450            .find(|item| item.cpp_data.is_same(&data))
451        {
452            // parser data takes priority
453            if source.is_parser() && !item.source.is_parser() {
454                item.source = source;
455            }
456            return false;
457        }
458        self.items.push(DatabaseItem {
459            cpp_data: data,
460            source,
461            rust_items: None,
462        });
463        true
464    }
465
466    /*
467    pub fn mark_missing_cpp_data(&mut self, env: DataEnv) {
468      let info = DataEnvInfo {
469        is_success: false,
470        ..DataEnvInfo::default()
471      };
472      for item in &mut self.items {
473        if !item.environments.iter().any(|env2| env2.env == env) {
474          item.environments.push(DataEnvWithInfo {
475            env: env.clone(),
476            info: info.clone(),
477          });
478        }
479      }
480    }*/
481}