opencv_binding_generator/
constant.rs1use 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}