interoptopus/
util.rs

1//! Helpers for backend authors.
2
3use crate::lang::c::{CType, Function};
4use crate::patterns::TypePattern;
5use std::collections::{HashMap, HashSet};
6use std::iter::FromIterator;
7
8/// Converts an internal name like `fn() -> X` to a safe name like `fn_rval_x`
9///
10/// # Example
11///
12/// ```
13/// use interoptopus::util::safe_name;
14///
15/// assert_eq!(safe_name("fn(u32) -> u8"), "fn_u32_rval_u8");
16/// ```
17pub fn safe_name(name: &str) -> String {
18    let mut rval = name.to_string();
19
20    rval = rval.replace("fn(", "fn_");
21    rval = rval.replace("-> ()", "");
22    rval = rval.replace("->", "rval");
23    rval = rval.replace('(', "");
24    rval = rval.replace(')', "");
25    rval = rval.replace('*', "p");
26    rval = rval.replace(',', "_");
27    rval = rval.replace(' ', "_");
28
29    rval = rval.trim_end_matches('_').to_string();
30
31    rval
32}
33
34// TODO: Create a few unit tests for this.
35/// Sorts types so the latter entries will find their dependents earlier in this list.
36pub fn sort_types_by_dependencies(mut types: Vec<CType>) -> Vec<CType> {
37    let mut rval = Vec::new();
38
39    // Outer loop keeps iterating while there are still more types to sort.
40    //
41    // Example of input  = [ F(A), D(E), G(?), C(D(E)), A, B(D(E)), E ]
42    //            output = [ G(?), A, E, F(A), D(E), C(D(E)), B(D(E)) ]
43    //
44    // Where A(B) means A depends on B, and ? is a type that cannot be fulfilled by input.
45    //
46    // The idea is to keep iterating `types`, removing all entries that either have no dependencies, or which
47    // have already been satisfied.
48    while !types.is_empty() {
49        // Types which have dependents fulfilled; we're going to fill this.
50        let mut may_add_this_round = Vec::new();
51
52        // Check any top-level type still in the list.
53        for t in &types {
54            let needed_to_exist = t.embedded_types();
55
56            let t_is_sufficiently_fulfilled = needed_to_exist.iter().all(|x| {
57                // All types exist if they, well, already exist in the output array. In addition, if a type
58                // cannot be fulfilled by the remaining types we also skip it (this can happen when filtering
59                // incomplete type lists which might be fulfilled by 3rd party user code).
60                rval.contains(x) || !types.contains(x)
61            });
62
63            // Add `t` if it didn't have any dependencies, or if we already added them.
64            if needed_to_exist.is_empty() || t_is_sufficiently_fulfilled {
65                may_add_this_round.push(t.clone());
66            }
67        }
68
69        types.retain(|x| !may_add_this_round.contains(x));
70        rval.append(&mut may_add_this_round);
71    }
72
73    rval
74}
75
76/// Given a number of functions like [`lib_x`, `lib_y`] return the longest common prefix `lib_`.
77///
78///
79/// # Example
80///
81/// ```rust
82/// # use interoptopus::lang::c::{Function, FunctionSignature, Meta};
83/// # use interoptopus::util::longest_common_prefix;
84///
85/// let functions = [
86///     Function::new("my_lib_f".to_string(), FunctionSignature::default(), Meta::default()),
87///     Function::new("my_lib_g".to_string(), FunctionSignature::default(), Meta::default()),
88///     Function::new("my_lib_h".to_string(), FunctionSignature::default(), Meta::default()),
89/// ];
90///
91/// assert_eq!(longest_common_prefix(&functions), "my_lib_".to_string());
92/// ```
93pub fn longest_common_prefix(functions: &[Function]) -> String {
94    let funcs_as_chars = functions.iter().map(|x| x.name().chars().collect::<Vec<_>>()).collect::<Vec<_>>();
95
96    let mut longest_common: Vec<char> = Vec::new();
97
98    if let Some(first) = funcs_as_chars.first() {
99        for (i, c) in first.iter().enumerate() {
100            for function in &funcs_as_chars {
101                if !function.get(i).map(|x| x == c).unwrap_or(false) {
102                    return String::from_iter(&longest_common);
103                }
104            }
105            longest_common.push(*c);
106        }
107    }
108
109    String::from_iter(&longest_common)
110}
111
112/// Given some functions and types, return all used and nested types, without duplicates.
113pub(crate) fn ctypes_from_functions_types(functions: &[Function], extra_types: &[CType]) -> Vec<CType> {
114    let mut types = HashSet::new();
115
116    for function in functions {
117        ctypes_from_type_recursive(function.signature().rval(), &mut types);
118
119        for param in function.signature().params() {
120            ctypes_from_type_recursive(param.the_type(), &mut types);
121        }
122    }
123
124    for ty in extra_types {
125        ctypes_from_type_recursive(ty, &mut types);
126    }
127
128    types.iter().cloned().collect()
129}
130
131/// Recursive helper for [`ctypes_from_functions_types`].
132pub(crate) fn ctypes_from_type_recursive(start: &CType, types: &mut HashSet<CType>) {
133    types.insert(start.clone());
134
135    match start {
136        CType::Composite(inner) => {
137            for field in inner.fields() {
138                ctypes_from_type_recursive(field.the_type(), types);
139            }
140        }
141        CType::Array(inner) => ctypes_from_type_recursive(inner.array_type(), types),
142        CType::FnPointer(inner) => {
143            ctypes_from_type_recursive(inner.signature().rval(), types);
144            for param in inner.signature().params() {
145                ctypes_from_type_recursive(param.the_type(), types);
146            }
147        }
148        CType::ReadPointer(inner) => ctypes_from_type_recursive(inner, types),
149        CType::ReadWritePointer(inner) => ctypes_from_type_recursive(inner, types),
150        CType::Primitive(_) => {}
151        CType::Enum(_) => {}
152        CType::Opaque(_) => {}
153        // Note, patterns must _NEVER_ add themselves as fallbacks. Instead, each code generator should
154        // decide on a case-by-case bases whether it wants to use the type's fallback, or generate an
155        // entirely new pattern. The exception to this rule are patterns that can embed arbitrary
156        // types; which we need to recursively inspect.
157        CType::Pattern(x) => match x {
158            TypePattern::AsciiPointer => {}
159            TypePattern::FFIErrorEnum(_) => {}
160            TypePattern::NamedCallback(x) => {
161                let inner = x.fnpointer();
162                ctypes_from_type_recursive(inner.signature().rval(), types);
163                for param in inner.signature().params() {
164                    ctypes_from_type_recursive(param.the_type(), types);
165                }
166            }
167            TypePattern::Slice(x) => {
168                for field in x.fields() {
169                    ctypes_from_type_recursive(field.the_type(), types);
170                }
171            }
172            TypePattern::SliceMut(x) => {
173                for field in x.fields() {
174                    ctypes_from_type_recursive(field.the_type(), types);
175                }
176            }
177            TypePattern::Option(x) => {
178                for field in x.fields() {
179                    ctypes_from_type_recursive(field.the_type(), types);
180                }
181            }
182            TypePattern::Bool => {}
183            TypePattern::CChar => {}
184            TypePattern::APIVersion => {}
185        },
186    }
187}
188
189/// Extracts annotated namespace strings.
190pub(crate) fn extract_namespaces_from_types(types: &[CType], into: &mut HashSet<String>) {
191    for t in types {
192        match t {
193            CType::Primitive(_) => {}
194            CType::Array(_) => {}
195
196            CType::Enum(x) => {
197                into.insert(x.meta().namespace().to_string());
198            }
199            CType::Opaque(x) => {
200                into.insert(x.meta().namespace().to_string());
201            }
202            CType::Composite(x) => {
203                into.insert(x.meta().namespace().to_string());
204            }
205            CType::FnPointer(_) => {}
206            CType::ReadPointer(_) => {}
207            CType::ReadWritePointer(_) => {}
208            CType::Pattern(x) => match x {
209                TypePattern::AsciiPointer => {}
210                TypePattern::APIVersion => {}
211                TypePattern::FFIErrorEnum(x) => {
212                    into.insert(x.the_enum().meta().namespace().to_string());
213                }
214                TypePattern::Slice(x) => {
215                    into.insert(x.meta().namespace().to_string());
216                }
217                TypePattern::SliceMut(x) => {
218                    into.insert(x.meta().namespace().to_string());
219                }
220                TypePattern::Option(x) => {
221                    into.insert(x.meta().namespace().to_string());
222                }
223                TypePattern::Bool => {}
224                TypePattern::CChar => {}
225                TypePattern::NamedCallback(_) => {}
226            },
227        }
228    }
229}
230
231/// Maps an internal namespace like `common` to a language namespace like `Company.Common`.
232#[derive(Clone, Debug, Eq, PartialEq)]
233pub struct NamespaceMappings {
234    mappings: HashMap<String, String>,
235}
236
237impl NamespaceMappings {
238    /// Creates a new mapping, assinging namespace id `""` to `default`.
239    pub fn new(default: &str) -> Self {
240        let mut mappings = HashMap::new();
241        mappings.insert("".to_string(), default.to_string());
242        mappings.insert("_global".to_string(), default.to_string());
243
244        Self { mappings }
245    }
246
247    /// Adds a mapping between namespace `id` to string `value`.
248    pub fn add(mut self, id: &str, value: &str) -> Self {
249        self.mappings.insert(id.to_string(), value.to_string());
250        self
251    }
252
253    /// Returns the default namespace mapping
254    pub fn default_namespace(&self) -> &str {
255        self.get("").expect("This must exist")
256    }
257
258    /// Obtains a mapping for the given ID.
259    pub fn get(&self, id: &str) -> Option<&str> {
260        self.mappings.get(id).map(|x| x.as_str())
261    }
262}
263
264/// Allows, for example, `my_id` to be converted to `MyId`.
265pub struct IdPrettifier {
266    tokens: Vec<String>,
267}
268
269impl IdPrettifier {
270    /// Creates a new prettifier from a `my_name` identifier.
271    pub fn from_rust_lower(id: &str) -> Self {
272        Self {
273            tokens: id.split('_').map(|x| x.to_string()).collect(),
274        }
275    }
276
277    pub fn to_camel_case(&self) -> String {
278        self.tokens
279            .iter()
280            .map(|x| {
281                x.chars()
282                    .enumerate()
283                    .map(|(i, x)| if i == 0 { x.to_ascii_uppercase() } else { x })
284                    .collect::<String>()
285            })
286            .collect::<Vec<_>>()
287            .join("")
288    }
289}
290
291/// Checks whether the given type should be "the same type everywhere".
292///
293/// In complex setups we sometimes want to use types between two (otherwise unrelated) bindings.
294/// For example, we would like to produce a `FFISlice<u8>` in library A, and consume that in
295/// library B. On the other hand, a  `FFISlice<MyStruct>` is not something everyone should know of.
296///
297/// For our bindings to know whether some types should go to a shared namespace this function
298/// will inform them whether the underlying type should be shared.
299///
300///
301pub fn is_global_type(t: &CType) -> bool {
302    match t {
303        CType::Primitive(_) => true,
304        CType::Array(x) => is_global_type(x.array_type()),
305        CType::Enum(_) => false,
306        CType::Opaque(_) => false,
307        CType::Composite(_) => false,
308        CType::FnPointer(_) => false,
309        CType::ReadPointer(x) => is_global_type(x),
310        CType::ReadWritePointer(x) => is_global_type(x),
311        CType::Pattern(x) => match x {
312            TypePattern::AsciiPointer => true,
313            TypePattern::APIVersion => false,
314            TypePattern::FFIErrorEnum(_) => false,
315            TypePattern::Slice(x) => x.fields().iter().all(|x| is_global_type(x.the_type())),
316            TypePattern::SliceMut(x) => x.fields().iter().all(|x| is_global_type(x.the_type())),
317            TypePattern::Option(x) => x.fields().iter().all(|x| is_global_type(x.the_type())),
318            TypePattern::Bool => true,
319            TypePattern::CChar => true,
320            TypePattern::NamedCallback(_) => false,
321        },
322    }
323}
324
325/// Debug macro resolving to the current file and line number.
326///
327/// ```
328/// use interoptopus::here;
329///
330/// println!(here!())
331/// ```
332#[macro_export]
333macro_rules! here {
334    () => {
335        concat!(file!(), ":", line!())
336    };
337}
338
339/// Logs an error if compiled with feature `log`.
340#[cfg(feature = "log")]
341#[inline(always)]
342pub fn log_error<S: AsRef<str>, F: Fn() -> S>(f: F) {
343    log::error!("{}", f().as_ref());
344}
345
346/// Logs an error if compiled with feature `log`.
347#[cfg(not(feature = "log"))]
348#[inline(always)]
349pub fn log_error<S: AsRef<str>, F: Fn() -> S>(_f: F) {}
350
351#[cfg(test)]
352mod test {
353    use crate::util::IdPrettifier;
354
355    #[test]
356    fn is_pretty() {
357        assert_eq!(IdPrettifier::from_rust_lower("hello_world").to_camel_case(), "HelloWorld");
358        assert_eq!(IdPrettifier::from_rust_lower("single").to_camel_case(), "Single");
359    }
360}