use convert_case::{Case, Casing};
use genco::prelude::*;
use rust::Tokens;
use sails_idl_parser::{ast::visitor, ast::visitor::Visitor, ast::*};
use crate::events_generator::EventsModuleGenerator;
use crate::helpers::*;
use crate::type_generators::generate_type_decl_with_path;
pub(crate) struct ServiceCtorGenerator<'a> {
service_name: &'a str,
sails_path: &'a str,
trait_tokens: Tokens,
impl_tokens: Tokens,
}
impl<'a> ServiceCtorGenerator<'a> {
pub(crate) fn new(service_name: &'a str, sails_path: &'a str) -> Self {
Self {
service_name,
sails_path,
trait_tokens: Tokens::new(),
impl_tokens: Tokens::new(),
}
}
pub(crate) fn finalize(self) -> (Tokens, Tokens) {
(self.trait_tokens, self.impl_tokens)
}
}
impl<'ast> Visitor<'ast> for ServiceCtorGenerator<'_> {
fn visit_service(&mut self, _service: &'ast Service) {
let service_name_snake = &self.service_name.to_case(Case::Snake);
quote_in!(self.trait_tokens =>
fn $service_name_snake(&self) -> $(self.sails_path)::client::Service<$service_name_snake::$(self.service_name)Impl, Self::Env, $(self.sails_path)::client::RouteName>;
);
quote_in!(self.impl_tokens =>
fn $service_name_snake(&self) -> $(self.sails_path)::client::Service<$service_name_snake::$(self.service_name)Impl, Self::Env, $(self.sails_path)::client::RouteName> {
self.service_v1(stringify!($(self.service_name)))
}
);
}
}
pub(crate) struct ServiceGenerator<'a> {
service_name: &'a str,
sails_path: &'a str,
trait_tokens: Tokens,
impl_tokens: Tokens,
io_tokens: Tokens,
events_tokens: Tokens,
}
impl<'a> ServiceGenerator<'a> {
pub(crate) fn new(service_name: &'a str, sails_path: &'a str) -> Self {
Self {
service_name,
sails_path,
trait_tokens: Tokens::new(),
impl_tokens: Tokens::new(),
io_tokens: Tokens::new(),
events_tokens: Tokens::new(),
}
}
pub(crate) fn finalize(self) -> Tokens {
let service_name_snake = &self.service_name.to_case(Case::Snake);
quote! {
$['\n']
pub mod $service_name_snake {
use super::*;
pub trait $(self.service_name) {
type Env: $(self.sails_path)::client::GearEnv;
$(self.trait_tokens)
}
pub struct $(self.service_name)Impl;
impl<E: $(self.sails_path)::client::GearEnv> $(self.service_name) for $(self.sails_path)::client::Service<$(self.service_name)Impl, E, $(self.sails_path)::client::RouteName> {
type Env = E;
$(self.impl_tokens)
}
$['\n']
pub mod io {
use super::*;
$(self.io_tokens)
}
$(self.events_tokens)
}
}
}
}
impl<'ast> Visitor<'ast> for ServiceGenerator<'_> {
fn visit_service(&mut self, service: &'ast Service) {
visitor::accept_service(service, self);
if !service.events().is_empty() {
let mut events_mod_gen = EventsModuleGenerator::new(self.service_name, self.sails_path);
events_mod_gen.visit_service(service);
self.events_tokens = events_mod_gen.finalize();
}
}
fn visit_service_func(&mut self, func: &'ast ServiceFunc) {
let mutability = if func.is_query() { "" } else { "mut" };
let fn_name = func.name();
let fn_name_snake = &fn_name.to_case(Case::Snake);
let params_with_types = &fn_args_with_types(func.params());
let args = encoded_args(func.params());
for doc in func.docs() {
quote_in! { self.trait_tokens =>
$['\r'] $("///") $doc
};
}
quote_in! { self.trait_tokens =>
$['\r'] fn $fn_name_snake (&$mutability self, $params_with_types)
-> $(self.sails_path)::client::PendingCall<io::$fn_name, Self::Env>;
};
quote_in! { self.impl_tokens =>
$['\r'] fn $fn_name_snake (&$mutability self, $params_with_types)
-> $(self.sails_path)::client::PendingCall<io::$fn_name, Self::Env> {
self.pending_call($args)
}
};
let output_type_decl_code = generate_type_decl_with_path(func.output(), "super".to_owned());
let params_with_types_super = &fn_args_with_types_path(func.params(), "super");
quote_in! { self.io_tokens =>
$(self.sails_path)::io_struct_impl_v1!($fn_name ($params_with_types_super) -> $output_type_decl_code);
};
}
}