From 2d1a6b2d432408d74eb57e0bda3f7434617e1070 Mon Sep 17 00:00:00 2001 From: HampusM Date: Wed, 20 Jul 2022 14:29:45 +0200 Subject: refactor: reorganize folder hierarchy --- Cargo.toml | 22 +- macros/Cargo.toml | 19 + macros/src/declare_interface_args.rs | 23 + macros/src/factory_type_alias.rs | 84 ++++ macros/src/injectable_impl.rs | 245 ++++++++++ macros/src/injectable_macro_args.rs | 17 + macros/src/lib.rs | 96 ++++ macros/src/libs/intertrait_macros/LICENSE-APACHE | 176 +++++++ macros/src/libs/intertrait_macros/LICENSE-MIT | 17 + macros/src/libs/intertrait_macros/gen_caster.rs | 55 +++ macros/src/libs/intertrait_macros/mod.rs | 14 + macros/src/libs/mod.rs | 1 + src/castable_factory.rs | 76 ++++ src/di_container.rs | 504 +++++++++++++++++++++ src/errors/di_container.rs | 17 + src/errors/injectable.rs | 17 + src/errors/mod.rs | 2 + src/interfaces/factory.rs | 10 + src/interfaces/injectable.rs | 17 + src/interfaces/mod.rs | 2 + src/lib.rs | 22 + src/libs/intertrait/LICENSE-APACHE | 176 +++++++ src/libs/intertrait/LICENSE-MIT | 17 + src/libs/intertrait/cast_box.rs | 32 ++ src/libs/intertrait/cast_rc.rs | 34 ++ src/libs/intertrait/hasher.rs | 48 ++ src/libs/intertrait/mod.rs | 137 ++++++ src/libs/mod.rs | 3 + src/provider.rs | 82 ++++ src/ptr.rs | 6 + syrette/Cargo.toml | 19 - syrette/README.md | 11 - syrette/src/castable_factory.rs | 76 ---- syrette/src/di_container.rs | 504 --------------------- syrette/src/errors/di_container.rs | 17 - syrette/src/errors/injectable.rs | 17 - syrette/src/errors/mod.rs | 2 - syrette/src/interfaces/factory.rs | 10 - syrette/src/interfaces/injectable.rs | 17 - syrette/src/interfaces/mod.rs | 2 - syrette/src/lib.rs | 22 - syrette/src/libs/intertrait/LICENSE-APACHE | 176 ------- syrette/src/libs/intertrait/LICENSE-MIT | 17 - syrette/src/libs/intertrait/cast_box.rs | 32 -- syrette/src/libs/intertrait/cast_rc.rs | 34 -- syrette/src/libs/intertrait/hasher.rs | 48 -- syrette/src/libs/intertrait/mod.rs | 137 ------ syrette/src/libs/mod.rs | 3 - syrette/src/provider.rs | 82 ---- syrette/src/ptr.rs | 6 - syrette_macros/Cargo.toml | 19 - syrette_macros/src/declare_interface_args.rs | 23 - syrette_macros/src/factory_type_alias.rs | 84 ---- syrette_macros/src/injectable_impl.rs | 245 ---------- syrette_macros/src/injectable_macro_args.rs | 17 - syrette_macros/src/lib.rs | 96 ---- .../src/libs/intertrait_macros/LICENSE-APACHE | 176 ------- .../src/libs/intertrait_macros/LICENSE-MIT | 17 - .../src/libs/intertrait_macros/gen_caster.rs | 55 --- syrette_macros/src/libs/intertrait_macros/mod.rs | 14 - syrette_macros/src/libs/mod.rs | 1 - 61 files changed, 1969 insertions(+), 1981 deletions(-) create mode 100644 macros/Cargo.toml create mode 100644 macros/src/declare_interface_args.rs create mode 100644 macros/src/factory_type_alias.rs create mode 100644 macros/src/injectable_impl.rs create mode 100644 macros/src/injectable_macro_args.rs create mode 100644 macros/src/lib.rs create mode 100644 macros/src/libs/intertrait_macros/LICENSE-APACHE create mode 100644 macros/src/libs/intertrait_macros/LICENSE-MIT create mode 100644 macros/src/libs/intertrait_macros/gen_caster.rs create mode 100644 macros/src/libs/intertrait_macros/mod.rs create mode 100644 macros/src/libs/mod.rs create mode 100644 src/castable_factory.rs create mode 100644 src/di_container.rs create mode 100644 src/errors/di_container.rs create mode 100644 src/errors/injectable.rs create mode 100644 src/errors/mod.rs create mode 100644 src/interfaces/factory.rs create mode 100644 src/interfaces/injectable.rs create mode 100644 src/interfaces/mod.rs create mode 100644 src/lib.rs create mode 100644 src/libs/intertrait/LICENSE-APACHE create mode 100644 src/libs/intertrait/LICENSE-MIT create mode 100644 src/libs/intertrait/cast_box.rs create mode 100644 src/libs/intertrait/cast_rc.rs create mode 100644 src/libs/intertrait/hasher.rs create mode 100644 src/libs/intertrait/mod.rs create mode 100644 src/libs/mod.rs create mode 100644 src/provider.rs create mode 100644 src/ptr.rs delete mode 100644 syrette/Cargo.toml delete mode 100644 syrette/README.md delete mode 100644 syrette/src/castable_factory.rs delete mode 100644 syrette/src/di_container.rs delete mode 100644 syrette/src/errors/di_container.rs delete mode 100644 syrette/src/errors/injectable.rs delete mode 100644 syrette/src/errors/mod.rs delete mode 100644 syrette/src/interfaces/factory.rs delete mode 100644 syrette/src/interfaces/injectable.rs delete mode 100644 syrette/src/interfaces/mod.rs delete mode 100644 syrette/src/lib.rs delete mode 100644 syrette/src/libs/intertrait/LICENSE-APACHE delete mode 100644 syrette/src/libs/intertrait/LICENSE-MIT delete mode 100644 syrette/src/libs/intertrait/cast_box.rs delete mode 100644 syrette/src/libs/intertrait/cast_rc.rs delete mode 100644 syrette/src/libs/intertrait/hasher.rs delete mode 100644 syrette/src/libs/intertrait/mod.rs delete mode 100644 syrette/src/libs/mod.rs delete mode 100644 syrette/src/provider.rs delete mode 100644 syrette/src/ptr.rs delete mode 100644 syrette_macros/Cargo.toml delete mode 100644 syrette_macros/src/declare_interface_args.rs delete mode 100644 syrette_macros/src/factory_type_alias.rs delete mode 100644 syrette_macros/src/injectable_impl.rs delete mode 100644 syrette_macros/src/injectable_macro_args.rs delete mode 100644 syrette_macros/src/lib.rs delete mode 100644 syrette_macros/src/libs/intertrait_macros/LICENSE-APACHE delete mode 100644 syrette_macros/src/libs/intertrait_macros/LICENSE-MIT delete mode 100644 syrette_macros/src/libs/intertrait_macros/gen_caster.rs delete mode 100644 syrette_macros/src/libs/intertrait_macros/mod.rs delete mode 100644 syrette_macros/src/libs/mod.rs diff --git a/Cargo.toml b/Cargo.toml index a0e6dd9..41d00f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,24 @@ +[package] +name = "syrette" +version = "0.1.0" +license = "LGPL-2.1" +authors = ["HampusM "] +description = "The convenient dependency injection library" +repository = "https://git.hampusmat.com/syrette" +keywords = ["DI", "dependency-injection", "ioc", "inversion-of-control"] +edition = "2021" + +[dependencies] +syrette_macros = "0.1.0" +linkme = "0.3.0" +once_cell = "1.4" +error-stack = "0.1.1" + +[dev_dependencies] +mockall = "0.11.1" + [workspace] members = [ - "syrette", - "syrette_macros", + "macros", "example", ] diff --git a/macros/Cargo.toml b/macros/Cargo.toml new file mode 100644 index 0000000..74b4f91 --- /dev/null +++ b/macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "syrette_macros" +version = "0.1.0" +license = "LGPL-2.1" +authors = ["HampusM "] +description = "Macros for Syrette, the convenient dependency injection library" +repository = "https://git.hampusmat.com/syrette" +keywords = ["DI", "dependency-injection", "ioc", "inversion-of-control"] +edition = "2021" + +[lib] +proc_macro = true + +[dependencies] +syn = { version = "1.0.96", features = ["full"] } +quote = "1.0.18" +proc-macro2 = "1.0.40" +uuid = { version = "0.8", features = ["v4"] } + diff --git a/macros/src/declare_interface_args.rs b/macros/src/declare_interface_args.rs new file mode 100644 index 0000000..b54f458 --- /dev/null +++ b/macros/src/declare_interface_args.rs @@ -0,0 +1,23 @@ +use syn::parse::{Parse, ParseStream, Result}; +use syn::{Path, Token, Type}; + +pub struct DeclareInterfaceArgs +{ + pub implementation: Type, + pub interface: Path, +} + +impl Parse for DeclareInterfaceArgs +{ + fn parse(input: ParseStream) -> Result + { + let implementation: Type = input.parse()?; + + input.parse::]>()?; + + Ok(Self { + implementation, + interface: input.parse()?, + }) + } +} diff --git a/macros/src/factory_type_alias.rs b/macros/src/factory_type_alias.rs new file mode 100644 index 0000000..8ea7baa --- /dev/null +++ b/macros/src/factory_type_alias.rs @@ -0,0 +1,84 @@ +use syn::parse::{Parse, ParseStream}; +use syn::{GenericArgument, ItemType, Path, Type, TypeParamBound, TypeTuple}; + +pub struct FactoryTypeAlias +{ + pub type_alias: ItemType, + pub factory_interface: Path, + pub arg_types: TypeTuple, + pub return_type: Type, +} + +impl Parse for FactoryTypeAlias +{ + #[allow(clippy::match_wildcard_for_single_variants)] + fn parse(input: ParseStream) -> syn::Result + { + let type_alias = match input.parse::() { + Ok(type_alias) => Ok(type_alias), + Err(_) => Err(input.error("Expected a type alias")), + }?; + + let aliased_trait = match &type_alias.ty.as_ref() { + Type::TraitObject(alias_type) => Ok(alias_type), + &_ => Err(input.error("Expected the aliased type to be a trait")), + }?; + + if aliased_trait.bounds.len() != 1 { + return Err(input.error("Expected the aliased trait to have a single bound.")); + } + + let bound_path = &match aliased_trait.bounds.first().unwrap() { + TypeParamBound::Trait(trait_bound) => Ok(trait_bound), + &_ => { + Err(input.error("Expected the bound of the aliased trait to be a trait")) + } + }? + .path; + + if bound_path.segments.is_empty() + || bound_path.segments.last().unwrap().ident != "IFactory" + { + return Err(input + .error("Expected the bound of the aliased trait to be 'dyn IFactory'")); + } + + let angle_bracketed_args = match &bound_path.segments.last().unwrap().arguments { + syn::PathArguments::AngleBracketed(angle_bracketed_args) => { + Ok(angle_bracketed_args) + } + &_ => { + Err(input.error("Expected angle bracketed arguments for 'dyn IFactory'")) + } + }?; + + let arg_types = match &angle_bracketed_args.args[0] { + GenericArgument::Type(arg_types_type) => match arg_types_type { + Type::Tuple(arg_types) => Ok(arg_types), + &_ => Err(input.error(concat!( + "Expected the first angle bracketed argument ", + "of 'dyn IFactory' to be a type tuple" + ))), + }, + &_ => Err(input.error(concat!( + "Expected the first angle bracketed argument ", + "of 'dyn IFactory' to be a type" + ))), + }?; + + let return_type = match &angle_bracketed_args.args[1] { + GenericArgument::Type(arg_type) => Ok(arg_type), + &_ => Err(input.error(concat!( + "Expected the second angle bracketed argument ", + "of 'dyn IFactory' to be a type" + ))), + }?; + + Ok(Self { + type_alias: type_alias.clone(), + factory_interface: bound_path.clone(), + arg_types: arg_types.clone(), + return_type: return_type.clone(), + }) + } +} diff --git a/macros/src/injectable_impl.rs b/macros/src/injectable_impl.rs new file mode 100644 index 0000000..3000253 --- /dev/null +++ b/macros/src/injectable_impl.rs @@ -0,0 +1,245 @@ +use quote::{quote, ToTokens}; +use syn::parse::{Parse, ParseStream}; +use syn::{ + parse_str, punctuated::Punctuated, token::Comma, ExprMethodCall, FnArg, + GenericArgument, Ident, ImplItem, ImplItemMethod, ItemImpl, Path, PathArguments, + Type, TypePath, +}; + +const DI_CONTAINER_VAR_NAME: &str = "di_container"; + +pub struct InjectableImpl +{ + pub dependency_types: Vec, + pub self_type: Type, + pub original_impl: ItemImpl, +} + +impl Parse for InjectableImpl +{ + fn parse(input: ParseStream) -> syn::Result + { + match input.parse::() { + Ok(impl_parsed_input) => { + match Self::_get_dependency_types(&impl_parsed_input) { + Ok(dependency_types) => Ok(Self { + dependency_types, + self_type: impl_parsed_input.self_ty.as_ref().clone(), + original_impl: impl_parsed_input, + }), + Err(error_msg) => Err(input.error(error_msg)), + } + } + Err(_) => Err(input.error("Expected an impl")), + } + } +} + +impl InjectableImpl +{ + pub fn expand(&self) -> proc_macro2::TokenStream + { + let original_impl = &self.original_impl; + let self_type = &self.self_type; + + let di_container_var: Ident = parse_str(DI_CONTAINER_VAR_NAME).unwrap(); + + let get_dependencies = Self::_create_get_dependencies(&self.dependency_types); + + quote! { + #original_impl + + impl syrette::interfaces::injectable::Injectable for #self_type { + fn resolve( + #di_container_var: &syrette::DIContainer + ) -> error_stack::Result< + syrette::ptr::InterfacePtr, + syrette::errors::injectable::ResolveError> + { + use error_stack::ResultExt; + + return Ok(syrette::ptr::InterfacePtr::new(Self::new( + #(#get_dependencies + .change_context(syrette::errors::injectable::ResolveError) + .attach_printable( + format!( + "Unable to resolve a dependency of {}", + std::any::type_name::<#self_type>() + ) + )? + ),* + ))); + } + } + } + } + + fn _create_get_dependencies(dependency_types: &[Type]) -> Vec + { + dependency_types + .iter() + .filter_map(|dep_type| match dep_type { + Type::TraitObject(dep_type_trait) => Some( + parse_str( + format!( + "{}.get::<{}>()", + DI_CONTAINER_VAR_NAME, + dep_type_trait.to_token_stream() + ) + .as_str(), + ) + .unwrap(), + ), + Type::Path(dep_type_path) => { + let dep_type_path_str = Self::_path_to_string(&dep_type_path.path); + + let get_method_name = if dep_type_path_str.ends_with("Factory") { + "get_factory" + } else { + "get" + }; + + Some( + parse_str( + format!( + "{}.{}::<{}>()", + DI_CONTAINER_VAR_NAME, get_method_name, dep_type_path_str + ) + .as_str(), + ) + .unwrap(), + ) + } + &_ => None, + }) + .collect() + } + + fn _find_method_by_name<'impl_lt>( + item_impl: &'impl_lt ItemImpl, + method_name: &'static str, + ) -> Option<&'impl_lt ImplItemMethod> + { + let impl_items = &item_impl.items; + + impl_items + .iter() + .filter_map(|impl_item| match impl_item { + ImplItem::Method(method_item) => Some(method_item), + &_ => None, + }) + .find(|method_item| method_item.sig.ident == method_name) + } + + #[allow(clippy::match_wildcard_for_single_variants)] + fn get_has_fn_args_self(fn_args: &Punctuated) -> bool + { + fn_args.iter().any(|arg| match arg { + FnArg::Receiver(_) => true, + &_ => false, + }) + } + + fn _get_fn_arg_type_paths(fn_args: &Punctuated) -> Vec<&TypePath> + { + fn_args + .iter() + .filter_map(|arg| match arg { + FnArg::Typed(typed_fn_arg) => match typed_fn_arg.ty.as_ref() { + Type::Path(arg_type_path) => Some(arg_type_path), + Type::Reference(ref_type_path) => match ref_type_path.elem.as_ref() { + Type::Path(arg_type_path) => Some(arg_type_path), + &_ => None, + }, + &_ => None, + }, + FnArg::Receiver(_receiver_fn_arg) => None, + }) + .collect() + } + + fn _path_to_string(path: &Path) -> String + { + path.segments + .pairs() + .fold(String::new(), |mut acc, segment_pair| { + let segment_ident = &segment_pair.value().ident; + + acc.push_str(segment_ident.to_string().as_str()); + + let opt_colon_two = segment_pair.punct(); + + match opt_colon_two { + Some(colon_two) => { + acc.push_str(colon_two.to_token_stream().to_string().as_str()); + } + None => {} + } + + acc + }) + } + + fn _is_type_path_ptr(type_path: &TypePath) -> bool + { + let arg_type_path_string = Self::_path_to_string(&type_path.path); + + arg_type_path_string == "InterfacePtr" + || arg_type_path_string == "ptr::InterfacePtr" + || arg_type_path_string == "syrrete::ptr::InterfacePtr" + || arg_type_path_string == "FactoryPtr" + || arg_type_path_string == "ptr::FactoryPtr" + || arg_type_path_string == "syrrete::ptr::FactoryPtr" + } + + fn _get_dependency_types(item_impl: &ItemImpl) -> Result, &'static str> + { + let new_method_impl_item = match Self::_find_method_by_name(item_impl, "new") { + Some(method_item) => Ok(method_item), + None => Err("Missing a 'new' method"), + }?; + + let new_method_args = &new_method_impl_item.sig.inputs; + + if Self::get_has_fn_args_self(new_method_args) { + return Err("Unexpected self argument in 'new' method"); + } + + let new_method_arg_type_paths = Self::_get_fn_arg_type_paths(new_method_args); + + if new_method_arg_type_paths + .iter() + .any(|arg_type_path| !Self::_is_type_path_ptr(arg_type_path)) + { + return Err("All argument types in 'new' method must ptr types"); + } + + Ok(new_method_arg_type_paths + .iter() + .filter_map(|arg_type_path| { + // Assume the type path has a last segment. + let last_path_segment = arg_type_path.path.segments.last().unwrap(); + + match &last_path_segment.arguments { + PathArguments::AngleBracketed(angle_bracketed_generic_args) => { + let generic_args = &angle_bracketed_generic_args.args; + + let opt_first_generic_arg = generic_args.first(); + + // Assume a first generic argument exists because InterfacePtr and + // FactoryPtr requires one + let first_generic_arg = opt_first_generic_arg.as_ref().unwrap(); + + match first_generic_arg { + GenericArgument::Type(first_generic_arg_type) => { + Some(first_generic_arg_type.clone()) + } + &_ => None, + } + } + &_ => None, + } + }) + .collect()) + } +} diff --git a/macros/src/injectable_macro_args.rs b/macros/src/injectable_macro_args.rs new file mode 100644 index 0000000..4ef4389 --- /dev/null +++ b/macros/src/injectable_macro_args.rs @@ -0,0 +1,17 @@ +use syn::parse::{Parse, ParseStream}; +use syn::TypePath; + +pub struct InjectableMacroArgs +{ + pub interface: TypePath, +} + +impl Parse for InjectableMacroArgs +{ + fn parse(input: ParseStream) -> syn::Result + { + Ok(Self { + interface: input.parse()?, + }) + } +} diff --git a/macros/src/lib.rs b/macros/src/lib.rs new file mode 100644 index 0000000..ed1a509 --- /dev/null +++ b/macros/src/lib.rs @@ -0,0 +1,96 @@ +#![deny(clippy::all)] +#![deny(clippy::pedantic)] + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse, parse_macro_input}; + +mod declare_interface_args; +mod factory_type_alias; +mod injectable_impl; +mod injectable_macro_args; +mod libs; + +use declare_interface_args::DeclareInterfaceArgs; +use factory_type_alias::FactoryTypeAlias; +use injectable_impl::InjectableImpl; +use injectable_macro_args::InjectableMacroArgs; +use libs::intertrait_macros::gen_caster::generate_caster; + +/// Makes a struct injectable. Thereby usable with `DIContainer`. +/// +/// # Arguments +/// * A interface trait the struct implements. +/// +/// # Panics +/// If the attributed item is not a impl. +#[proc_macro_attribute] +pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenStream +{ + let InjectableMacroArgs { + interface: interface_type_path, + } = parse_macro_input!(args_stream); + + let injectable_impl: InjectableImpl = parse(impl_stream).unwrap(); + + let expanded_injectable_impl = injectable_impl.expand(); + + let self_type = &injectable_impl.self_type; + + quote! { + #expanded_injectable_impl + + syrette::declare_interface!(#self_type -> #interface_type_path); + } + .into() +} + +/// Makes a type alias usable as a factory interface. +/// +/// # Panics +/// If the attributed item is not a type alias. +#[proc_macro_attribute] +pub fn factory(_: TokenStream, type_alias_stream: TokenStream) -> TokenStream +{ + let FactoryTypeAlias { + type_alias, + factory_interface, + arg_types, + return_type, + } = parse(type_alias_stream).unwrap(); + + quote! { + #type_alias + + syrette::declare_interface!( + syrette::castable_factory::CastableFactory< + #arg_types, + #return_type + > -> #factory_interface + ); + + syrette::declare_interface!( + syrette::castable_factory::CastableFactory< + #arg_types, + #return_type + > -> syrette::castable_factory::AnyFactory + ); + } + .into() +} + +/// Declares the interface trait of a implementation. +/// +/// # Arguments +/// {Implementation} -> {Interface} +/// +#[proc_macro] +pub fn declare_interface(input: TokenStream) -> TokenStream +{ + let DeclareInterfaceArgs { + implementation, + interface, + } = parse_macro_input!(input); + + generate_caster(&implementation, &interface).into() +} diff --git a/macros/src/libs/intertrait_macros/LICENSE-APACHE b/macros/src/libs/intertrait_macros/LICENSE-APACHE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/macros/src/libs/intertrait_macros/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/macros/src/libs/intertrait_macros/LICENSE-MIT b/macros/src/libs/intertrait_macros/LICENSE-MIT new file mode 100644 index 0000000..969d061 --- /dev/null +++ b/macros/src/libs/intertrait_macros/LICENSE-MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/macros/src/libs/intertrait_macros/gen_caster.rs b/macros/src/libs/intertrait_macros/gen_caster.rs new file mode 100644 index 0000000..9126200 --- /dev/null +++ b/macros/src/libs/intertrait_macros/gen_caster.rs @@ -0,0 +1,55 @@ +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +use std::str::from_utf8_unchecked; + +use proc_macro2::TokenStream; +use quote::format_ident; +use quote::quote; +use quote::ToTokens; +use uuid::adapter::Simple; +use uuid::Uuid; + +pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens) -> TokenStream +{ + let mut fn_buf = [0u8; FN_BUF_LEN]; + + let fn_ident = format_ident!("{}", new_fn_name(&mut fn_buf)); + + let new_caster = quote! { + syrette::libs::intertrait::Caster::::new( + |from| from.downcast::<#ty>().unwrap(), + |from| from.downcast::<#ty>().unwrap(), + ) + }; + + quote! { + #[syrette::libs::linkme::distributed_slice(syrette::libs::intertrait::CASTERS)] + #[linkme(crate = syrette::libs::linkme)] + fn #fn_ident() -> (::std::any::TypeId, syrette::libs::intertrait::BoxedCaster) { + (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) + } + } +} + +const FN_PREFIX: &[u8] = b"__"; +const FN_BUF_LEN: usize = FN_PREFIX.len() + Simple::LENGTH; + +fn new_fn_name(buf: &mut [u8]) -> &str +{ + buf[..FN_PREFIX.len()].copy_from_slice(FN_PREFIX); + Uuid::new_v4() + .to_simple() + .encode_lower(&mut buf[FN_PREFIX.len()..]); + unsafe { from_utf8_unchecked(&buf[..FN_BUF_LEN]) } +} diff --git a/macros/src/libs/intertrait_macros/mod.rs b/macros/src/libs/intertrait_macros/mod.rs new file mode 100644 index 0000000..fa8c021 --- /dev/null +++ b/macros/src/libs/intertrait_macros/mod.rs @@ -0,0 +1,14 @@ +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +pub mod gen_caster; diff --git a/macros/src/libs/mod.rs b/macros/src/libs/mod.rs new file mode 100644 index 0000000..e4964de --- /dev/null +++ b/macros/src/libs/mod.rs @@ -0,0 +1 @@ +pub mod intertrait_macros; diff --git a/src/castable_factory.rs b/src/castable_factory.rs new file mode 100644 index 0000000..5d582bb --- /dev/null +++ b/src/castable_factory.rs @@ -0,0 +1,76 @@ +use crate::interfaces::factory::IFactory; +use crate::libs::intertrait::CastFrom; +use crate::ptr::InterfacePtr; + +pub trait AnyFactory: CastFrom {} + +pub struct CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ + func: &'static dyn Fn>, +} + +impl CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ + pub fn new( + func: &'static dyn Fn>, + ) -> Self + { + Self { func } + } +} + +impl IFactory + for CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ +} + +impl Fn for CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ + extern "rust-call" fn call(&self, args: Args) -> Self::Output + { + self.func.call(args) + } +} + +impl FnMut for CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ + extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output + { + self.call(args) + } +} + +impl FnOnce for CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ + type Output = InterfacePtr; + + extern "rust-call" fn call_once(self, args: Args) -> Self::Output + { + self.call(args) + } +} + +impl AnyFactory for CastableFactory +where + Args: 'static, + ReturnInterface: 'static + ?Sized, +{ +} diff --git a/src/di_container.rs b/src/di_container.rs new file mode 100644 index 0000000..6982a10 --- /dev/null +++ b/src/di_container.rs @@ -0,0 +1,504 @@ +use std::any::{type_name, TypeId}; +use std::collections::HashMap; +use std::marker::PhantomData; +use std::rc::Rc; + +use error_stack::{Report, ResultExt}; + +use crate::castable_factory::CastableFactory; +use crate::errors::di_container::DIContainerError; +use crate::interfaces::factory::IFactory; +use crate::interfaces::injectable::Injectable; +use crate::libs::intertrait::cast_box::CastBox; +use crate::libs::intertrait::cast_rc::CastRc; +use crate::provider::{FactoryProvider, IProvider, InjectableTypeProvider, Providable}; +use crate::ptr::{FactoryPtr, InterfacePtr}; + +/// Binding builder for type `Interface` inside a [`DIContainer`]. +pub struct BindingBuilder<'di_container_lt, Interface> +where + Interface: 'static + ?Sized, +{ + di_container: &'di_container_lt mut DIContainer, + interface_phantom: PhantomData, +} + +impl<'di_container_lt, Interface> BindingBuilder<'di_container_lt, Interface> +where + Interface: 'static + ?Sized, +{ + fn new(di_container: &'di_container_lt mut DIContainer) -> Self + { + Self { + di_container, + interface_phantom: PhantomData, + } + } + + /// Creates a binding of type `Interface` to type `Implementation` inside of the + /// associated [`DIContainer`]. + pub fn to(&mut self) + where + Implementation: Injectable, + { + let interface_typeid = TypeId::of::(); + + self.di_container.bindings.insert( + interface_typeid, + Rc::new(InjectableTypeProvider::::new()), + ); + } + + /// Creates a binding of factory type `Interface` to a factory inside of the + /// associated [`DIContainer`]. + pub fn to_factory( + &mut self, + factory_func: &'static dyn Fn>, + ) where + Args: 'static, + Return: 'static + ?Sized, + Interface: IFactory, + { + let interface_typeid = TypeId::of::(); + + let factory_impl = CastableFactory::new(factory_func); + + self.di_container.bindings.insert( + interface_typeid, + Rc::new(FactoryProvider::new(FactoryPtr::new(factory_impl))), + ); + } +} + +/// Dependency injection container. +/// +/// # Examples +/// ``` +/// use std::collections::HashMap; +/// +/// use syrette::{DIContainer, injectable}; +/// use syrette::errors::di_container::DIContainerError; +/// +/// trait IDatabaseService +/// { +/// fn get_all_records(&self, table_name: String) -> HashMap; +/// } +/// +/// struct DatabaseService {} +/// +/// #[injectable(IDatabaseService)] +/// impl DatabaseService +/// { +/// fn new() -> Self +/// { +/// Self {} +/// } +/// } +/// +/// impl IDatabaseService for DatabaseService +/// { +/// fn get_all_records(&self, table_name: String) -> HashMap +/// { +/// // Do stuff here +/// HashMap::::new() +/// } +/// } +/// +/// fn main() -> error_stack::Result<(), DIContainerError> +/// { +/// let mut di_container = DIContainer::new(); +/// +/// di_container.bind::().to::(); +/// +/// let database_service = di_container.get::()?; +/// +/// Ok(()) +/// } +/// ``` +pub struct DIContainer +{ + bindings: HashMap>, +} + +impl DIContainer +{ + /// Returns a new `DIContainer`. + #[must_use] + pub fn new() -> Self + { + Self { + bindings: HashMap::new(), + } + } + + /// Returns a new [`BindingBuilder`] for the given interface. + pub fn bind(&mut self) -> BindingBuilder + where + Interface: 'static + ?Sized, + { + BindingBuilder::::new(self) + } + + /// Returns a new instance of the type bound with `Interface`. + /// + /// # Errors + /// Will return `Err` if: + /// - No binding for `Interface` exists + /// - Resolving the binding for `Interface` fails + /// - Casting the binding for `Interface` fails + /// - The binding for `Interface` is not injectable + pub fn get( + &self, + ) -> error_stack::Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + let interface_typeid = TypeId::of::(); + + let interface_name = type_name::(); + + let binding = self.bindings.get(&interface_typeid).ok_or_else(|| { + Report::new(DIContainerError) + .attach_printable(format!("No binding exists for {}", interface_name)) + })?; + + let binding_providable = binding + .provide(self) + .change_context(DIContainerError) + .attach_printable(format!( + "Failed to resolve binding for interface {}", + interface_name + ))?; + + match binding_providable { + Providable::Injectable(binding_injectable) => { + let interface_box_result = binding_injectable.cast::(); + + match interface_box_result { + Ok(interface_box) => Ok(interface_box), + Err(_) => Err(Report::new(DIContainerError).attach_printable( + format!("Unable to cast binding for {}", interface_name), + )), + } + } + Providable::Factory(_) => Err(Report::new(DIContainerError) + .attach_printable(format!( + "Binding for {} is not injectable", + interface_name + ))), + } + } + + /// Returns the factory bound with factory type `Interface`. + /// + /// # Errors + /// Will return `Err` if: + /// - No binding for `Interface` exists + /// - Resolving the binding for `Interface` fails + /// - Casting the binding for `Interface` fails + /// - The binding for `Interface` is not a factory + pub fn get_factory( + &self, + ) -> error_stack::Result, DIContainerError> + where + Interface: 'static + ?Sized, + { + let interface_typeid = TypeId::of::(); + + let interface_name = type_name::(); + + let binding = self.bindings.get(&interface_typeid).ok_or_else(|| { + Report::new(DIContainerError) + .attach_printable(format!("No binding exists for {}", interface_name)) + })?; + + let binding_providable = binding + .provide(self) + .change_context(DIContainerError) + .attach_printable(format!( + "Failed to resolve binding for interface {}", + interface_name + ))?; + + match binding_providable { + Providable::Factory(binding_factory) => { + let factory_box_result = binding_factory.cast::(); + + match factory_box_result { + Ok(interface_box) => Ok(interface_box), + Err(_) => Err(Report::new(DIContainerError).attach_printable( + format!("Unable to cast binding for {}", interface_name), + )), + } + } + Providable::Injectable(_) => Err(Report::new(DIContainerError) + .attach_printable(format!( + "Binding for {} is not a factory", + interface_name + ))), + } + } +} + +impl Default for DIContainer +{ + fn default() -> Self + { + Self::new() + } +} + +#[cfg(test)] +mod tests +{ + use mockall::mock; + + use super::*; + use crate::errors::injectable::ResolveError; + + #[test] + fn can_bind_to() + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + impl Injectable for UserManager + { + fn resolve( + _di_container: &DIContainer, + ) -> error_stack::Result< + InterfacePtr, + crate::errors::injectable::ResolveError, + > + where + Self: Sized, + { + Ok(InterfacePtr::new(Self {})) + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + assert_eq!(di_container.bindings.len(), 0); + + di_container.bind::().to::(); + + assert_eq!(di_container.bindings.len(), 1); + } + + #[test] + fn can_bind_to_factory() + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + impl UserManager + { + fn new() -> Self + { + Self {} + } + } + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + type IUserManagerFactory = dyn IFactory<(), dyn IUserManager>; + + let mut di_container: DIContainer = DIContainer::new(); + + assert_eq!(di_container.bindings.len(), 0); + + di_container.bind::().to_factory(&|| { + let user_manager: InterfacePtr = + InterfacePtr::new(UserManager::new()); + + user_manager + }); + + assert_eq!(di_container.bindings.len(), 1); + } + + #[test] + fn can_get() -> error_stack::Result<(), DIContainerError> + { + trait IUserManager + { + fn add_user(&self, user_id: i128); + + fn remove_user(&self, user_id: i128); + } + + struct UserManager {} + + use crate as syrette; + use crate::injectable; + + #[injectable(IUserManager)] + impl UserManager + { + fn new() -> Self + { + Self {} + } + } + + impl IUserManager for UserManager + { + fn add_user(&self, _user_id: i128) + { + // ... + } + + fn remove_user(&self, _user_id: i128) + { + // ... + } + } + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result; + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning(|_| { + Ok(Providable::Injectable( + InterfacePtr::new(UserManager::new()), + )) + }); + + di_container + .bindings + .insert(TypeId::of::(), Rc::new(mock_provider)); + + di_container.get::()?; + + Ok(()) + } + + #[test] + fn can_get_factory() -> error_stack::Result<(), DIContainerError> + { + trait IUserManager + { + fn add_user(&mut self, user_id: i128); + + fn remove_user(&mut self, user_id: i128); + } + + struct UserManager + { + users: Vec, + } + + impl UserManager + { + fn new(users: Vec) -> Self + { + Self { users } + } + } + + impl IUserManager for UserManager + { + fn add_user(&mut self, user_id: i128) + { + self.users.push(user_id); + } + + fn remove_user(&mut self, user_id: i128) + { + let user_index = + self.users.iter().position(|user| *user == user_id).unwrap(); + + self.users.remove(user_index); + } + } + + use crate as syrette; + + #[crate::factory] + type IUserManagerFactory = dyn IFactory<(Vec,), dyn IUserManager>; + + mock! { + Provider {} + + impl IProvider for Provider + { + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result; + } + } + + let mut di_container: DIContainer = DIContainer::new(); + + let mut mock_provider = MockProvider::new(); + + mock_provider.expect_provide().returning(|_| { + Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( + &|users| { + let user_manager: InterfacePtr = + InterfacePtr::new(UserManager::new(users)); + + user_manager + }, + )))) + }); + + di_container + .bindings + .insert(TypeId::of::(), Rc::new(mock_provider)); + + di_container.get_factory::()?; + + Ok(()) + } +} diff --git a/src/errors/di_container.rs b/src/errors/di_container.rs new file mode 100644 index 0000000..f2b8add --- /dev/null +++ b/src/errors/di_container.rs @@ -0,0 +1,17 @@ +use std::fmt; +use std::fmt::{Display, Formatter}; + +use error_stack::Context; + +#[derive(Debug)] +pub struct DIContainerError; + +impl Display for DIContainerError +{ + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + { + fmt.write_str("A DI container error has occurred") + } +} + +impl Context for DIContainerError {} diff --git a/src/errors/injectable.rs b/src/errors/injectable.rs new file mode 100644 index 0000000..6b0cdc5 --- /dev/null +++ b/src/errors/injectable.rs @@ -0,0 +1,17 @@ +use core::fmt; +use std::fmt::{Display, Formatter}; + +use error_stack::Context; + +#[derive(Debug)] +pub struct ResolveError; + +impl Display for ResolveError +{ + fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + { + fmt.write_str("Failed to resolve injectable struct") + } +} + +impl Context for ResolveError {} diff --git a/src/errors/mod.rs b/src/errors/mod.rs new file mode 100644 index 0000000..b0d50f0 --- /dev/null +++ b/src/errors/mod.rs @@ -0,0 +1,2 @@ +pub mod di_container; +pub mod injectable; diff --git a/src/interfaces/factory.rs b/src/interfaces/factory.rs new file mode 100644 index 0000000..c97fc09 --- /dev/null +++ b/src/interfaces/factory.rs @@ -0,0 +1,10 @@ +#![allow(clippy::module_name_repetitions)] +use crate::libs::intertrait::CastFrom; +use crate::ptr::InterfacePtr; + +pub trait IFactory: + Fn> + CastFrom +where + ReturnInterface: 'static + ?Sized, +{ +} diff --git a/src/interfaces/injectable.rs b/src/interfaces/injectable.rs new file mode 100644 index 0000000..24032a6 --- /dev/null +++ b/src/interfaces/injectable.rs @@ -0,0 +1,17 @@ +use crate::errors::injectable::ResolveError; +use crate::libs::intertrait::CastFrom; +use crate::ptr::InterfacePtr; +use crate::DIContainer; + +pub trait Injectable: CastFrom +{ + /// Resolves the dependencies of the injectable. + /// + /// # Errors + /// Will return `Err` if resolving the dependencies fails. + fn resolve( + di_container: &DIContainer, + ) -> error_stack::Result, ResolveError> + where + Self: Sized; +} diff --git a/src/interfaces/mod.rs b/src/interfaces/mod.rs new file mode 100644 index 0000000..921bb9c --- /dev/null +++ b/src/interfaces/mod.rs @@ -0,0 +1,2 @@ +pub mod factory; +pub mod injectable; diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..992f276 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,22 @@ +#![feature(unboxed_closures, fn_traits)] +#![deny(clippy::all)] +#![deny(clippy::pedantic)] + +//! Syrette +//! +//! Syrette is a collection of utilities useful for performing dependency injection. + +pub mod castable_factory; +pub mod di_container; +pub mod errors; +pub mod interfaces; +pub mod ptr; + +pub use di_container::*; +pub use syrette_macros::*; + +#[doc(hidden)] +pub mod libs; + +// Private +mod provider; diff --git a/src/libs/intertrait/LICENSE-APACHE b/src/libs/intertrait/LICENSE-APACHE new file mode 100644 index 0000000..d9a10c0 --- /dev/null +++ b/src/libs/intertrait/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/src/libs/intertrait/LICENSE-MIT b/src/libs/intertrait/LICENSE-MIT new file mode 100644 index 0000000..969d061 --- /dev/null +++ b/src/libs/intertrait/LICENSE-MIT @@ -0,0 +1,17 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/libs/intertrait/cast_box.rs b/src/libs/intertrait/cast_box.rs new file mode 100644 index 0000000..793efd0 --- /dev/null +++ b/src/libs/intertrait/cast_box.rs @@ -0,0 +1,32 @@ +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +use crate::libs::intertrait::{caster, CastFrom}; + +pub trait CastBox +{ + /// Casts a box to this trait into that of type `T`. If fails, returns the receiver. + fn cast(self: Box) -> Result, Box>; +} + +/// A blanket implementation of `CastBox` for traits extending `CastFrom`. +impl CastBox for S +{ + fn cast(self: Box) -> Result, Box> + { + match caster::((*self).type_id()) { + Some(caster) => Ok((caster.cast_box)(self.box_any())), + None => Err(self), + } + } +} diff --git a/src/libs/intertrait/cast_rc.rs b/src/libs/intertrait/cast_rc.rs new file mode 100644 index 0000000..79d8d60 --- /dev/null +++ b/src/libs/intertrait/cast_rc.rs @@ -0,0 +1,34 @@ +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +use std::rc::Rc; + +use crate::libs::intertrait::{caster, CastFrom}; + +pub trait CastRc +{ + /// Casts an `Rc` for this trait into that for type `T`. + fn cast(self: Rc) -> Result, Rc>; +} + +/// A blanket implementation of `CastRc` for traits extending `CastFrom`. +impl CastRc for S +{ + fn cast(self: Rc) -> Result, Rc> + { + match caster::((*self).type_id()) { + Some(caster) => Ok((caster.cast_rc)(self.rc_any())), + None => Err(self), + } + } +} diff --git a/src/libs/intertrait/hasher.rs b/src/libs/intertrait/hasher.rs new file mode 100644 index 0000000..e7f110d --- /dev/null +++ b/src/libs/intertrait/hasher.rs @@ -0,0 +1,48 @@ +#![allow(clippy::module_name_repetitions)] + +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +use std::convert::TryInto; +use std::hash::{BuildHasherDefault, Hasher}; +use std::mem::size_of; + +/// A simple `Hasher` implementation tuned for performance. +#[derive(Default)] +pub struct FastHasher(u64); + +/// A `BuildHasher` for `FastHasher`. +pub type BuildFastHasher = BuildHasherDefault; + +impl Hasher for FastHasher +{ + fn finish(&self) -> u64 + { + self.0 + } + + fn write(&mut self, bytes: &[u8]) + { + let mut bytes = bytes; + while bytes.len() > size_of::() { + let (u64_bytes, remaining) = bytes.split_at(size_of::()); + + self.0 ^= u64::from_ne_bytes(u64_bytes.try_into().unwrap()); + + bytes = remaining; + } + self.0 ^= bytes + .iter() + .fold(0u64, |result, b| (result << 8) | u64::from(*b)); + } +} diff --git a/src/libs/intertrait/mod.rs b/src/libs/intertrait/mod.rs new file mode 100644 index 0000000..1daca64 --- /dev/null +++ b/src/libs/intertrait/mod.rs @@ -0,0 +1,137 @@ +/** + * Originally from Intertrait by CodeChain + * + * + * + * + * Licensed under either of + * + * Apache License, Version 2.0 (LICENSE-APACHE or ) + * MIT license (LICENSE-MIT or ) + + * at your option. +*/ +use std::any::{Any, TypeId}; +use std::collections::HashMap; +use std::rc::Rc; +use std::sync::Arc; + +use linkme::distributed_slice; +use once_cell::sync::Lazy; + +mod hasher; + +use hasher::BuildFastHasher; + +pub mod cast_box; +pub mod cast_rc; + +pub type BoxedCaster = Box; + +#[distributed_slice] +pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; + +static CASTER_MAP: Lazy> = + Lazy::new(|| { + CASTERS + .iter() + .map(|f| { + let (type_id, caster) = f(); + ((type_id, (*caster).type_id()), caster) + }) + .collect() + }); + +pub struct Caster +{ + /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait object + /// for trait `T`. + pub cast_box: fn(from: Box) -> Box, + + /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait object + /// for trait `T`. + pub cast_rc: fn(from: Rc) -> Rc, +} + +impl Caster +{ + pub fn new( + cast_box: fn(from: Box) -> Box, + cast_rc: fn(from: Rc) -> Rc, + ) -> Caster + { + Caster:: { cast_box, cast_rc } + } +} + +/// Returns a `Caster` from a concrete type `S` to a trait `T` implemented by it. +fn caster(type_id: TypeId) -> Option<&'static Caster> +{ + CASTER_MAP + .get(&(type_id, TypeId::of::>())) + .and_then(|caster| caster.downcast_ref::>()) +} + +/// `CastFrom` must be extended by a trait that wants to allow for casting into another trait. +/// +/// It is used for obtaining a trait object for [`Any`] from a trait object for its sub-trait, +/// and blanket implemented for all `Sized + Any + 'static` types. +/// +/// # Examples +/// ```ignore +/// trait Source: CastFrom { +/// ... +/// } +/// ``` +pub trait CastFrom: Any + 'static +{ + /// Returns a `Box` of `Any`, which is backed by the type implementing this trait. + fn box_any(self: Box) -> Box; + + /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait. + fn rc_any(self: Rc) -> Rc; +} + +pub trait CastFromSync: CastFrom + Sync + Send + 'static +{ + fn arc_any(self: Arc) -> Arc; +} + +impl CastFrom for T +{ + fn box_any(self: Box) -> Box + { + self + } + + fn rc_any(self: Rc) -> Rc + { + self + } +} + +impl CastFrom for dyn Any + 'static +{ + fn box_any(self: Box) -> Box + { + self + } + + fn rc_any(self: Rc) -> Rc + { + self + } +} + +impl CastFrom for dyn Any + Sync + Send + 'static +{ + fn box_any(self: Box) -> Box + { + self + } + + fn rc_any(self: Rc) -> Rc + { + self + } +} diff --git a/src/libs/mod.rs b/src/libs/mod.rs new file mode 100644 index 0000000..8d5583d --- /dev/null +++ b/src/libs/mod.rs @@ -0,0 +1,3 @@ +pub mod intertrait; + +pub extern crate linkme; diff --git a/src/provider.rs b/src/provider.rs new file mode 100644 index 0000000..3b7e04c --- /dev/null +++ b/src/provider.rs @@ -0,0 +1,82 @@ +#![allow(clippy::module_name_repetitions)] +use std::marker::PhantomData; + +use crate::castable_factory::AnyFactory; +use crate::errors::injectable::ResolveError; +use crate::interfaces::injectable::Injectable; +use crate::ptr::{FactoryPtr, InterfacePtr}; +use crate::DIContainer; + +extern crate error_stack; + +pub enum Providable +{ + Injectable(InterfacePtr), + Factory(FactoryPtr), +} + +pub trait IProvider +{ + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result; +} + +pub struct InjectableTypeProvider +where + InjectableType: Injectable, +{ + injectable_phantom: PhantomData, +} + +impl InjectableTypeProvider +where + InjectableType: Injectable, +{ + pub fn new() -> Self + { + Self { + injectable_phantom: PhantomData, + } + } +} + +impl IProvider for InjectableTypeProvider +where + InjectableType: Injectable, +{ + fn provide( + &self, + di_container: &DIContainer, + ) -> error_stack::Result + { + Ok(Providable::Injectable(InjectableType::resolve( + di_container, + )?)) + } +} + +pub struct FactoryProvider +{ + factory: FactoryPtr, +} + +impl FactoryProvider +{ + pub fn new(factory: FactoryPtr) -> Self + { + Self { factory } + } +} + +impl IProvider for FactoryProvider +{ + fn provide( + &self, + _di_container: &DIContainer, + ) -> error_stack::Result + { + Ok(Providable::Factory(self.factory.clone())) + } +} diff --git a/src/ptr.rs b/src/ptr.rs new file mode 100644 index 0000000..414d086 --- /dev/null +++ b/src/ptr.rs @@ -0,0 +1,6 @@ +#![allow(clippy::module_name_repetitions)] +use std::rc::Rc; + +pub type InterfacePtr = Box; + +pub type FactoryPtr = Rc; diff --git a/syrette/Cargo.toml b/syrette/Cargo.toml deleted file mode 100644 index ab1d551..0000000 --- a/syrette/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "syrette" -version = "0.1.0" -license = "LGPL-2.1" -authors = ["HampusM "] -description = "The convenient dependency injection library" -repository = "https://git.hampusmat.com/syrette" -keywords = ["DI", "dependency-injection", "ioc", "inversion-of-control"] -edition = "2021" - -[dependencies] -syrette_macros = "0.1.0" -linkme = "0.3.0" -once_cell = "1.4" -error-stack = "0.1.1" - -[dev_dependencies] -mockall = "0.11.1" - diff --git a/syrette/README.md b/syrette/README.md deleted file mode 100644 index 74eab4f..0000000 --- a/syrette/README.md +++ /dev/null @@ -1,11 +0,0 @@ -## Syrette - -The convenient dependency injection library for Rust. - -## Namesake - -From the [Syrette Wikipedia article](https://en.wikipedia.org/wiki/Syrette). -> A syrette is a device for injecting liquid through a needle. -> It is similar to a syringe except that it has a closed flexible -> tube (like that typically used for toothpaste) instead of a rigid tube and piston. - diff --git a/syrette/src/castable_factory.rs b/syrette/src/castable_factory.rs deleted file mode 100644 index 5d582bb..0000000 --- a/syrette/src/castable_factory.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::interfaces::factory::IFactory; -use crate::libs::intertrait::CastFrom; -use crate::ptr::InterfacePtr; - -pub trait AnyFactory: CastFrom {} - -pub struct CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ - func: &'static dyn Fn>, -} - -impl CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ - pub fn new( - func: &'static dyn Fn>, - ) -> Self - { - Self { func } - } -} - -impl IFactory - for CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ -} - -impl Fn for CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ - extern "rust-call" fn call(&self, args: Args) -> Self::Output - { - self.func.call(args) - } -} - -impl FnMut for CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ - extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output - { - self.call(args) - } -} - -impl FnOnce for CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ - type Output = InterfacePtr; - - extern "rust-call" fn call_once(self, args: Args) -> Self::Output - { - self.call(args) - } -} - -impl AnyFactory for CastableFactory -where - Args: 'static, - ReturnInterface: 'static + ?Sized, -{ -} diff --git a/syrette/src/di_container.rs b/syrette/src/di_container.rs deleted file mode 100644 index 6982a10..0000000 --- a/syrette/src/di_container.rs +++ /dev/null @@ -1,504 +0,0 @@ -use std::any::{type_name, TypeId}; -use std::collections::HashMap; -use std::marker::PhantomData; -use std::rc::Rc; - -use error_stack::{Report, ResultExt}; - -use crate::castable_factory::CastableFactory; -use crate::errors::di_container::DIContainerError; -use crate::interfaces::factory::IFactory; -use crate::interfaces::injectable::Injectable; -use crate::libs::intertrait::cast_box::CastBox; -use crate::libs::intertrait::cast_rc::CastRc; -use crate::provider::{FactoryProvider, IProvider, InjectableTypeProvider, Providable}; -use crate::ptr::{FactoryPtr, InterfacePtr}; - -/// Binding builder for type `Interface` inside a [`DIContainer`]. -pub struct BindingBuilder<'di_container_lt, Interface> -where - Interface: 'static + ?Sized, -{ - di_container: &'di_container_lt mut DIContainer, - interface_phantom: PhantomData, -} - -impl<'di_container_lt, Interface> BindingBuilder<'di_container_lt, Interface> -where - Interface: 'static + ?Sized, -{ - fn new(di_container: &'di_container_lt mut DIContainer) -> Self - { - Self { - di_container, - interface_phantom: PhantomData, - } - } - - /// Creates a binding of type `Interface` to type `Implementation` inside of the - /// associated [`DIContainer`]. - pub fn to(&mut self) - where - Implementation: Injectable, - { - let interface_typeid = TypeId::of::(); - - self.di_container.bindings.insert( - interface_typeid, - Rc::new(InjectableTypeProvider::::new()), - ); - } - - /// Creates a binding of factory type `Interface` to a factory inside of the - /// associated [`DIContainer`]. - pub fn to_factory( - &mut self, - factory_func: &'static dyn Fn>, - ) where - Args: 'static, - Return: 'static + ?Sized, - Interface: IFactory, - { - let interface_typeid = TypeId::of::(); - - let factory_impl = CastableFactory::new(factory_func); - - self.di_container.bindings.insert( - interface_typeid, - Rc::new(FactoryProvider::new(FactoryPtr::new(factory_impl))), - ); - } -} - -/// Dependency injection container. -/// -/// # Examples -/// ``` -/// use std::collections::HashMap; -/// -/// use syrette::{DIContainer, injectable}; -/// use syrette::errors::di_container::DIContainerError; -/// -/// trait IDatabaseService -/// { -/// fn get_all_records(&self, table_name: String) -> HashMap; -/// } -/// -/// struct DatabaseService {} -/// -/// #[injectable(IDatabaseService)] -/// impl DatabaseService -/// { -/// fn new() -> Self -/// { -/// Self {} -/// } -/// } -/// -/// impl IDatabaseService for DatabaseService -/// { -/// fn get_all_records(&self, table_name: String) -> HashMap -/// { -/// // Do stuff here -/// HashMap::::new() -/// } -/// } -/// -/// fn main() -> error_stack::Result<(), DIContainerError> -/// { -/// let mut di_container = DIContainer::new(); -/// -/// di_container.bind::().to::(); -/// -/// let database_service = di_container.get::()?; -/// -/// Ok(()) -/// } -/// ``` -pub struct DIContainer -{ - bindings: HashMap>, -} - -impl DIContainer -{ - /// Returns a new `DIContainer`. - #[must_use] - pub fn new() -> Self - { - Self { - bindings: HashMap::new(), - } - } - - /// Returns a new [`BindingBuilder`] for the given interface. - pub fn bind(&mut self) -> BindingBuilder - where - Interface: 'static + ?Sized, - { - BindingBuilder::::new(self) - } - - /// Returns a new instance of the type bound with `Interface`. - /// - /// # Errors - /// Will return `Err` if: - /// - No binding for `Interface` exists - /// - Resolving the binding for `Interface` fails - /// - Casting the binding for `Interface` fails - /// - The binding for `Interface` is not injectable - pub fn get( - &self, - ) -> error_stack::Result, DIContainerError> - where - Interface: 'static + ?Sized, - { - let interface_typeid = TypeId::of::(); - - let interface_name = type_name::(); - - let binding = self.bindings.get(&interface_typeid).ok_or_else(|| { - Report::new(DIContainerError) - .attach_printable(format!("No binding exists for {}", interface_name)) - })?; - - let binding_providable = binding - .provide(self) - .change_context(DIContainerError) - .attach_printable(format!( - "Failed to resolve binding for interface {}", - interface_name - ))?; - - match binding_providable { - Providable::Injectable(binding_injectable) => { - let interface_box_result = binding_injectable.cast::(); - - match interface_box_result { - Ok(interface_box) => Ok(interface_box), - Err(_) => Err(Report::new(DIContainerError).attach_printable( - format!("Unable to cast binding for {}", interface_name), - )), - } - } - Providable::Factory(_) => Err(Report::new(DIContainerError) - .attach_printable(format!( - "Binding for {} is not injectable", - interface_name - ))), - } - } - - /// Returns the factory bound with factory type `Interface`. - /// - /// # Errors - /// Will return `Err` if: - /// - No binding for `Interface` exists - /// - Resolving the binding for `Interface` fails - /// - Casting the binding for `Interface` fails - /// - The binding for `Interface` is not a factory - pub fn get_factory( - &self, - ) -> error_stack::Result, DIContainerError> - where - Interface: 'static + ?Sized, - { - let interface_typeid = TypeId::of::(); - - let interface_name = type_name::(); - - let binding = self.bindings.get(&interface_typeid).ok_or_else(|| { - Report::new(DIContainerError) - .attach_printable(format!("No binding exists for {}", interface_name)) - })?; - - let binding_providable = binding - .provide(self) - .change_context(DIContainerError) - .attach_printable(format!( - "Failed to resolve binding for interface {}", - interface_name - ))?; - - match binding_providable { - Providable::Factory(binding_factory) => { - let factory_box_result = binding_factory.cast::(); - - match factory_box_result { - Ok(interface_box) => Ok(interface_box), - Err(_) => Err(Report::new(DIContainerError).attach_printable( - format!("Unable to cast binding for {}", interface_name), - )), - } - } - Providable::Injectable(_) => Err(Report::new(DIContainerError) - .attach_printable(format!( - "Binding for {} is not a factory", - interface_name - ))), - } - } -} - -impl Default for DIContainer -{ - fn default() -> Self - { - Self::new() - } -} - -#[cfg(test)] -mod tests -{ - use mockall::mock; - - use super::*; - use crate::errors::injectable::ResolveError; - - #[test] - fn can_bind_to() - { - trait IUserManager - { - fn add_user(&self, user_id: i128); - - fn remove_user(&self, user_id: i128); - } - - struct UserManager {} - - impl IUserManager for UserManager - { - fn add_user(&self, _user_id: i128) - { - // ... - } - - fn remove_user(&self, _user_id: i128) - { - // ... - } - } - - impl Injectable for UserManager - { - fn resolve( - _di_container: &DIContainer, - ) -> error_stack::Result< - InterfacePtr, - crate::errors::injectable::ResolveError, - > - where - Self: Sized, - { - Ok(InterfacePtr::new(Self {})) - } - } - - let mut di_container: DIContainer = DIContainer::new(); - - assert_eq!(di_container.bindings.len(), 0); - - di_container.bind::().to::(); - - assert_eq!(di_container.bindings.len(), 1); - } - - #[test] - fn can_bind_to_factory() - { - trait IUserManager - { - fn add_user(&self, user_id: i128); - - fn remove_user(&self, user_id: i128); - } - - struct UserManager {} - - impl UserManager - { - fn new() -> Self - { - Self {} - } - } - - impl IUserManager for UserManager - { - fn add_user(&self, _user_id: i128) - { - // ... - } - - fn remove_user(&self, _user_id: i128) - { - // ... - } - } - - type IUserManagerFactory = dyn IFactory<(), dyn IUserManager>; - - let mut di_container: DIContainer = DIContainer::new(); - - assert_eq!(di_container.bindings.len(), 0); - - di_container.bind::().to_factory(&|| { - let user_manager: InterfacePtr = - InterfacePtr::new(UserManager::new()); - - user_manager - }); - - assert_eq!(di_container.bindings.len(), 1); - } - - #[test] - fn can_get() -> error_stack::Result<(), DIContainerError> - { - trait IUserManager - { - fn add_user(&self, user_id: i128); - - fn remove_user(&self, user_id: i128); - } - - struct UserManager {} - - use crate as syrette; - use crate::injectable; - - #[injectable(IUserManager)] - impl UserManager - { - fn new() -> Self - { - Self {} - } - } - - impl IUserManager for UserManager - { - fn add_user(&self, _user_id: i128) - { - // ... - } - - fn remove_user(&self, _user_id: i128) - { - // ... - } - } - - mock! { - Provider {} - - impl IProvider for Provider - { - fn provide( - &self, - di_container: &DIContainer, - ) -> error_stack::Result; - } - } - - let mut di_container: DIContainer = DIContainer::new(); - - let mut mock_provider = MockProvider::new(); - - mock_provider.expect_provide().returning(|_| { - Ok(Providable::Injectable( - InterfacePtr::new(UserManager::new()), - )) - }); - - di_container - .bindings - .insert(TypeId::of::(), Rc::new(mock_provider)); - - di_container.get::()?; - - Ok(()) - } - - #[test] - fn can_get_factory() -> error_stack::Result<(), DIContainerError> - { - trait IUserManager - { - fn add_user(&mut self, user_id: i128); - - fn remove_user(&mut self, user_id: i128); - } - - struct UserManager - { - users: Vec, - } - - impl UserManager - { - fn new(users: Vec) -> Self - { - Self { users } - } - } - - impl IUserManager for UserManager - { - fn add_user(&mut self, user_id: i128) - { - self.users.push(user_id); - } - - fn remove_user(&mut self, user_id: i128) - { - let user_index = - self.users.iter().position(|user| *user == user_id).unwrap(); - - self.users.remove(user_index); - } - } - - use crate as syrette; - - #[crate::factory] - type IUserManagerFactory = dyn IFactory<(Vec,), dyn IUserManager>; - - mock! { - Provider {} - - impl IProvider for Provider - { - fn provide( - &self, - di_container: &DIContainer, - ) -> error_stack::Result; - } - } - - let mut di_container: DIContainer = DIContainer::new(); - - let mut mock_provider = MockProvider::new(); - - mock_provider.expect_provide().returning(|_| { - Ok(Providable::Factory(FactoryPtr::new(CastableFactory::new( - &|users| { - let user_manager: InterfacePtr = - InterfacePtr::new(UserManager::new(users)); - - user_manager - }, - )))) - }); - - di_container - .bindings - .insert(TypeId::of::(), Rc::new(mock_provider)); - - di_container.get_factory::()?; - - Ok(()) - } -} diff --git a/syrette/src/errors/di_container.rs b/syrette/src/errors/di_container.rs deleted file mode 100644 index f2b8add..0000000 --- a/syrette/src/errors/di_container.rs +++ /dev/null @@ -1,17 +0,0 @@ -use std::fmt; -use std::fmt::{Display, Formatter}; - -use error_stack::Context; - -#[derive(Debug)] -pub struct DIContainerError; - -impl Display for DIContainerError -{ - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result - { - fmt.write_str("A DI container error has occurred") - } -} - -impl Context for DIContainerError {} diff --git a/syrette/src/errors/injectable.rs b/syrette/src/errors/injectable.rs deleted file mode 100644 index 6b0cdc5..0000000 --- a/syrette/src/errors/injectable.rs +++ /dev/null @@ -1,17 +0,0 @@ -use core::fmt; -use std::fmt::{Display, Formatter}; - -use error_stack::Context; - -#[derive(Debug)] -pub struct ResolveError; - -impl Display for ResolveError -{ - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result - { - fmt.write_str("Failed to resolve injectable struct") - } -} - -impl Context for ResolveError {} diff --git a/syrette/src/errors/mod.rs b/syrette/src/errors/mod.rs deleted file mode 100644 index b0d50f0..0000000 --- a/syrette/src/errors/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod di_container; -pub mod injectable; diff --git a/syrette/src/interfaces/factory.rs b/syrette/src/interfaces/factory.rs deleted file mode 100644 index c97fc09..0000000 --- a/syrette/src/interfaces/factory.rs +++ /dev/null @@ -1,10 +0,0 @@ -#![allow(clippy::module_name_repetitions)] -use crate::libs::intertrait::CastFrom; -use crate::ptr::InterfacePtr; - -pub trait IFactory: - Fn> + CastFrom -where - ReturnInterface: 'static + ?Sized, -{ -} diff --git a/syrette/src/interfaces/injectable.rs b/syrette/src/interfaces/injectable.rs deleted file mode 100644 index 24032a6..0000000 --- a/syrette/src/interfaces/injectable.rs +++ /dev/null @@ -1,17 +0,0 @@ -use crate::errors::injectable::ResolveError; -use crate::libs::intertrait::CastFrom; -use crate::ptr::InterfacePtr; -use crate::DIContainer; - -pub trait Injectable: CastFrom -{ - /// Resolves the dependencies of the injectable. - /// - /// # Errors - /// Will return `Err` if resolving the dependencies fails. - fn resolve( - di_container: &DIContainer, - ) -> error_stack::Result, ResolveError> - where - Self: Sized; -} diff --git a/syrette/src/interfaces/mod.rs b/syrette/src/interfaces/mod.rs deleted file mode 100644 index 921bb9c..0000000 --- a/syrette/src/interfaces/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod factory; -pub mod injectable; diff --git a/syrette/src/lib.rs b/syrette/src/lib.rs deleted file mode 100644 index 992f276..0000000 --- a/syrette/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -#![feature(unboxed_closures, fn_traits)] -#![deny(clippy::all)] -#![deny(clippy::pedantic)] - -//! Syrette -//! -//! Syrette is a collection of utilities useful for performing dependency injection. - -pub mod castable_factory; -pub mod di_container; -pub mod errors; -pub mod interfaces; -pub mod ptr; - -pub use di_container::*; -pub use syrette_macros::*; - -#[doc(hidden)] -pub mod libs; - -// Private -mod provider; diff --git a/syrette/src/libs/intertrait/LICENSE-APACHE b/syrette/src/libs/intertrait/LICENSE-APACHE deleted file mode 100644 index d9a10c0..0000000 --- a/syrette/src/libs/intertrait/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/syrette/src/libs/intertrait/LICENSE-MIT b/syrette/src/libs/intertrait/LICENSE-MIT deleted file mode 100644 index 969d061..0000000 --- a/syrette/src/libs/intertrait/LICENSE-MIT +++ /dev/null @@ -1,17 +0,0 @@ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/syrette/src/libs/intertrait/cast_box.rs b/syrette/src/libs/intertrait/cast_box.rs deleted file mode 100644 index 793efd0..0000000 --- a/syrette/src/libs/intertrait/cast_box.rs +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -use crate::libs::intertrait::{caster, CastFrom}; - -pub trait CastBox -{ - /// Casts a box to this trait into that of type `T`. If fails, returns the receiver. - fn cast(self: Box) -> Result, Box>; -} - -/// A blanket implementation of `CastBox` for traits extending `CastFrom`. -impl CastBox for S -{ - fn cast(self: Box) -> Result, Box> - { - match caster::((*self).type_id()) { - Some(caster) => Ok((caster.cast_box)(self.box_any())), - None => Err(self), - } - } -} diff --git a/syrette/src/libs/intertrait/cast_rc.rs b/syrette/src/libs/intertrait/cast_rc.rs deleted file mode 100644 index 79d8d60..0000000 --- a/syrette/src/libs/intertrait/cast_rc.rs +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -use std::rc::Rc; - -use crate::libs::intertrait::{caster, CastFrom}; - -pub trait CastRc -{ - /// Casts an `Rc` for this trait into that for type `T`. - fn cast(self: Rc) -> Result, Rc>; -} - -/// A blanket implementation of `CastRc` for traits extending `CastFrom`. -impl CastRc for S -{ - fn cast(self: Rc) -> Result, Rc> - { - match caster::((*self).type_id()) { - Some(caster) => Ok((caster.cast_rc)(self.rc_any())), - None => Err(self), - } - } -} diff --git a/syrette/src/libs/intertrait/hasher.rs b/syrette/src/libs/intertrait/hasher.rs deleted file mode 100644 index e7f110d..0000000 --- a/syrette/src/libs/intertrait/hasher.rs +++ /dev/null @@ -1,48 +0,0 @@ -#![allow(clippy::module_name_repetitions)] - -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -use std::convert::TryInto; -use std::hash::{BuildHasherDefault, Hasher}; -use std::mem::size_of; - -/// A simple `Hasher` implementation tuned for performance. -#[derive(Default)] -pub struct FastHasher(u64); - -/// A `BuildHasher` for `FastHasher`. -pub type BuildFastHasher = BuildHasherDefault; - -impl Hasher for FastHasher -{ - fn finish(&self) -> u64 - { - self.0 - } - - fn write(&mut self, bytes: &[u8]) - { - let mut bytes = bytes; - while bytes.len() > size_of::() { - let (u64_bytes, remaining) = bytes.split_at(size_of::()); - - self.0 ^= u64::from_ne_bytes(u64_bytes.try_into().unwrap()); - - bytes = remaining; - } - self.0 ^= bytes - .iter() - .fold(0u64, |result, b| (result << 8) | u64::from(*b)); - } -} diff --git a/syrette/src/libs/intertrait/mod.rs b/syrette/src/libs/intertrait/mod.rs deleted file mode 100644 index 1daca64..0000000 --- a/syrette/src/libs/intertrait/mod.rs +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -use std::any::{Any, TypeId}; -use std::collections::HashMap; -use std::rc::Rc; -use std::sync::Arc; - -use linkme::distributed_slice; -use once_cell::sync::Lazy; - -mod hasher; - -use hasher::BuildFastHasher; - -pub mod cast_box; -pub mod cast_rc; - -pub type BoxedCaster = Box; - -#[distributed_slice] -pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; - -static CASTER_MAP: Lazy> = - Lazy::new(|| { - CASTERS - .iter() - .map(|f| { - let (type_id, caster) = f(); - ((type_id, (*caster).type_id()), caster) - }) - .collect() - }); - -pub struct Caster -{ - /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait object - /// for trait `T`. - pub cast_box: fn(from: Box) -> Box, - - /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait object - /// for trait `T`. - pub cast_rc: fn(from: Rc) -> Rc, -} - -impl Caster -{ - pub fn new( - cast_box: fn(from: Box) -> Box, - cast_rc: fn(from: Rc) -> Rc, - ) -> Caster - { - Caster:: { cast_box, cast_rc } - } -} - -/// Returns a `Caster` from a concrete type `S` to a trait `T` implemented by it. -fn caster(type_id: TypeId) -> Option<&'static Caster> -{ - CASTER_MAP - .get(&(type_id, TypeId::of::>())) - .and_then(|caster| caster.downcast_ref::>()) -} - -/// `CastFrom` must be extended by a trait that wants to allow for casting into another trait. -/// -/// It is used for obtaining a trait object for [`Any`] from a trait object for its sub-trait, -/// and blanket implemented for all `Sized + Any + 'static` types. -/// -/// # Examples -/// ```ignore -/// trait Source: CastFrom { -/// ... -/// } -/// ``` -pub trait CastFrom: Any + 'static -{ - /// Returns a `Box` of `Any`, which is backed by the type implementing this trait. - fn box_any(self: Box) -> Box; - - /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait. - fn rc_any(self: Rc) -> Rc; -} - -pub trait CastFromSync: CastFrom + Sync + Send + 'static -{ - fn arc_any(self: Arc) -> Arc; -} - -impl CastFrom for T -{ - fn box_any(self: Box) -> Box - { - self - } - - fn rc_any(self: Rc) -> Rc - { - self - } -} - -impl CastFrom for dyn Any + 'static -{ - fn box_any(self: Box) -> Box - { - self - } - - fn rc_any(self: Rc) -> Rc - { - self - } -} - -impl CastFrom for dyn Any + Sync + Send + 'static -{ - fn box_any(self: Box) -> Box - { - self - } - - fn rc_any(self: Rc) -> Rc - { - self - } -} diff --git a/syrette/src/libs/mod.rs b/syrette/src/libs/mod.rs deleted file mode 100644 index 8d5583d..0000000 --- a/syrette/src/libs/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod intertrait; - -pub extern crate linkme; diff --git a/syrette/src/provider.rs b/syrette/src/provider.rs deleted file mode 100644 index 3b7e04c..0000000 --- a/syrette/src/provider.rs +++ /dev/null @@ -1,82 +0,0 @@ -#![allow(clippy::module_name_repetitions)] -use std::marker::PhantomData; - -use crate::castable_factory::AnyFactory; -use crate::errors::injectable::ResolveError; -use crate::interfaces::injectable::Injectable; -use crate::ptr::{FactoryPtr, InterfacePtr}; -use crate::DIContainer; - -extern crate error_stack; - -pub enum Providable -{ - Injectable(InterfacePtr), - Factory(FactoryPtr), -} - -pub trait IProvider -{ - fn provide( - &self, - di_container: &DIContainer, - ) -> error_stack::Result; -} - -pub struct InjectableTypeProvider -where - InjectableType: Injectable, -{ - injectable_phantom: PhantomData, -} - -impl InjectableTypeProvider -where - InjectableType: Injectable, -{ - pub fn new() -> Self - { - Self { - injectable_phantom: PhantomData, - } - } -} - -impl IProvider for InjectableTypeProvider -where - InjectableType: Injectable, -{ - fn provide( - &self, - di_container: &DIContainer, - ) -> error_stack::Result - { - Ok(Providable::Injectable(InjectableType::resolve( - di_container, - )?)) - } -} - -pub struct FactoryProvider -{ - factory: FactoryPtr, -} - -impl FactoryProvider -{ - pub fn new(factory: FactoryPtr) -> Self - { - Self { factory } - } -} - -impl IProvider for FactoryProvider -{ - fn provide( - &self, - _di_container: &DIContainer, - ) -> error_stack::Result - { - Ok(Providable::Factory(self.factory.clone())) - } -} diff --git a/syrette/src/ptr.rs b/syrette/src/ptr.rs deleted file mode 100644 index 414d086..0000000 --- a/syrette/src/ptr.rs +++ /dev/null @@ -1,6 +0,0 @@ -#![allow(clippy::module_name_repetitions)] -use std::rc::Rc; - -pub type InterfacePtr = Box; - -pub type FactoryPtr = Rc; diff --git a/syrette_macros/Cargo.toml b/syrette_macros/Cargo.toml deleted file mode 100644 index 74b4f91..0000000 --- a/syrette_macros/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "syrette_macros" -version = "0.1.0" -license = "LGPL-2.1" -authors = ["HampusM "] -description = "Macros for Syrette, the convenient dependency injection library" -repository = "https://git.hampusmat.com/syrette" -keywords = ["DI", "dependency-injection", "ioc", "inversion-of-control"] -edition = "2021" - -[lib] -proc_macro = true - -[dependencies] -syn = { version = "1.0.96", features = ["full"] } -quote = "1.0.18" -proc-macro2 = "1.0.40" -uuid = { version = "0.8", features = ["v4"] } - diff --git a/syrette_macros/src/declare_interface_args.rs b/syrette_macros/src/declare_interface_args.rs deleted file mode 100644 index b54f458..0000000 --- a/syrette_macros/src/declare_interface_args.rs +++ /dev/null @@ -1,23 +0,0 @@ -use syn::parse::{Parse, ParseStream, Result}; -use syn::{Path, Token, Type}; - -pub struct DeclareInterfaceArgs -{ - pub implementation: Type, - pub interface: Path, -} - -impl Parse for DeclareInterfaceArgs -{ - fn parse(input: ParseStream) -> Result - { - let implementation: Type = input.parse()?; - - input.parse::]>()?; - - Ok(Self { - implementation, - interface: input.parse()?, - }) - } -} diff --git a/syrette_macros/src/factory_type_alias.rs b/syrette_macros/src/factory_type_alias.rs deleted file mode 100644 index 8ea7baa..0000000 --- a/syrette_macros/src/factory_type_alias.rs +++ /dev/null @@ -1,84 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::{GenericArgument, ItemType, Path, Type, TypeParamBound, TypeTuple}; - -pub struct FactoryTypeAlias -{ - pub type_alias: ItemType, - pub factory_interface: Path, - pub arg_types: TypeTuple, - pub return_type: Type, -} - -impl Parse for FactoryTypeAlias -{ - #[allow(clippy::match_wildcard_for_single_variants)] - fn parse(input: ParseStream) -> syn::Result - { - let type_alias = match input.parse::() { - Ok(type_alias) => Ok(type_alias), - Err(_) => Err(input.error("Expected a type alias")), - }?; - - let aliased_trait = match &type_alias.ty.as_ref() { - Type::TraitObject(alias_type) => Ok(alias_type), - &_ => Err(input.error("Expected the aliased type to be a trait")), - }?; - - if aliased_trait.bounds.len() != 1 { - return Err(input.error("Expected the aliased trait to have a single bound.")); - } - - let bound_path = &match aliased_trait.bounds.first().unwrap() { - TypeParamBound::Trait(trait_bound) => Ok(trait_bound), - &_ => { - Err(input.error("Expected the bound of the aliased trait to be a trait")) - } - }? - .path; - - if bound_path.segments.is_empty() - || bound_path.segments.last().unwrap().ident != "IFactory" - { - return Err(input - .error("Expected the bound of the aliased trait to be 'dyn IFactory'")); - } - - let angle_bracketed_args = match &bound_path.segments.last().unwrap().arguments { - syn::PathArguments::AngleBracketed(angle_bracketed_args) => { - Ok(angle_bracketed_args) - } - &_ => { - Err(input.error("Expected angle bracketed arguments for 'dyn IFactory'")) - } - }?; - - let arg_types = match &angle_bracketed_args.args[0] { - GenericArgument::Type(arg_types_type) => match arg_types_type { - Type::Tuple(arg_types) => Ok(arg_types), - &_ => Err(input.error(concat!( - "Expected the first angle bracketed argument ", - "of 'dyn IFactory' to be a type tuple" - ))), - }, - &_ => Err(input.error(concat!( - "Expected the first angle bracketed argument ", - "of 'dyn IFactory' to be a type" - ))), - }?; - - let return_type = match &angle_bracketed_args.args[1] { - GenericArgument::Type(arg_type) => Ok(arg_type), - &_ => Err(input.error(concat!( - "Expected the second angle bracketed argument ", - "of 'dyn IFactory' to be a type" - ))), - }?; - - Ok(Self { - type_alias: type_alias.clone(), - factory_interface: bound_path.clone(), - arg_types: arg_types.clone(), - return_type: return_type.clone(), - }) - } -} diff --git a/syrette_macros/src/injectable_impl.rs b/syrette_macros/src/injectable_impl.rs deleted file mode 100644 index 3000253..0000000 --- a/syrette_macros/src/injectable_impl.rs +++ /dev/null @@ -1,245 +0,0 @@ -use quote::{quote, ToTokens}; -use syn::parse::{Parse, ParseStream}; -use syn::{ - parse_str, punctuated::Punctuated, token::Comma, ExprMethodCall, FnArg, - GenericArgument, Ident, ImplItem, ImplItemMethod, ItemImpl, Path, PathArguments, - Type, TypePath, -}; - -const DI_CONTAINER_VAR_NAME: &str = "di_container"; - -pub struct InjectableImpl -{ - pub dependency_types: Vec, - pub self_type: Type, - pub original_impl: ItemImpl, -} - -impl Parse for InjectableImpl -{ - fn parse(input: ParseStream) -> syn::Result - { - match input.parse::() { - Ok(impl_parsed_input) => { - match Self::_get_dependency_types(&impl_parsed_input) { - Ok(dependency_types) => Ok(Self { - dependency_types, - self_type: impl_parsed_input.self_ty.as_ref().clone(), - original_impl: impl_parsed_input, - }), - Err(error_msg) => Err(input.error(error_msg)), - } - } - Err(_) => Err(input.error("Expected an impl")), - } - } -} - -impl InjectableImpl -{ - pub fn expand(&self) -> proc_macro2::TokenStream - { - let original_impl = &self.original_impl; - let self_type = &self.self_type; - - let di_container_var: Ident = parse_str(DI_CONTAINER_VAR_NAME).unwrap(); - - let get_dependencies = Self::_create_get_dependencies(&self.dependency_types); - - quote! { - #original_impl - - impl syrette::interfaces::injectable::Injectable for #self_type { - fn resolve( - #di_container_var: &syrette::DIContainer - ) -> error_stack::Result< - syrette::ptr::InterfacePtr, - syrette::errors::injectable::ResolveError> - { - use error_stack::ResultExt; - - return Ok(syrette::ptr::InterfacePtr::new(Self::new( - #(#get_dependencies - .change_context(syrette::errors::injectable::ResolveError) - .attach_printable( - format!( - "Unable to resolve a dependency of {}", - std::any::type_name::<#self_type>() - ) - )? - ),* - ))); - } - } - } - } - - fn _create_get_dependencies(dependency_types: &[Type]) -> Vec - { - dependency_types - .iter() - .filter_map(|dep_type| match dep_type { - Type::TraitObject(dep_type_trait) => Some( - parse_str( - format!( - "{}.get::<{}>()", - DI_CONTAINER_VAR_NAME, - dep_type_trait.to_token_stream() - ) - .as_str(), - ) - .unwrap(), - ), - Type::Path(dep_type_path) => { - let dep_type_path_str = Self::_path_to_string(&dep_type_path.path); - - let get_method_name = if dep_type_path_str.ends_with("Factory") { - "get_factory" - } else { - "get" - }; - - Some( - parse_str( - format!( - "{}.{}::<{}>()", - DI_CONTAINER_VAR_NAME, get_method_name, dep_type_path_str - ) - .as_str(), - ) - .unwrap(), - ) - } - &_ => None, - }) - .collect() - } - - fn _find_method_by_name<'impl_lt>( - item_impl: &'impl_lt ItemImpl, - method_name: &'static str, - ) -> Option<&'impl_lt ImplItemMethod> - { - let impl_items = &item_impl.items; - - impl_items - .iter() - .filter_map(|impl_item| match impl_item { - ImplItem::Method(method_item) => Some(method_item), - &_ => None, - }) - .find(|method_item| method_item.sig.ident == method_name) - } - - #[allow(clippy::match_wildcard_for_single_variants)] - fn get_has_fn_args_self(fn_args: &Punctuated) -> bool - { - fn_args.iter().any(|arg| match arg { - FnArg::Receiver(_) => true, - &_ => false, - }) - } - - fn _get_fn_arg_type_paths(fn_args: &Punctuated) -> Vec<&TypePath> - { - fn_args - .iter() - .filter_map(|arg| match arg { - FnArg::Typed(typed_fn_arg) => match typed_fn_arg.ty.as_ref() { - Type::Path(arg_type_path) => Some(arg_type_path), - Type::Reference(ref_type_path) => match ref_type_path.elem.as_ref() { - Type::Path(arg_type_path) => Some(arg_type_path), - &_ => None, - }, - &_ => None, - }, - FnArg::Receiver(_receiver_fn_arg) => None, - }) - .collect() - } - - fn _path_to_string(path: &Path) -> String - { - path.segments - .pairs() - .fold(String::new(), |mut acc, segment_pair| { - let segment_ident = &segment_pair.value().ident; - - acc.push_str(segment_ident.to_string().as_str()); - - let opt_colon_two = segment_pair.punct(); - - match opt_colon_two { - Some(colon_two) => { - acc.push_str(colon_two.to_token_stream().to_string().as_str()); - } - None => {} - } - - acc - }) - } - - fn _is_type_path_ptr(type_path: &TypePath) -> bool - { - let arg_type_path_string = Self::_path_to_string(&type_path.path); - - arg_type_path_string == "InterfacePtr" - || arg_type_path_string == "ptr::InterfacePtr" - || arg_type_path_string == "syrrete::ptr::InterfacePtr" - || arg_type_path_string == "FactoryPtr" - || arg_type_path_string == "ptr::FactoryPtr" - || arg_type_path_string == "syrrete::ptr::FactoryPtr" - } - - fn _get_dependency_types(item_impl: &ItemImpl) -> Result, &'static str> - { - let new_method_impl_item = match Self::_find_method_by_name(item_impl, "new") { - Some(method_item) => Ok(method_item), - None => Err("Missing a 'new' method"), - }?; - - let new_method_args = &new_method_impl_item.sig.inputs; - - if Self::get_has_fn_args_self(new_method_args) { - return Err("Unexpected self argument in 'new' method"); - } - - let new_method_arg_type_paths = Self::_get_fn_arg_type_paths(new_method_args); - - if new_method_arg_type_paths - .iter() - .any(|arg_type_path| !Self::_is_type_path_ptr(arg_type_path)) - { - return Err("All argument types in 'new' method must ptr types"); - } - - Ok(new_method_arg_type_paths - .iter() - .filter_map(|arg_type_path| { - // Assume the type path has a last segment. - let last_path_segment = arg_type_path.path.segments.last().unwrap(); - - match &last_path_segment.arguments { - PathArguments::AngleBracketed(angle_bracketed_generic_args) => { - let generic_args = &angle_bracketed_generic_args.args; - - let opt_first_generic_arg = generic_args.first(); - - // Assume a first generic argument exists because InterfacePtr and - // FactoryPtr requires one - let first_generic_arg = opt_first_generic_arg.as_ref().unwrap(); - - match first_generic_arg { - GenericArgument::Type(first_generic_arg_type) => { - Some(first_generic_arg_type.clone()) - } - &_ => None, - } - } - &_ => None, - } - }) - .collect()) - } -} diff --git a/syrette_macros/src/injectable_macro_args.rs b/syrette_macros/src/injectable_macro_args.rs deleted file mode 100644 index 4ef4389..0000000 --- a/syrette_macros/src/injectable_macro_args.rs +++ /dev/null @@ -1,17 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::TypePath; - -pub struct InjectableMacroArgs -{ - pub interface: TypePath, -} - -impl Parse for InjectableMacroArgs -{ - fn parse(input: ParseStream) -> syn::Result - { - Ok(Self { - interface: input.parse()?, - }) - } -} diff --git a/syrette_macros/src/lib.rs b/syrette_macros/src/lib.rs deleted file mode 100644 index ed1a509..0000000 --- a/syrette_macros/src/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -#![deny(clippy::all)] -#![deny(clippy::pedantic)] - -use proc_macro::TokenStream; -use quote::quote; -use syn::{parse, parse_macro_input}; - -mod declare_interface_args; -mod factory_type_alias; -mod injectable_impl; -mod injectable_macro_args; -mod libs; - -use declare_interface_args::DeclareInterfaceArgs; -use factory_type_alias::FactoryTypeAlias; -use injectable_impl::InjectableImpl; -use injectable_macro_args::InjectableMacroArgs; -use libs::intertrait_macros::gen_caster::generate_caster; - -/// Makes a struct injectable. Thereby usable with `DIContainer`. -/// -/// # Arguments -/// * A interface trait the struct implements. -/// -/// # Panics -/// If the attributed item is not a impl. -#[proc_macro_attribute] -pub fn injectable(args_stream: TokenStream, impl_stream: TokenStream) -> TokenStream -{ - let InjectableMacroArgs { - interface: interface_type_path, - } = parse_macro_input!(args_stream); - - let injectable_impl: InjectableImpl = parse(impl_stream).unwrap(); - - let expanded_injectable_impl = injectable_impl.expand(); - - let self_type = &injectable_impl.self_type; - - quote! { - #expanded_injectable_impl - - syrette::declare_interface!(#self_type -> #interface_type_path); - } - .into() -} - -/// Makes a type alias usable as a factory interface. -/// -/// # Panics -/// If the attributed item is not a type alias. -#[proc_macro_attribute] -pub fn factory(_: TokenStream, type_alias_stream: TokenStream) -> TokenStream -{ - let FactoryTypeAlias { - type_alias, - factory_interface, - arg_types, - return_type, - } = parse(type_alias_stream).unwrap(); - - quote! { - #type_alias - - syrette::declare_interface!( - syrette::castable_factory::CastableFactory< - #arg_types, - #return_type - > -> #factory_interface - ); - - syrette::declare_interface!( - syrette::castable_factory::CastableFactory< - #arg_types, - #return_type - > -> syrette::castable_factory::AnyFactory - ); - } - .into() -} - -/// Declares the interface trait of a implementation. -/// -/// # Arguments -/// {Implementation} -> {Interface} -/// -#[proc_macro] -pub fn declare_interface(input: TokenStream) -> TokenStream -{ - let DeclareInterfaceArgs { - implementation, - interface, - } = parse_macro_input!(input); - - generate_caster(&implementation, &interface).into() -} diff --git a/syrette_macros/src/libs/intertrait_macros/LICENSE-APACHE b/syrette_macros/src/libs/intertrait_macros/LICENSE-APACHE deleted file mode 100644 index d9a10c0..0000000 --- a/syrette_macros/src/libs/intertrait_macros/LICENSE-APACHE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS diff --git a/syrette_macros/src/libs/intertrait_macros/LICENSE-MIT b/syrette_macros/src/libs/intertrait_macros/LICENSE-MIT deleted file mode 100644 index 969d061..0000000 --- a/syrette_macros/src/libs/intertrait_macros/LICENSE-MIT +++ /dev/null @@ -1,17 +0,0 @@ -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/syrette_macros/src/libs/intertrait_macros/gen_caster.rs b/syrette_macros/src/libs/intertrait_macros/gen_caster.rs deleted file mode 100644 index 9126200..0000000 --- a/syrette_macros/src/libs/intertrait_macros/gen_caster.rs +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -use std::str::from_utf8_unchecked; - -use proc_macro2::TokenStream; -use quote::format_ident; -use quote::quote; -use quote::ToTokens; -use uuid::adapter::Simple; -use uuid::Uuid; - -pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens) -> TokenStream -{ - let mut fn_buf = [0u8; FN_BUF_LEN]; - - let fn_ident = format_ident!("{}", new_fn_name(&mut fn_buf)); - - let new_caster = quote! { - syrette::libs::intertrait::Caster::::new( - |from| from.downcast::<#ty>().unwrap(), - |from| from.downcast::<#ty>().unwrap(), - ) - }; - - quote! { - #[syrette::libs::linkme::distributed_slice(syrette::libs::intertrait::CASTERS)] - #[linkme(crate = syrette::libs::linkme)] - fn #fn_ident() -> (::std::any::TypeId, syrette::libs::intertrait::BoxedCaster) { - (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) - } - } -} - -const FN_PREFIX: &[u8] = b"__"; -const FN_BUF_LEN: usize = FN_PREFIX.len() + Simple::LENGTH; - -fn new_fn_name(buf: &mut [u8]) -> &str -{ - buf[..FN_PREFIX.len()].copy_from_slice(FN_PREFIX); - Uuid::new_v4() - .to_simple() - .encode_lower(&mut buf[FN_PREFIX.len()..]); - unsafe { from_utf8_unchecked(&buf[..FN_BUF_LEN]) } -} diff --git a/syrette_macros/src/libs/intertrait_macros/mod.rs b/syrette_macros/src/libs/intertrait_macros/mod.rs deleted file mode 100644 index fa8c021..0000000 --- a/syrette_macros/src/libs/intertrait_macros/mod.rs +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Originally from Intertrait by CodeChain - * - * - * - * - * Licensed under either of - * - * Apache License, Version 2.0 (LICENSE-APACHE or ) - * MIT license (LICENSE-MIT or ) - - * at your option. -*/ -pub mod gen_caster; diff --git a/syrette_macros/src/libs/mod.rs b/syrette_macros/src/libs/mod.rs deleted file mode 100644 index e4964de..0000000 --- a/syrette_macros/src/libs/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod intertrait_macros; -- cgit v1.2.3-18-g5258