Skip to main content
Glama
dal_test.rs16.5 kB
//! Expansion implementation of the `dal_test` attribute macro. //! //! This implementation is a combination of a configurable threaded Tokio runtime (formerly //! provided via the `tokio::test` macro from the `tokio` crate), support for optional //! tracing/logging support (formerly provided via the `test` mecro from the `test-env-log` crate), //! and an "extractor"-style dependency setup a little like axum's extractors. //! //! # Reference Implementations and Credits //! //! * [`tokio::test` macro](https://github.com/tokio-rs/tokio/blob/121769c762ad6b1686ecd0e8618005aab8b7e980/tokio-macros/src/entry.rs) //! * [`test_env_log::test` macro](https://github.com/d-e-s-o/test-log/blob/544dbac50321aaf580959ad7a7997358517db198/src/lib.rs) use std::rc::Rc; use proc_macro2::{ Ident, TokenStream, }; use syn::{ Expr, FnArg, ItemFn, Type, parse_quote, punctuated::Punctuated, token::Comma, }; use crate::{ Args, expand::{ FnSetup, FnSetupExpander, expand_test, }, path_as_string, }; pub(crate) fn expand(item: ItemFn, args: Args) -> TokenStream { let fn_setup = fn_setup(item.sig.inputs.iter()); expand_test(item, args, fn_setup) } fn fn_setup<'a>(params: impl Iterator<Item = &'a FnArg>) -> DalTestFnSetup { let mut expander = DalTestFnSetupExpander::new(); expander.setup_start_forklift_server(); expander.setup_start_veritech_server(); expander.setup_start_pinga_server(); expander.setup_start_edda_server(); expander.setup_start_rebaser_server(); for param in params { match param { FnArg::Typed(pat_type) => match &*pat_type.ty { Type::Path(type_path) => { let path = path_as_string(&type_path.path); if let Some(ty_str) = path.split("::").last() { // Each string match corresponds to an imported type that corresponds to an // **owned** variable. For example: // // ```ignore // #[test] // async fn does_things(wid: WorkspacePk) { // // ... // } // ``` // // Note that several types such as `DalContextHead` may have interior // references and/or mutability, however the surrounding type is passed as // an owned type. match ty_str { "DalContext" => { let var = expander.setup_dal_context_default(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "DalContextBuilder" => { let var = expander.setup_dal_context_builder(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "DalContextHead" => { let var = expander.setup_dal_context_head(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "DalContextHeadRef" => { let var = expander.setup_dal_context_head_ref(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "DalContextHeadMutRef" => { let var = expander.setup_dal_context_head_mut_ref(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "ServicesContext" => { let var = expander.setup_services_context(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "WorkspacePk" => { let var = expander.setup_workspace_pk(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } "WorkspaceSignup" => { let var = expander.setup_workspace_signup(); let var = var.0.as_ref(); expander.push_arg(parse_quote! {#var}); } "AuditDatabaseContext" => { let var = expander.setup_audit_database_context(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } _ => panic!("unexpected argument type: {type_path:?}"), }; } } Type::Reference(type_ref) => match &*type_ref.elem { Type::Path(type_path) => { let path = path_as_string(&type_path.path); if let Some(ty_str) = path.split("::").last() { // Each string match corresponds to an imported type that corresponds // to an **borrowed**/**referenced** variable. For example: // // ```ignore // #[test] // async fn does_things( // ctx: &mut DalContext, // nw: &WorkspaceSignup // ) { // // ... // } // ``` // // In the above example, both would be matched types in this section, // even though `ctx` is a mutable reference and `nw` is an immutable // reference. match ty_str { "DalContext" => { if type_ref.mutability.is_some() { let var = expander.setup_dal_context_default_mut(); let var = var.as_ref(); expander.push_arg(parse_quote! {&mut #var}); } else { let var = expander.setup_dal_context_default(); let var = var.as_ref(); expander.push_arg(parse_quote! {&#var}); } } "DalContextBuilder" => { let var = expander.setup_dal_context_builder(); let var = var.as_ref(); expander.push_arg(parse_quote! {&#var}); } "ServicesContext" => { let var = expander.setup_services_context(); let var = var.as_ref(); expander.push_arg(parse_quote! {&#var}); } "WorkspaceSignup" => { let var = expander.setup_workspace_signup(); let var = var.0.as_ref(); expander.push_arg(parse_quote! {&#var}); } "AuditDatabaseContext" => { let var = expander.setup_audit_database_context(); let var = var.as_ref(); expander.push_arg(parse_quote! {#var}); } _ => panic!("unexpected argument reference type: {type_ref:?}"), } } } unsupported => { panic!("argument reference type not supported: {unsupported:?}") } }, unsupported => panic!("argument type not supported: {unsupported:?}"), }, FnArg::Receiver(_) => { panic!("argument does not support receiver/method style (i.e. using `self`)") } } } expander.finish() } struct DalTestFnSetup { code: TokenStream, fn_args: Punctuated<Expr, Comma>, } impl FnSetup for DalTestFnSetup { fn into_parts(self) -> (TokenStream, Punctuated<Expr, Comma>) { (self.code, self.fn_args) } } struct DalTestFnSetupExpander { code: TokenStream, args: Punctuated<Expr, Comma>, test_context: Option<Rc<Ident>>, cancellation_token: Option<Rc<Ident>>, task_tracker: Option<Rc<Ident>>, pinga_server: Option<Rc<Ident>>, start_pinga_server: Option<()>, edda_server: Option<Rc<Ident>>, start_edda_server: Option<()>, rebaser_server: Option<Rc<Ident>>, start_rebaser_server: Option<()>, forklift_server: Option<Rc<Ident>>, start_forklift_server: Option<()>, veritech_server: Option<Rc<Ident>>, start_veritech_server: Option<()>, services_context: Option<Rc<Ident>>, dal_context_builder: Option<Rc<Ident>>, workspace_signup: Option<(Rc<Ident>, Rc<Ident>)>, workspace_pk: Option<Rc<Ident>>, dal_context_default: Option<Rc<Ident>>, dal_context_default_mut: Option<Rc<Ident>>, dal_context_head: Option<Rc<Ident>>, dal_context_head_ref: Option<Rc<Ident>>, dal_context_head_mut_ref: Option<Rc<Ident>>, } impl DalTestFnSetupExpander { fn new() -> Self { Self { code: TokenStream::new(), args: Punctuated::new(), test_context: None, cancellation_token: None, task_tracker: None, pinga_server: None, start_pinga_server: None, edda_server: None, start_edda_server: None, rebaser_server: None, start_rebaser_server: None, forklift_server: None, start_forklift_server: None, veritech_server: None, start_veritech_server: None, services_context: None, dal_context_builder: None, workspace_signup: None, workspace_pk: None, dal_context_default: None, dal_context_default_mut: None, dal_context_head: None, dal_context_head_ref: None, dal_context_head_mut_ref: None, } } #[allow(dead_code)] fn has_args(&self) -> bool { !self.args.is_empty() } fn finish(self) -> DalTestFnSetup { DalTestFnSetup { code: self.code, fn_args: self.args, } } } impl FnSetupExpander for DalTestFnSetupExpander { fn code_extend<I: IntoIterator<Item = proc_macro2::TokenTree>>(&mut self, stream: I) { self.code.extend(stream) } fn push_arg(&mut self, arg: Expr) { self.args.push(arg); } fn test_context(&self) -> Option<&Rc<Ident>> { self.test_context.as_ref() } fn set_test_context(&mut self, value: Option<Rc<Ident>>) { self.test_context = value; } fn cancellation_token(&self) -> Option<&Rc<Ident>> { self.cancellation_token.as_ref() } fn set_cancellation_token(&mut self, value: Option<Rc<Ident>>) { self.cancellation_token = value; } fn task_tracker(&self) -> Option<&Rc<Ident>> { self.task_tracker.as_ref() } fn set_task_tracker(&mut self, value: Option<Rc<Ident>>) { self.task_tracker = value; } fn pinga_server(&self) -> Option<&Rc<Ident>> { self.pinga_server.as_ref() } fn set_pinga_server(&mut self, value: Option<Rc<Ident>>) { self.pinga_server = value; } fn start_pinga_server(&self) -> Option<()> { self.start_pinga_server } fn set_start_pinga_server(&mut self, value: Option<()>) { self.start_pinga_server = value; } fn edda_server(&self) -> Option<&Rc<Ident>> { self.edda_server.as_ref() } fn set_edda_server(&mut self, value: Option<Rc<Ident>>) { self.edda_server = value; } fn start_edda_server(&self) -> Option<()> { self.start_edda_server } fn set_start_edda_server(&mut self, value: Option<()>) { self.start_edda_server = value; } fn rebaser_server(&self) -> Option<&Rc<Ident>> { self.rebaser_server.as_ref() } fn set_rebaser_server(&mut self, value: Option<Rc<Ident>>) { self.rebaser_server = value; } fn start_rebaser_server(&self) -> Option<()> { self.start_rebaser_server } fn set_start_rebaser_server(&mut self, value: Option<()>) { self.start_rebaser_server = value; } fn forklift_server(&self) -> Option<&Rc<Ident>> { self.forklift_server.as_ref() } fn set_forklift_server(&mut self, value: Option<Rc<Ident>>) { self.forklift_server = value; } fn start_forklift_server(&self) -> Option<()> { self.start_forklift_server } fn set_start_forklift_server(&mut self, value: Option<()>) { self.start_forklift_server = value; } fn veritech_server(&self) -> Option<&Rc<Ident>> { self.veritech_server.as_ref() } fn set_veritech_server(&mut self, value: Option<Rc<Ident>>) { self.veritech_server = value; } fn start_veritech_server(&self) -> Option<()> { self.start_veritech_server } fn set_start_veritech_server(&mut self, value: Option<()>) { self.start_veritech_server = value; } fn services_context(&self) -> Option<&Rc<Ident>> { self.services_context.as_ref() } fn set_services_context(&mut self, value: Option<Rc<Ident>>) { self.services_context = value; } fn dal_context_builder(&self) -> Option<&Rc<Ident>> { self.dal_context_builder.as_ref() } fn set_dal_context_builder(&mut self, value: Option<Rc<Ident>>) { self.dal_context_builder = value; } fn workspace_signup(&self) -> Option<&(Rc<Ident>, Rc<Ident>)> { self.workspace_signup.as_ref() } fn set_workspace_signup(&mut self, value: Option<(Rc<Ident>, Rc<Ident>)>) { self.workspace_signup = value; } fn workspace_pk(&self) -> Option<&Rc<Ident>> { self.workspace_pk.as_ref() } fn set_workspace_pk(&mut self, value: Option<Rc<Ident>>) { self.workspace_pk = value; } fn dal_context_default(&self) -> Option<&Rc<Ident>> { self.dal_context_default.as_ref() } fn set_dal_context_default(&mut self, value: Option<Rc<Ident>>) { self.dal_context_default = value; } fn dal_context_default_mut(&self) -> Option<&Rc<Ident>> { self.dal_context_default_mut.as_ref() } fn set_dal_context_default_mut(&mut self, value: Option<Rc<Ident>>) { self.dal_context_default_mut = value; } fn dal_context_head(&self) -> Option<&Rc<Ident>> { self.dal_context_head.as_ref() } fn set_dal_context_head(&mut self, value: Option<Rc<Ident>>) { self.dal_context_head = value; } fn dal_context_head_ref(&self) -> Option<&Rc<Ident>> { self.dal_context_head_ref.as_ref() } fn set_dal_context_head_ref(&mut self, value: Option<Rc<Ident>>) { self.dal_context_head_ref = value; } fn dal_context_head_mut_ref(&self) -> Option<&Rc<Ident>> { self.dal_context_head_mut_ref.as_ref() } fn set_dal_context_head_mut_ref(&mut self, value: Option<Rc<Ident>>) { self.dal_context_head_mut_ref = value; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server