sofie-cli 0.4.0

The `sofie` command: gen, check and the tools reading the app model.
Documentation
use proc_macro2::TokenStream;
use quote::quote;
use sofie_model::{Gateway, Module};

use super::in_module;

pub(super) fn gateways_function(modules: &[&Module]) -> Option<TokenStream> {
    let loads: Vec<TokenStream> = modules
        .iter()
        .flat_map(|module| {
            module
                .gateways
                .iter()
                .map(move |gateway| load(module, gateway))
        })
        .collect();
    if loads.is_empty() {
        return None;
    }
    Some(quote! {
        fn gateways() -> Result<()> {
            #(#loads)*
            Ok(())
        }
    })
}

pub(super) fn provided(module: &Module) -> impl Iterator<Item = TokenStream> {
    module.gateways.iter().map(|gateway| {
        let gateway = gateway_path(module, gateway);
        quote!(sofie::client::provide::<dyn #gateway>(db);)
    })
}

fn load(module: &Module, gateway: &Gateway) -> TokenStream {
    let name = &module.krate.crate_name;
    let key = &gateway.name;
    let choices = gateway
        .adapters
        .iter()
        .filter(|adapter| adapter.is_adapter)
        .map(|adapter| {
            let adapter = in_module(module, &adapter.ty);
            quote! {
                sofie::client::Choice::new(
                    <#adapter as sofie::client::Adapter>::VENDOR,
                    |keys| Ok(std::sync::Arc::new(sofie::client::adapter::<#adapter>(keys)?)),
                )
            }
        });
    let gateway = gateway_path(module, gateway);
    quote!(sofie::client::load::<dyn #gateway>(#name, #key, &[#(#choices),*])?;)
}

fn gateway_path(module: &Module, gateway: &Gateway) -> TokenStream {
    in_module(module, &gateway.path)
}