safer_ffi/headers/languages/
mod.rs

1#![cfg_attr(rustfmt, rustfmt::skip)]
2#![allow(irrefutable_let_patterns)]
3
4use_prelude!();
5use {
6    ::std::io::{
7        self,
8        Write as _,
9    },
10    super::{
11        Definer,
12    },
13};
14
15pub use c::C;
16mod c;
17
18__cfg_csharp__! {
19    pub use csharp::CSharp;
20    mod csharp;
21}
22
23__cfg_python__! {
24    pub use python::Python;
25    mod python;
26}
27
28pub
29struct Indentation {
30    depth: ::core::cell::Cell<usize>,
31    width: usize,
32}
33
34impl Indentation {
35    pub
36    fn new (width: usize)
37      -> Indentation
38    {
39        Self { depth: 0.into(), width }
40    }
41
42    pub
43    fn scope (self: &'_ Self)
44      -> impl '_ + Sized
45    {
46        self.depth.set(self.depth.get() + 1);
47        ::scopeguard::guard((), move |()| {
48            self.depth.set(self.depth.get() - 1);
49        })
50    }
51}
52
53impl ::core::fmt::Display for Indentation {
54    fn fmt (
55        self: &'_ Indentation,
56        fmt: &'_ mut ::core::fmt::Formatter<'_>,
57    ) -> ::core::fmt::Result
58    {
59        write!(fmt, "{: <indent$}", "", indent = self.depth.get() * self.width)
60    }
61}
62
63type Docs<'lt> = &'lt [&'lt str];
64
65pub
66trait HeaderLanguage : UpcastAny {
67    fn language_name(self: &'_ Self) -> &'static str {
68        ::core::any::type_name::<Self>()
69    }
70
71    fn supports_type_aliases(self: &'_ Self)
72      -> Option<&'_ dyn HeaderLanguageSupportingTypeAliases>
73    {
74        None
75    }
76
77    fn emit_simple_enum (
78        self: &'_ Self,
79        ctx: &'_ mut dyn Definer,
80        docs: Docs<'_>,
81        self_ty: &'_ dyn PhantomCType,
82        backing_integer: Option<&'_ dyn PhantomCType>,
83        variants: &'_ [EnumVariant<'_>],
84    ) -> io::Result<()>
85    ;
86
87    fn emit_struct (
88        self: &'_ Self,
89        ctx: &'_ mut dyn Definer,
90        docs: Docs<'_>,
91        self_ty: &'_ dyn PhantomCType,
92        fields: &'_ [StructField<'_>]
93    ) -> io::Result<()>
94    ;
95
96    fn emit_opaque_type (
97        self: &'_ Self,
98        ctx: &'_ mut dyn Definer,
99        docs: Docs<'_>,
100        self_ty: &'_ dyn PhantomCType,
101    ) -> io::Result<()>
102    ;
103
104    fn emit_function (
105        self: &'_ Self,
106        ctx: &'_ mut dyn Definer,
107        docs: Docs<'_>,
108        fname: &'_ str,
109        args: &'_ [FunctionArg<'_>],
110        ret_ty: &'_ dyn PhantomCType,
111    ) -> io::Result<()>
112    ;
113
114    fn emit_constant (
115        self: &'_ Self,
116        ctx: &'_ mut dyn Definer,
117        docs: Docs<'_>,
118        name: &'_ str,
119        ty: &'_ dyn PhantomCType,
120        skip_type: bool,
121        value: &'_ dyn ::core::fmt::Debug,
122    ) -> io::Result<()>
123    ;
124
125    fn emit_docs (
126        self: &'_ Self,
127        _ctx: &'_ mut dyn Definer,
128        _docs: Docs<'_>,
129        _indentation: &'_ Indentation,
130    ) -> io::Result<()>
131    {
132        // This function is just offered as a convenience helper;
133        // it is not directly called by the framework.
134        Ok(())
135    }
136}
137
138pub
139trait HeaderLanguageSupportingTypeAliases : HeaderLanguage {
140    fn emit_type_alias(
141        self: &'_ Self,
142        ctx: &'_ mut dyn Definer,
143        docs: Docs<'_>,
144        self_ty: &'_ dyn PhantomCType,
145        inner_ty: &'_ dyn PhantomCType,
146    ) -> io::Result<()>
147    ;
148}
149
150pub
151struct EnumVariant<'lt> {
152    pub
153    docs: Docs<'lt>,
154
155    pub
156    name: &'lt str,
157
158    pub
159    discriminant: Option<&'lt dyn ::core::fmt::Debug>,
160}
161
162pub
163struct StructField<'lt> {
164    pub
165    docs: Docs<'lt>,
166
167    pub
168    name: &'lt str,
169
170    pub
171    ty: &'lt dyn PhantomCType,
172}
173
174pub
175struct FunctionArg<'lt> {
176    // pub
177    // docs: Docs<'lt>,
178
179    pub
180    name: &'lt str,
181
182    pub
183    ty: &'lt dyn PhantomCType,
184}
185
186/// `T::assoc_func()` -> `PhantomData::<T>.method()` conversion
187/// so as to become `dyn`-friendly (you can't pass a heterogeneous array of
188/// *distinct* `T : Trait`s *types* to a function, but you can pass a slice of
189/// `PhantomData`-materialized `dyn Trait`s).
190///
191/// In other words, we are projecting a compile-time type-level knowledge
192/// of an array / struct / "table" of a type's associated functions
193/// into a _runtime_ table of such, thence allowing runtime / `dyn`amic
194/// unification within a heterogeneous collection.
195pub
196trait PhantomCType {
197    fn short_name (
198        self: &'_ Self,
199    ) -> String
200    ;
201
202    fn name_wrapping_var (
203        self: &'_ Self,
204        language: &'_ dyn HeaderLanguage,
205        var_name: &'_ str,
206    ) -> String
207    ;
208
209    fn name (
210        self: &'_ Self,
211        language: &'_ dyn HeaderLanguage,
212    ) -> String
213    ;
214
215    fn csharp_marshaler (
216        self: &'_ Self,
217    ) -> Option<String>
218    ;
219
220    fn size (
221        self: &'_ Self,
222    ) -> usize
223    ;
224
225    fn align (
226        self: &'_ Self,
227    ) -> usize
228    ;
229}
230
231impl<T : ?Sized>
232    PhantomCType
233for
234    ::core::marker::PhantomData<T>
235where
236    T : CType,
237{
238    fn short_name (
239        self: &'_ Self,
240    ) -> String
241    {
242        <T as CType>::short_name()
243    }
244
245    fn name_wrapping_var (
246        self: &'_ Self,
247        language: &'_ dyn HeaderLanguage,
248        var_name: &'_ str,
249    ) -> String
250    {
251        T::name_wrapping_var(language, var_name)
252    }
253
254    fn name (
255        self: &'_ Self,
256        language: &'_ dyn HeaderLanguage,
257    ) -> String
258    {
259        T::name(language)
260    }
261
262    fn csharp_marshaler (
263        self: &'_ Self,
264    ) -> Option<String>
265    {
266        T::csharp_marshaler()
267    }
268
269    fn size (
270        self: &'_ Self,
271    ) -> usize
272    {
273        ::core::mem::size_of::<T>()
274    }
275
276    fn align (
277        self: &'_ Self,
278    ) -> usize
279    {
280        ::core::mem::align_of::<T>()
281    }
282}
283
284/// Generates an `out!` macro.
285///
286/// Important: the `out!` macro accepts a `("foo" "bar" "baz")` shorthand
287/// for the format literal parameter, to automatically convert it to:
288///
289/** ```rust ,ignore
290concat!(
291    "{indent}foo\n",
292    "{indent}bar\n",
293    "{indent}baz\n",
294)
295``` */
296///
297/// where `"{indent}"` is the first parameter passed to `mk_out!`,
298/// and the second parameter is the `impl Write` the `write!`s will
299/// be outputting to.
300macro_rules! mk_out {
301    (
302        $indent_name:ident,
303        // $indent:tt,
304        $out:expr $(,)?
305    ) => (
306        mk_out! { $indent_name /* $indent */ $out $ }
307    );
308
309    (
310        $indent_name:tt /* $indent:tt */ $out:tt $_:tt
311    ) => (
312        macro_rules! out {
313            (
314                ($_(
315                    $line:tt
316                )*) $_($rest:tt)*
317            ) => (
318                // we have to use eager expansion of `concat!` coupled with
319                // span manipulation for the implicit format args to work…
320                ::with_builtin_macros::with_builtin! {
321                    let $concat = concat!($_(
322                        "{", stringify!($indent_name), "}",
323                        $line,
324                        "\n",
325                    )*) in {
326                        ::safer_ffi_proc_macros::__respan! {
327                            // take the (first) span of the format string
328                            // literals provided by the caller…
329                            ( $_($line)* ) (
330                                // …and replace, with it, the spans of the whole
331                                //  `write!(` invocation.
332                                for line in
333                                    format!(
334                                        $concat
335                                        $_($rest)*
336                                    )
337                                    .split_inclusive('\n')
338                                {
339                                    let new_line = if line.ends_with('\n') { "\n" } else { "" };
340                                    write!($out, "{}{new_line}", line.trim_end())?;
341                                }
342                            )
343                        }
344                    }
345                }
346                /* for reference, here is the "simple usecase" I'd have expected
347                 * to Just Work™: */
348                // write!(
349                //     $out,
350                //     concat!($_(
351                //         // "{", stringify!($indent), "}",
352                //         $indent,
353                //         $line,
354                //         "\n",
355                //     )*)
356                //     // , $indent_name = $indent_name
357                //     $_($rest)*
358                // )
359            );
360
361            ( $_($tt:tt)* ) => (
362                write!($out, $_($tt)*)?
363            )
364        }
365    );
366} use mk_out;
367
368pub
369trait UpcastAny : 'static {
370    fn upcast_any (self: &'_ Self)
371      -> &'_ dyn ::core::any::Any
372    ;
373}
374impl<T : 'static> UpcastAny for T {
375    fn upcast_any (self: &'_ Self)
376      -> &'_ dyn ::core::any::Any
377    {
378        self
379    }
380}
381
382impl dyn HeaderLanguage {
383    pub
384    fn is<Concrete : HeaderLanguage> (
385        self: &'_ Self,
386    ) -> bool
387    {
388        self.upcast_any().is::<Concrete>()
389    }
390
391    pub
392    fn downcast_ref<Concrete : HeaderLanguage> (
393        self: &'_ Self,
394    ) -> Option<&'_ Concrete>
395    {
396        self.upcast_any()
397            .downcast_ref()
398    }
399}