ploidy_codegen_rust/
types.rs

1use std::collections::BTreeSet;
2
3use itertools::Itertools;
4use ploidy_core::{codegen::IntoCode, ir::View};
5use proc_macro2::TokenStream;
6use quote::{ToTokens, TokenStreamExt, quote};
7
8use super::{
9    graph::CodegenGraph,
10    naming::{CodegenIdent, CodegenIdentUsage},
11};
12
13/// Generates the `types/mod.rs` module.
14pub struct CodegenTypesModule<'a> {
15    graph: &'a CodegenGraph<'a>,
16}
17
18impl<'a> CodegenTypesModule<'a> {
19    pub fn new(graph: &'a CodegenGraph<'a>) -> Self {
20        Self { graph }
21    }
22}
23
24impl ToTokens for CodegenTypesModule<'_> {
25    fn to_tokens(&self, tokens: &mut TokenStream) {
26        let mut tys = self
27            .graph
28            .schemas()
29            .filter_map(|view| {
30                let resources: BTreeSet<_> = view.used_by().map(|op| op.resource()).collect();
31                let cfg_attr = cfg_attr(&resources)?;
32                Some((view.extensions().get::<CodegenIdent>()?.clone(), cfg_attr))
33            })
34            .collect_vec();
35        tys.sort_by(|(a, _), (b, _)| a.cmp(b));
36
37        let mods = tys.iter().map(|(ident, cfg_attr)| {
38            let mod_name = CodegenIdentUsage::Module(ident);
39            quote! {
40                #cfg_attr
41                pub mod #mod_name;
42            }
43        });
44        let uses = tys.iter().map(|(ident, cfg_attr)| {
45            let mod_name = CodegenIdentUsage::Module(ident);
46            let ty_name = CodegenIdentUsage::Type(ident);
47            quote! {
48                #cfg_attr
49                pub use #mod_name::#ty_name;
50            }
51        });
52
53        tokens.append_all(quote! {
54            #(#mods)*
55
56            #(#uses)*
57        });
58    }
59}
60
61impl IntoCode for CodegenTypesModule<'_> {
62    type Code = (&'static str, TokenStream);
63
64    fn into_code(self) -> Self::Code {
65        ("src/types/mod.rs", self.into_token_stream())
66    }
67}
68
69/// Generates a `#[cfg(feature = "...")]` or `#[cfg(any(feature = "...", ...))]`
70/// attribute for the given resources.
71fn cfg_attr(resources: &BTreeSet<&str>) -> Option<TokenStream> {
72    let mut features = resources.iter().peekable();
73    let first = features.next()?;
74    Some(match features.next() {
75        Some(next) => {
76            let rest = features.map(|f| quote! { feature = #f });
77            quote! { #[cfg(any(feature = #first, feature = #next, #(#rest),*))] }
78        }
79        None => quote! { #[cfg(feature = #first)] },
80    })
81}