ploidy_codegen_rust/
types.rs

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