opencv_binding_generator/
constant.rs

1use std::borrow::Cow;
2use std::borrow::Cow::{Borrowed, Owned};
3use std::rc::Rc;
4
5use clang::token::{Token, TokenKind};
6use clang::{Entity, EntityKind, EvaluationResult};
7pub use desc::ConstDesc;
8
9use crate::debug::{DefinitionLocation, LocationName};
10use crate::element::ExcludeKind;
11use crate::type_ref::CppNameStyle;
12use crate::{settings, DefaultElement, Element, NameDebug, StrExt};
13
14mod desc;
15
16pub fn render_constant_cpp(tokens: &[Token]) -> String {
17	tokens
18		.iter()
19		.fold(String::with_capacity(tokens.len() * 8), |out, x| out + &x.get_spelling())
20}
21
22pub fn render_evaluation_result_rust(result: EvaluationResult) -> Value {
23	match result {
24		EvaluationResult::Unexposed => panic!("Can't render evaluation result"),
25		EvaluationResult::SignedInteger(x) => Value {
26			kind: ValueKind::Integer,
27			value: x.to_string(),
28		},
29		EvaluationResult::UnsignedInteger(x) => Value {
30			kind: ValueKind::UnsignedInteger,
31			value: x.to_string(),
32		},
33		EvaluationResult::Float(x) => Value {
34			kind: ValueKind::Double,
35			value: x.to_string(),
36		},
37		EvaluationResult::String(x)
38		| EvaluationResult::ObjCString(x)
39		| EvaluationResult::CFString(x)
40		| EvaluationResult::Other(x) => Value {
41			kind: ValueKind::String,
42			value: format!(r#""{}""#, x.to_string_lossy()),
43		},
44	}
45}
46
47#[derive(Clone, Debug)]
48pub enum Const<'tu> {
49	Clang { entity: Entity<'tu> },
50	Desc(Rc<ConstDesc>),
51}
52
53impl<'tu> Const<'tu> {
54	pub fn new(entity: Entity<'tu>) -> Self {
55		Self::Clang { entity }
56	}
57
58	pub fn new_desc(desc: ConstDesc) -> Self {
59		Self::Desc(Rc::new(desc))
60	}
61
62	pub fn value(&self) -> Option<Cow<'_, Value>> {
63		match self {
64			Self::Clang { entity } => match entity.get_kind() {
65				EntityKind::MacroDefinition => {
66					let tokens = entity.get_range().expect("Can't get macro definition range").tokenize();
67					if tokens.len() <= 1 {
68						None
69					} else {
70						Value::try_from_tokens(&tokens[1..]).map(Owned)
71					}
72				}
73				EntityKind::EnumConstantDecl => Some(Owned(Value {
74					kind: ValueKind::Integer,
75					value: entity
76						.get_enum_constant_value()
77						.expect("Can't get enum constant value")
78						.0
79						.to_string(),
80				})),
81				EntityKind::VarDecl => entity.evaluate().map(render_evaluation_result_rust).map(Owned),
82				_ => unreachable!("Invalid entity type for constant"),
83			},
84			Self::Desc(desc) => Some(Borrowed(desc.value.as_ref())),
85		}
86	}
87}
88
89impl Element for Const<'_> {
90	fn exclude_kind(&self) -> ExcludeKind {
91		match self {
92			Self::Clang { entity } => DefaultElement::exclude_kind(self).with_is_excluded(|| {
93				entity.is_function_like_macro()
94					&& !settings::IMPLEMENTED_FUNCTION_LIKE_MACROS.contains(self.cpp_name(CppNameStyle::Reference).as_ref())
95			}),
96			Self::Desc(_) => ExcludeKind::Included,
97		}
98	}
99
100	fn is_system(&self) -> bool {
101		match self {
102			&Self::Clang { entity } => DefaultElement::is_system(entity),
103			Self::Desc(_) => false,
104		}
105	}
106
107	fn is_public(&self) -> bool {
108		match self {
109			&Self::Clang { entity } => DefaultElement::is_public(entity),
110			Self::Desc(_) => true,
111		}
112	}
113
114	fn doc_comment(&self) -> Cow<'_, str> {
115		match self {
116			Self::Clang { entity } => entity.doc_comment(),
117			Self::Desc(desc) => Borrowed(desc.doc_comment.as_ref()),
118		}
119	}
120
121	fn cpp_namespace(&self) -> Cow<'_, str> {
122		match self {
123			&Self::Clang { entity } => DefaultElement::cpp_namespace(entity).into(),
124			Self::Desc(desc) => Borrowed(desc.cpp_fullname.namespace()),
125		}
126	}
127
128	fn cpp_name(&self, style: CppNameStyle) -> Cow<'_, str> {
129		match self {
130			&Self::Clang { entity } => DefaultElement::cpp_name(self, entity, style),
131			Self::Desc(desc) => Borrowed(desc.cpp_fullname.cpp_name_from_fullname(style)),
132		}
133	}
134}
135
136impl<'me> NameDebug<'me> for &'me Const<'_> {
137	fn file_line_name(self) -> LocationName<'me> {
138		match self {
139			Const::Clang { entity } => entity.file_line_name(),
140			Const::Desc(desc) => LocationName::new(DefinitionLocation::Generated, desc.cpp_fullname.as_ref()),
141		}
142	}
143}
144
145#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub enum ValueKind {
147	Integer,
148	UnsignedInteger,
149	Usize,
150	Float,
151	Double,
152	String,
153}
154
155#[derive(Clone, Debug)]
156pub struct Value {
157	pub kind: ValueKind,
158	pub value: String,
159}
160
161impl Value {
162	pub fn integer(val: i32) -> Self {
163		Self {
164			kind: ValueKind::Integer,
165			value: val.to_string(),
166		}
167	}
168
169	pub fn try_from_tokens(tokens: &[Token]) -> Option<Self> {
170		let mut out = Value {
171			kind: ValueKind::Integer,
172			value: String::with_capacity(tokens.len() * 8),
173		};
174		for t in tokens {
175			match t.get_kind() {
176				TokenKind::Comment => {
177					out.value.push_str("/* ");
178					out.value.push_str(&t.get_spelling());
179					out.value.push_str(" */");
180				}
181				TokenKind::Identifier => {
182					let spelling = t.get_spelling();
183					if let Some(entity) = t.get_location().get_entity() {
184						if let EntityKind::MacroExpansion = entity.get_kind() {
185							let cnst = Const::new(entity);
186							if cnst.exclude_kind().is_excluded() {
187								return None;
188							}
189						}
190					}
191					if spelling.starts_with("CV_") {
192						out.value += &spelling;
193					} else {
194						return None;
195					}
196				}
197				TokenKind::Keyword => {
198					return None;
199				}
200				TokenKind::Literal => {
201					let spelling = t.get_spelling();
202					if spelling.contains(['"', '\'']) {
203						out.kind = ValueKind::String;
204						out.value += &spelling;
205					} else if spelling.contains('.') {
206						if let Some(float) = spelling.strip_suffix(['F', 'f']) {
207							out.kind = ValueKind::Float;
208							out.value += float;
209						} else {
210							out.kind = ValueKind::Double;
211							out.value += &spelling;
212						}
213					} else if let Some(unsigned_value) = spelling.strip_suffix(['U', 'u']) {
214						out.kind = ValueKind::UnsignedInteger;
215						out.value += unsigned_value;
216					} else {
217						out.value += &spelling;
218					}
219				}
220				TokenKind::Punctuation => {
221					let spelling = t.get_spelling();
222					if spelling == "{" || spelling == "}" {
223						return None;
224					}
225					out.value += &spelling;
226				}
227			}
228		}
229		Some(out)
230	}
231}