opencv_binding_generator/
constant.rs

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