opencv_binding_generator/
debug.rs1use std::borrow::Cow;
2use std::fmt;
3use std::fmt::Display;
4use std::path::{Path, PathBuf};
5use std::sync::OnceLock;
6
7use clang::Entity;
8use dunce::canonicalize;
9
10static DEBUG_CONFIG: OnceLock<Option<DebugConfig>> = OnceLock::new();
11
12#[derive(Debug)]
13pub struct DebugConfig {
14 pub installation_root: PathBuf,
15}
16
17pub fn enable(debug_config: Option<DebugConfig>) {
19 DEBUG_CONFIG.set(debug_config).expect("Debug config is already initialized");
20}
21
22#[inline(always)]
23pub fn enabled() -> bool {
24 DEBUG_CONFIG.get().is_some()
25}
26
27#[inline(always)]
28pub fn config() -> Option<&'static DebugConfig> {
29 DEBUG_CONFIG.get().and_then(|c| c.as_ref())
30}
31
32#[derive(Clone, Debug)]
33pub struct LocationName<'me> {
34 pub location: DefinitionLocation,
35 pub name: Cow<'me, str>,
36}
37
38impl<'me> LocationName<'me> {
39 pub fn new(location: DefinitionLocation, name: impl Into<Cow<'me, str>>) -> Self {
40 Self {
41 location,
42 name: name.into(),
43 }
44 }
45}
46
47#[derive(Clone, Debug)]
48pub enum DefinitionLocation {
49 Generated,
50 File(PathBuf, u32),
51}
52
53impl DefinitionLocation {
54 pub fn as_file(&self) -> Option<(&Path, u32)> {
55 match self {
56 DefinitionLocation::Generated => None,
57 DefinitionLocation::File(path, line) => Some((path, *line)),
58 }
59 }
60}
61
62impl Display for DefinitionLocation {
63 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
64 match self {
65 Self::Generated => f.write_str("generated"),
66 Self::File(file, line) => {
67 let file = canonicalize(file).expect("Can't canonicalize path");
68 let path = config()
69 .and_then(|dbg_config| file.strip_prefix(&dbg_config.installation_root).ok())
70 .unwrap_or(&file);
71 write!(f, "{}:{line}", path.display())
72 }
73 }
74 }
75}
76
77pub trait NameDebug<'me> {
78 fn file_line_name(self) -> LocationName<'me>;
79
80 fn get_debug(self) -> String
81 where
82 Self: Sized,
83 {
84 if enabled() {
85 let LocationName { location, name } = self.file_line_name();
86 format!("// {name} {location}")
87 } else {
88 "".to_string()
89 }
90 }
91}
92
93impl NameDebug<'_> for &Entity<'_> {
94 fn file_line_name(self) -> LocationName<'static> {
95 let loc = self.get_location().expect("Can't get entity location").get_file_location();
96 let mut name = self
97 .get_display_name()
98 .unwrap_or_else(|| "<unknown display name>".to_string());
99 if let Some(unnamed) = name.strip_prefix("(")
102 && unnamed.starts_with("unnamed ")
103 {
104 if let Some(parent_name) = self.get_semantic_parent().and_then(|p| p.get_display_name()) {
105 name = parent_name;
107 } else if let Some((unnamed, _)) = unnamed.split_once(" at ") {
108 name = unnamed.to_string();
110 }
111 }
112 LocationName::new(
113 DefinitionLocation::File(loc.file.map(|f| f.get_path()).expect("Can't get file for debug"), loc.line),
114 name,
115 )
116 }
117}