use crate::{ctor_generators::*, mock_generator::*, service_generators::*, type_generators::*};
use convert_case::{Case, Casing};
use genco::prelude::*;
use rust::Tokens;
use sails_idl_parser::{ast::visitor::Visitor, ast::*};
use std::collections::HashMap;
pub(crate) struct RootGenerator<'a> {
tokens: Tokens,
mocks_tokens: Tokens,
service_impl_tokens: Tokens,
service_trait_tokens: Tokens,
anonymous_service_name: &'a str,
mocks_feature_name: Option<&'a str>,
sails_path: &'a str,
external_types: HashMap<&'a str, &'a str>,
no_derive_traits: bool,
}
impl<'a> RootGenerator<'a> {
pub(crate) fn new(
anonymous_service_name: &'a str,
mocks_feature_name: Option<&'a str>,
sails_path: &'a str,
external_types: HashMap<&'a str, &'a str>,
no_derive_traits: bool,
) -> Self {
Self {
anonymous_service_name,
tokens: Tokens::new(),
mocks_tokens: Tokens::new(),
service_impl_tokens: Tokens::new(),
service_trait_tokens: Tokens::new(),
mocks_feature_name,
sails_path,
external_types,
no_derive_traits,
}
}
pub(crate) fn finalize(self, with_no_std: bool) -> String {
let mocks_tokens = if let Some(mocks_feature_name) = self.mocks_feature_name {
quote! {
$['\n']
#[cfg(feature = $(quoted(mocks_feature_name)))]
#[cfg(not(target_arch = "wasm32"))]
extern crate std;
$['\n']
#[cfg(feature = $(quoted(mocks_feature_name)))]
#[cfg(not(target_arch = "wasm32"))]
pub mod mockall {
use super::*;
use $(self.sails_path)::mockall::*;
$(self.mocks_tokens)
}
}
} else {
Tokens::new()
};
let mut tokens = quote! {
#[allow(unused_imports)]
use $(self.sails_path)::{client::*, collections::*, prelude::*};
};
for (&name, &path) in &self.external_types {
quote_in! { tokens =>
#[allow(unused_imports)]
use $path as $name;
};
}
let program_name = &self.anonymous_service_name.to_case(Case::Pascal);
quote_in! { tokens =>
pub struct $(program_name)Program;
impl $(self.sails_path)::client::Program for $(program_name)Program {}
pub trait $program_name {
type Env: $(self.sails_path)::client::GearEnv;
$(self.service_trait_tokens)
}
impl<E: $(self.sails_path)::client::GearEnv> $program_name for $(self.sails_path)::client::Actor<$(program_name)Program, E> {
type Env = E;
$(self.service_impl_tokens)
}
$(self.tokens)
$mocks_tokens
};
let mut result = tokens.to_file_string().unwrap();
if with_no_std {
result.insert_str(
0,
"// Code generated by sails-client-gen. DO NOT EDIT.\n#![no_std]\n\n",
);
} else {
result.insert_str(0, "// Code generated by sails-client-gen. DO NOT EDIT.\n");
}
result
}
}
impl<'ast> Visitor<'ast> for RootGenerator<'_> {
fn visit_ctor(&mut self, ctor: &'ast Ctor) {
let mut ctor_gen = CtorGenerator::new(self.anonymous_service_name, self.sails_path);
ctor_gen.visit_ctor(ctor);
self.tokens.extend(ctor_gen.finalize());
}
fn visit_service(&mut self, service: &'ast Service) {
let service_name = if service.name().is_empty() {
self.anonymous_service_name
} else {
service.name()
};
let mut ctor_gen = ServiceCtorGenerator::new(service_name, self.sails_path);
ctor_gen.visit_service(service);
let (trait_tokens, impl_tokens) = ctor_gen.finalize();
self.service_trait_tokens.extend(trait_tokens);
self.service_impl_tokens.extend(impl_tokens);
let mut client_gen = ServiceGenerator::new(service_name, self.sails_path);
client_gen.visit_service(service);
self.tokens.extend(client_gen.finalize());
let mut mock_gen = MockGenerator::new(service_name, self.sails_path);
mock_gen.visit_service(service);
self.mocks_tokens.extend(mock_gen.finalize());
}
fn visit_type(&mut self, t: &'ast Type) {
if self.external_types.contains_key(t.name()) {
return;
}
let mut type_gen =
TopLevelTypeGenerator::new(t.name(), self.sails_path, self.no_derive_traits);
type_gen.visit_type(t);
self.tokens.extend(type_gen.finalize());
}
}