From 607d8d50b76665b67ca6f3124aa81773bec110b7 Mon Sep 17 00:00:00 2001 From: HampusM Date: Mon, 25 Dec 2023 22:53:13 +0100 Subject: WIP Linkme dependency removal --- Cargo.lock | 21 -- Cargo.toml | 1 - macros/src/caster.rs | 158 --------------- macros/src/declare_interface_args.rs | 177 ----------------- macros/src/injectable/implementation.rs | 105 +++++++++- macros/src/injectable/macro_args.rs | 11 +- macros/src/lib.rs | 126 +----------- macros/src/util/syn_path.rs | 12 +- src/di_container/asynchronous/mod.rs | 50 ++--- src/di_container/blocking/mod.rs | 32 +-- src/interfaces/async_injectable.rs | 30 ++- src/interfaces/injectable.rs | 30 ++- src/lib.rs | 13 +- src/private/cast/arc.rs | 93 --------- src/private/cast/boxed.rs | 87 -------- src/private/cast/error.rs | 20 -- src/private/cast/mod.rs | 273 ------------------------- src/private/cast/rc.rs | 87 -------- src/private/mod.rs | 6 - src/ptr_buffer.rs | 341 ++++++++++++++++++++++++++++++++ src/test_utils.rs | 106 ++++++++-- 21 files changed, 645 insertions(+), 1134 deletions(-) delete mode 100644 macros/src/caster.rs delete mode 100644 macros/src/declare_interface_args.rs delete mode 100644 src/private/cast/arc.rs delete mode 100644 src/private/cast/boxed.rs delete mode 100644 src/private/cast/error.rs delete mode 100644 src/private/cast/mod.rs delete mode 100644 src/private/cast/rc.rs delete mode 100644 src/private/mod.rs create mode 100644 src/ptr_buffer.rs diff --git a/Cargo.lock b/Cargo.lock index 64e414a..08c447c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -179,26 +179,6 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" -[[package]] -name = "linkme" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6c7bb4dcb6a456ffd750601c389889a58daff6857125b75ee6e5edabf6388c8" -dependencies = [ - "linkme-impl", -] - -[[package]] -name = "linkme-impl" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d5adefdb2c04ca9173223070228ff26a04667003619257b8442f192d9986218" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - [[package]] name = "log" version = "0.4.19" @@ -501,7 +481,6 @@ dependencies = [ "ahash", "anyhow", "async-trait", - "linkme", "mockall", "once_cell", "paste", diff --git a/Cargo.toml b/Cargo.toml index 439fcae..7159157 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,6 @@ required-features = ["async", "factory"] [dependencies] syrette_macros = { path = "./macros", version = "0.5.1" } -linkme = "0.3.0" once_cell = "1.4" ahash = "0.7.6" thiserror = "1.0.32" diff --git a/macros/src/caster.rs b/macros/src/caster.rs deleted file mode 100644 index 417a881..0000000 --- a/macros/src/caster.rs +++ /dev/null @@ -1,158 +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; - -use proc_macro2::{Ident, TokenStream}; -use quote::{format_ident, quote, ToTokens}; -use uuid::adapter::Simple; -use uuid::Uuid; - -#[cfg(syrette_macros_logging)] -use crate::util::tokens::ToTokensExt; - -const CASTER_FN_NAME_PREFIX: &[u8] = b"__"; - -const FN_BUF_LEN: usize = CASTER_FN_NAME_PREFIX.len() + Simple::LENGTH; - -#[cfg_attr(syrette_macros_logging, tracing::instrument(skip(ty, dst_trait)))] -pub fn generate_caster( - ty: &impl ToTokens, - dst_trait: &impl ToTokens, - sync: bool, -) -> TokenStream -{ - #[cfg(syrette_macros_logging)] - tracing::debug!( - source = %ty.to_str_pretty(), - destination = %ty.to_str_pretty(), - "Generating caster", - ); - - let fn_ident = create_caster_fn_ident(Uuid::new_v4()); - - let new_caster = if sync { - quote! { - syrette::private::cast::Caster::<#dst_trait>::new_sync( - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastBoxFailed)?; - - Ok(concrete as Box<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastRcFailed)?; - - Ok(concrete as std::rc::Rc<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastArcFailed)?; - - Ok(concrete as std::sync::Arc<#dst_trait>) - }, - ) - } - } else { - quote! { - syrette::private::cast::Caster::<#dst_trait>::new( - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastBoxFailed)?; - - Ok(concrete as Box<#dst_trait>) - }, - |from| { - let concrete = from - .downcast::<#ty>() - .map_err(|_| syrette::private::cast::CasterError::CastRcFailed)?; - - Ok(concrete as std::rc::Rc<#dst_trait>) - }, - ) - } - }; - - quote! { - #[syrette::private::linkme::distributed_slice(syrette::private::cast::CASTERS)] - #[linkme(crate = syrette::private::linkme)] - fn #fn_ident() -> (::std::any::TypeId, syrette::private::cast::BoxedCaster) { - (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) - } - } -} - -fn create_caster_fn_ident(uuid: impl IUuid) -> Ident -{ - let buf = &mut [0u8; FN_BUF_LEN]; - - buf[..CASTER_FN_NAME_PREFIX.len()].copy_from_slice(CASTER_FN_NAME_PREFIX); - - uuid.encode_simple_lower_into(&mut buf[CASTER_FN_NAME_PREFIX.len()..]); - - let fn_name = - from_utf8(&buf[..FN_BUF_LEN]).expect("Created caster function name is not UTF-8"); - - format_ident!("{}", fn_name) -} - -/// Simple interface for `Uuid`. -/// -/// Created for ease of testing the [`create_caster_fn_ident`] function. -/// -/// [`Uuid`]: uuid::Uuid -#[cfg_attr(test, mockall::automock)] -trait IUuid -{ - /// Writes the Uuid as a simple lower-case string to `buf`. - fn encode_simple_lower_into(self, buf: &mut [u8]); -} - -impl IUuid for Uuid -{ - fn encode_simple_lower_into(self, buf: &mut [u8]) - { - self.to_simple().encode_lower(buf); - } -} - -#[cfg(test)] -mod tests -{ - use pretty_assertions::assert_eq; - - use super::*; - - #[test] - fn can_create_caster_fn_ident() - { - let mut uuid_mock = MockIUuid::new(); - - uuid_mock - .expect_encode_simple_lower_into() - .return_once(|buf| { - buf[..FN_BUF_LEN - 2].fill(b'f'); - }) - .once(); - - assert_eq!( - create_caster_fn_ident(uuid_mock), - format_ident!("__{}", "f".repeat(FN_BUF_LEN - 2)) - ); - } -} diff --git a/macros/src/declare_interface_args.rs b/macros/src/declare_interface_args.rs deleted file mode 100644 index 6556153..0000000 --- a/macros/src/declare_interface_args.rs +++ /dev/null @@ -1,177 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; -use syn::{Token, TypePath}; - -use crate::macro_flag::MacroFlag; -use crate::util::iterator_ext::IteratorExt; - -pub const DECLARE_INTERFACE_FLAGS: &[&str] = &["threadsafe_sharable"]; - -pub struct DeclareInterfaceArgs -{ - pub implementation: TypePath, - pub interface: TypePath, - pub flags: Punctuated, -} - -impl Parse for DeclareInterfaceArgs -{ - fn parse(input: ParseStream) -> syn::Result - { - let implementation: TypePath = input.parse()?; - - input.parse::]>()?; - - let interface: TypePath = input.parse()?; - - let flags = if input.peek(Token![,]) { - input.parse::()?; - - let flags = Punctuated::::parse_terminated(input)?; - - for flag in &flags { - let flag_name = flag.name().to_string(); - - if !DECLARE_INTERFACE_FLAGS.contains(&flag_name.as_str()) { - return Err(input.error(format!( - "Unknown flag '{flag_name}'. Expected one of [ {} ]", - DECLARE_INTERFACE_FLAGS.join(",") - ))); - } - } - - if let Some((dupe_flag, _)) = flags.iter().find_duplicate() { - return Err(input.error(format!("Duplicate flag '{}'", dupe_flag.name()))); - } - - flags - } else { - Punctuated::new() - }; - - Ok(Self { - implementation, - interface, - flags, - }) - } -} - -#[cfg(test)] -mod tests -{ - use proc_macro2::Span; - use quote::{format_ident, quote}; - use syn::{parse2, Lit, LitBool}; - - use super::*; - use crate::macro_flag::MacroFlagValue; - use crate::test_utils; - - #[test] - fn can_parse_with_no_flags() - { - let input_args = quote! { - Foo -> IFoo - }; - - let decl_interface_args = parse2::(input_args).unwrap(); - - assert_eq!( - decl_interface_args.implementation, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("Foo"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.interface, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("IFoo"), - &[] - )]) - } - ); - - assert!(decl_interface_args.flags.is_empty()); - } - - #[test] - fn can_parse_with_flags() - { - let input_args = quote! { - Foobar -> IFoobar, threadsafe_sharable = true - }; - - let decl_interface_args = parse2::(input_args).unwrap(); - - assert_eq!( - decl_interface_args.implementation, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("Foobar"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.interface, - TypePath { - qself: None, - path: test_utils::create_path(&[test_utils::create_path_segment( - format_ident!("IFoobar"), - &[] - )]) - } - ); - - assert_eq!( - decl_interface_args.flags, - Punctuated::from_iter(vec![MacroFlag { - name: format_ident!("threadsafe_sharable"), - value: MacroFlagValue::Literal(Lit::Bool(LitBool::new( - true, - Span::call_site() - ))) - }]) - ); - } - - #[test] - fn cannot_parse_with_invalid_flag() - { - let input_args = quote! { - Foobar -> IFoobar, xyz = false, threadsafe_sharable = true - }; - - assert!(parse2::(input_args).is_err()); - } - - #[test] - fn cannot_parse_with_duplicate_flag() - { - assert!( - // Formatting is weird without this comment - parse2::(quote! { - Foobar -> IFoobar, threadsafe_sharable = true, threadsafe_sharable = true - }) - .is_err() - ); - - assert!( - // Formatting is weird without this comment - parse2::(quote! { - Foobar -> IFoobar, threadsafe_sharable = true, threadsafe_sharable = false - }) - .is_err() - ); - } -} diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs index a67b4a3..a361567 100644 --- a/macros/src/injectable/implementation.rs +++ b/macros/src/injectable/implementation.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Ident, Span}; +use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, ToTokens}; use syn::spanned::Spanned; use syn::{ @@ -12,8 +12,10 @@ use syn::{ ImplItemMethod, ItemImpl, MethodTurbofish, + Path, ReturnType, Type, + TypePath, }; use crate::injectable::dependency::DependencyError; @@ -148,14 +150,13 @@ impl InjectableImpl Ok(()) } - pub fn self_type(&self) -> &Type - { - &self.original_impl.self_ty - } - #[cfg(not(tarpaulin_include))] - pub fn expand(&self, no_doc_hidden: bool, is_async: bool) - -> proc_macro2::TokenStream + pub fn expand( + &self, + no_doc_hidden: bool, + is_async: bool, + interface_trait: Option<&TypePath>, + ) -> proc_macro2::TokenStream { let di_container_var = format_ident!("{}", DI_CONTAINER_VAR_NAME); let dependency_history_var = format_ident!("{}", DEPENDENCY_HISTORY_VAR_NAME); @@ -186,6 +187,7 @@ impl InjectableImpl &dependency_history_var, &maybe_prevent_circular_deps, &get_dep_method_calls, + interface_trait, ) } else { self.expand_blocking_impl( @@ -194,6 +196,7 @@ impl InjectableImpl &dependency_history_var, &maybe_prevent_circular_deps, &get_dep_method_calls, + interface_trait, ) }; @@ -232,12 +235,31 @@ impl InjectableImpl dependency_history_var: &Ident, maybe_prevent_circular_deps: &proc_macro2::TokenStream, get_dep_method_calls: &Vec, + interface_trait: Option<&TypePath>, ) -> proc_macro2::TokenStream { let generics = &self.original_impl.generics; let self_type = &self.original_impl.self_ty; let constructor = &self.constructor_method.sig.ident; + let into_ptr_buf_fns = &[ + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Box"), + Path::new_empty(), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Rc"), + syn_path!(std::rc), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Arc"), + syn_path!(std::sync), + ), + ] as &[TokenStream]; + let dependency_idents = (0..get_dep_method_calls.len()) .map(|index| format_ident!("dependency_{index}")) .collect::>(); @@ -289,6 +311,8 @@ impl InjectableImpl )#maybe_await_constructor)) }) } + + #(#into_ptr_buf_fns)* } } } @@ -301,12 +325,31 @@ impl InjectableImpl dependency_history_var: &Ident, maybe_prevent_circular_deps: &proc_macro2::TokenStream, get_dep_method_calls: &Vec, + interface_trait: Option<&TypePath>, ) -> proc_macro2::TokenStream { let generics = &self.original_impl.generics; let self_type = &self.original_impl.self_ty; let constructor = &self.constructor_method.sig.ident; + let into_ptr_buf_fns = &[ + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Box"), + Path::new_empty(), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Rc"), + syn_path!(std::rc), + ), + Self::create_to_ptr_buf_fn( + interface_trait, + &format_ident!("Arc"), + syn_path!(std::sync), + ), + ] as &[TokenStream]; + quote! { #maybe_doc_hidden impl #generics syrette::interfaces::injectable::Injectable< @@ -332,6 +375,52 @@ impl InjectableImpl #(#get_dep_method_calls),* ))); } + + #(#into_ptr_buf_fns)* + } + } + } + + fn create_to_ptr_buf_fn( + interface_trait: Option<&TypePath>, + smart_ptr_ident: &Ident, + smart_ptr_parent_path: Path, + ) -> TokenStream + { + let ptr_buf_path = quote! { syrette::ptr_buffer::PtrBuffer }; + + let mut smart_ptr = smart_ptr_parent_path; + + smart_ptr.segments.push(smart_ptr_ident.clone().into()); + + let body = match interface_trait { + Some(interface_trait) => { + quote! { + let me: #smart_ptr = self; + + #ptr_buf_path::new_from( + syrette::ptr_buffer::SmartPtr::#smart_ptr_ident(me) + ) + } + } + None => { + quote! { + #ptr_buf_path::new_from( + syrette::ptr_buffer::SmartPtr::#smart_ptr_ident(self) + ) + } + } + }; + + let into_ptr_buf_fn = format_ident!( + "into_ptr_buffer_{}", + smart_ptr_ident.to_string().to_lowercase() + ); + + quote! { + fn #into_ptr_buf_fn(self: #smart_ptr) -> #ptr_buf_path + { + #body } } } diff --git a/macros/src/injectable/macro_args.rs b/macros/src/injectable/macro_args.rs index 719d551..f0469f7 100644 --- a/macros/src/injectable/macro_args.rs +++ b/macros/src/injectable/macro_args.rs @@ -7,12 +7,7 @@ use crate::macro_flag::MacroFlag; use crate::util::error::diagnostic_error_enum; use crate::util::iterator_ext::IteratorExt; -pub const INJECTABLE_MACRO_FLAGS: &[&str] = &[ - "no_doc_hidden", - "async", - "no_declare_concrete_interface", - "constructor", -]; +pub const INJECTABLE_MACRO_FLAGS: &[&str] = &["no_doc_hidden", "async", "constructor"]; pub struct InjectableMacroArgs { @@ -194,7 +189,7 @@ mod tests fn can_parse_with_flags_only() { let input_args = quote! { - async = false, no_declare_concrete_interface = false + async = false, no_doc_hidden = false }; let injectable_macro_args = parse2::(input_args).unwrap(); @@ -212,7 +207,7 @@ mod tests ))) }, MacroFlag { - name: format_ident!("no_declare_concrete_interface"), + name: format_ident!("no_doc_hidden"), value: MacroFlagValue::Literal(Lit::Bool(LitBool::new( false, Span::call_site() diff --git a/macros/src/lib.rs b/macros/src/lib.rs index b9b6ea5..3d60de7 100644 --- a/macros/src/lib.rs +++ b/macros/src/lib.rs @@ -9,27 +9,13 @@ use proc_macro::TokenStream; use proc_macro_error::{proc_macro_error, set_dummy, ResultExt}; use quote::{format_ident, quote}; -use syn::punctuated::Punctuated; -use syn::token::Dyn; -use syn::{ - parse, - ItemImpl, - TraitBound, - TraitBoundModifier, - Type, - TypeParamBound, - TypeTraitObject, -}; +use syn::{parse, ItemImpl}; -use crate::caster::generate_caster; -use crate::declare_interface_args::DeclareInterfaceArgs; use crate::injectable::dummy::expand_dummy_blocking_impl; use crate::injectable::implementation::{InjectableImpl, InjectableImplError}; use crate::injectable::macro_args::InjectableMacroArgs; use crate::macro_flag::MacroFlag; -mod caster; -mod declare_interface_args; mod injectable; mod macro_flag; mod util; @@ -57,12 +43,6 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); /// **Default:** `false`
/// Don't hide the impl of the [`Injectable`] trait from documentation. /// -/// #### `no_declare_concrete_interface` -/// **Value:** boolean literal
-/// **Default:** `false`
-/// Disable declaring the concrete type as the interface when no interface trait argument -/// is given. -/// /// #### `async` /// Available on crate feature async only. /// @@ -79,10 +59,7 @@ const PACKAGE_VERSION: &str = env!("CARGO_PKG_VERSION"); /// Constructor method name. /// /// # Important -/// When no interface trait argument is given, you have three options -/// - Manually declare the interface with the [`declare_interface!`] macro. -/// - Use the [`di_container_bind`] macro to create a DI container binding. -/// - Use the concrete type as the interface. +/// When no interface trait argument is given, the concrete type is used as a interface. /// /// # Examples /// ``` @@ -216,13 +193,6 @@ pub fn injectable(args_stream: TokenStream, input_stream: TokenStream) -> TokenS .map_or(Ok(false), MacroFlag::get_bool) .unwrap_or_abort(); - let no_declare_concrete_interface = args - .flags - .iter() - .find(|flag| flag.name() == "no_declare_concrete_interface") - .map_or(Ok(false), MacroFlag::get_bool) - .unwrap_or_abort(); - let constructor = args .flags .iter() @@ -258,103 +228,15 @@ pub fn injectable(args_stream: TokenStream, input_stream: TokenStream) -> TokenS injectable_impl.validate(is_async).unwrap_or_abort(); - let expanded_injectable_impl = injectable_impl.expand(no_doc_hidden, is_async); - - let self_type = injectable_impl.self_type(); - - let opt_interface = args.interface.map(Type::Path).or_else(|| { - if no_declare_concrete_interface { - None - } else { - Some(self_type.clone()) - } - }); - - let maybe_decl_interface = if let Some(interface) = opt_interface { - let threadsafe_sharable_flag = if is_async { - quote! { , threadsafe_sharable = true } - } else { - quote! {} - }; - - quote! { - syrette::declare_interface!( - #self_type -> #interface #threadsafe_sharable_flag - ); - } - } else { - quote! {} - }; + let expanded_injectable_impl = + injectable_impl.expand(no_doc_hidden, is_async, args.interface.as_ref()); quote! { #expanded_injectable_impl - - #maybe_decl_interface } .into() } -/// Declares the interface trait of a implementation. -/// -/// # Arguments -/// {Implementation} -> {Interface} -/// * (Zero or more) Flags. Like `a = true, b = false` -/// -/// # Flags -/// - `threadsafe_sharable` - Enables the use of thread-safe shared instances of the -/// implementation accessed with the interface. -/// -/// # Examples -/// ``` -/// # use syrette::declare_interface; -/// # -/// # trait INinja {} -/// # -/// # struct Ninja {} -/// # -/// # impl INinja for Ninja {} -/// # -/// declare_interface!(Ninja -> INinja); -/// ``` -#[cfg(not(tarpaulin_include))] -#[proc_macro_error] -#[proc_macro] -pub fn declare_interface(input: TokenStream) -> TokenStream -{ - let DeclareInterfaceArgs { - implementation, - interface, - flags, - } = parse(input).unwrap_or_abort(); - - let threadsafe_sharable_flag = flags - .iter() - .find(|flag| flag.name() == "threadsafe_sharable"); - - let is_async = threadsafe_sharable_flag - .map_or_else(|| Ok(false), MacroFlag::get_bool) - .unwrap_or_abort(); - - #[cfg(syrette_macros_logging)] - init_logging(); - - let interface_type = if interface == implementation { - Type::Path(interface) - } else { - Type::TraitObject(TypeTraitObject { - dyn_token: Some(Dyn::default()), - bounds: Punctuated::from_iter(vec![TypeParamBound::Trait(TraitBound { - paren_token: None, - modifier: TraitBoundModifier::None, - lifetimes: None, - path: interface.path, - })]), - }) - }; - - generate_caster(&implementation, &interface_type, is_async).into() -} - /// Used to declare the name of a dependency in the constructor of a impl block decorated /// with [`macro@injectable`]. /// diff --git a/macros/src/util/syn_path.rs b/macros/src/util/syn_path.rs index 26e2597..6efea01 100644 --- a/macros/src/util/syn_path.rs +++ b/macros/src/util/syn_path.rs @@ -1,16 +1,26 @@ use std::fmt::Write; use quote::ToTokens; -use syn::punctuated::Pair; +use syn::punctuated::{Pair, Punctuated}; pub trait SynPathExt { + fn new_empty() -> Self; + /// Converts the [`syn::Path`] to a [`String`]. fn to_string(&self) -> String; } impl SynPathExt for syn::Path { + fn new_empty() -> Self + { + Self { + leading_colon: None, + segments: Punctuated::new(), + } + } + fn to_string(&self) -> String { self.segments.pairs().map(Pair::into_tuple).fold( diff --git a/src/di_container/asynchronous/mod.rs b/src/di_container/asynchronous/mod.rs index 3e29ef6..e5fd1fd 100644 --- a/src/di_container/asynchronous/mod.rs +++ b/src/di_container/asynchronous/mod.rs @@ -55,9 +55,6 @@ use crate::di_container::asynchronous::binding::builder::AsyncBindingBuilder; use crate::di_container::binding_storage::DIContainerBindingStorage; use crate::di_container::BindingOptions; use crate::errors::async_di_container::AsyncDIContainerError; -use crate::private::cast::arc::CastArc; -use crate::private::cast::boxed::CastBox; -use crate::private::cast::error::CastError; use crate::provider::r#async::{AsyncProvidable, IAsyncProvider}; use crate::ptr::SomePtr; use crate::util::use_double; @@ -313,38 +310,27 @@ impl AsyncDIContainer Interface: 'static + ?Sized + Send + Sync, { match binding_providable { - AsyncProvidable::Transient(transient_binding) => Ok(SomePtr::Transient( - transient_binding.cast::().map_err(|_| { - AsyncDIContainerError::CastFailed { + AsyncProvidable::Transient(transient_binding) => { + let ptr_buf = transient_binding.into_ptr_buffer_box(); + + ptr_buf + .cast_into_boxed::() + .ok_or_else(|| AsyncDIContainerError::CastFailed { interface: type_name::(), binding_kind: "transient", - } - })?, - )), + }) + .map(SomePtr::Transient) + } AsyncProvidable::Singleton(singleton_binding) => { - Ok(SomePtr::ThreadsafeSingleton( - singleton_binding - .cast::() - .map_err(|err| match err { - CastError::NotArcCastable(_) => { - AsyncDIContainerError::InterfaceNotAsync(type_name::< - Interface, - >( - )) - } - CastError::CastFailed { - source: _, - from: _, - to: _, - } - | CastError::GetCasterFailed(_) => { - AsyncDIContainerError::CastFailed { - interface: type_name::(), - binding_kind: "singleton", - } - } - })?, - )) + let ptr_buf = singleton_binding.into_ptr_buffer_arc(); + + ptr_buf + .cast_into_arc::() + .ok_or_else(|| AsyncDIContainerError::CastFailed { + interface: type_name::(), + binding_kind: "singleton", + }) + .map(SomePtr::ThreadsafeSingleton) } #[cfg(feature = "factory")] AsyncProvidable::Factory(factory_binding) => { diff --git a/src/di_container/blocking/mod.rs b/src/di_container/blocking/mod.rs index d9efe94..7286f48 100644 --- a/src/di_container/blocking/mod.rs +++ b/src/di_container/blocking/mod.rs @@ -55,8 +55,6 @@ use crate::di_container::binding_storage::DIContainerBindingStorage; use crate::di_container::blocking::binding::builder::BindingBuilder; use crate::di_container::BindingOptions; use crate::errors::di_container::DIContainerError; -use crate::private::cast::boxed::CastBox; -use crate::private::cast::rc::CastRc; use crate::provider::blocking::{IProvider, Providable}; use crate::ptr::SomePtr; use crate::util::use_double; @@ -267,22 +265,28 @@ impl DIContainer .get_binding_providable::(binding_options, dependency_history)?; match binding_providable { - Providable::Transient(transient_binding) => Ok(SomePtr::Transient( - transient_binding.cast::().map_err(|_| { - DIContainerError::CastFailed { + Providable::Transient(transient_binding) => { + let ptr_buf = transient_binding.into_ptr_buffer_box(); + + ptr_buf + .cast_into_boxed::() + .ok_or_else(|| DIContainerError::CastFailed { interface: type_name::(), binding_kind: "transient", - } - })?, - )), - Providable::Singleton(singleton_binding) => Ok(SomePtr::Singleton( - singleton_binding.cast::().map_err(|_| { - DIContainerError::CastFailed { + }) + .map(SomePtr::Transient) + } + Providable::Singleton(singleton_binding) => { + let ptr_buf = singleton_binding.into_ptr_buffer_rc(); + + ptr_buf + .cast_into_rc::() + .ok_or_else(|| DIContainerError::CastFailed { interface: type_name::(), binding_kind: "singleton", - } - })?, - )), + }) + .map(SomePtr::Singleton) + } #[cfg(feature = "factory")] Providable::Factory(factory_binding) => { use crate::castable_factory::CastableFactory; diff --git a/src/interfaces/async_injectable.rs b/src/interfaces/async_injectable.rs index 3600bac..cf903e7 100644 --- a/src/interfaces/async_injectable.rs +++ b/src/interfaces/async_injectable.rs @@ -1,17 +1,19 @@ //! Interface for structs that can be injected into or be injected to. use std::fmt::Debug; use std::future::ready; +use std::rc::Rc; +use std::sync::Arc; use crate::errors::injectable::InjectableError; use crate::future::BoxFuture; -use crate::private::cast::CastFromArc; use crate::ptr::TransientPtr; +use crate::ptr_buffer::PtrBuffer; use crate::util::use_double; use_double!(crate::dependency_history::DependencyHistory); /// Interface for structs that can be injected into or be injected to. -pub trait AsyncInjectable: CastFromArc +pub trait AsyncInjectable: 'static + Send + Sync { /// Resolves the dependencies of the injectable. /// @@ -24,6 +26,15 @@ pub trait AsyncInjectable: CastFromArc where Self: Sized + 'fut, 'di_container: 'fut; + + /// A. + fn into_ptr_buffer_box(self: Box) -> PtrBuffer; + + /// A. + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer; + + /// A. + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer; } impl Debug for dyn AsyncInjectable @@ -48,4 +59,19 @@ where { Box::pin(ready(Ok(TransientPtr::new(Self::default())))) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + PtrBuffer::new_from(self) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + PtrBuffer::new_from(self) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + PtrBuffer::new_from(self) + } } diff --git a/src/interfaces/injectable.rs b/src/interfaces/injectable.rs index 1d3a1a7..eb0839c 100644 --- a/src/interfaces/injectable.rs +++ b/src/interfaces/injectable.rs @@ -1,15 +1,17 @@ //! Interface for structs that can be injected into or be injected to. use std::fmt::Debug; +use std::rc::Rc; +use std::sync::Arc; use crate::errors::injectable::InjectableError; -use crate::private::cast::CastFrom; use crate::ptr::TransientPtr; +use crate::ptr_buffer::{PtrBuffer, SmartPtr}; use crate::util::use_double; use_double!(crate::dependency_history::DependencyHistory); /// Interface for structs that can be injected into or be injected to. -pub trait Injectable: CastFrom +pub trait Injectable: 'static { /// Resolves the dependencies of the injectable. /// @@ -21,6 +23,15 @@ pub trait Injectable: CastFrom ) -> Result, InjectableError> where Self: Sized; + + /// A. + fn into_ptr_buffer_box(self: Box) -> PtrBuffer; + + /// A. + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer; + + /// A. + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer; } impl Debug for dyn Injectable @@ -42,4 +53,19 @@ where { Ok(TransientPtr::new(Self::default())) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + PtrBuffer::new_from(SmartPtr::Box(self)) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + PtrBuffer::new_from(SmartPtr::Rc(self)) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + PtrBuffer::new_from(SmartPtr::Arc(self)) + } } diff --git a/src/lib.rs b/src/lib.rs index d93acc8..f9923f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,6 +93,7 @@ pub mod di_container; pub mod errors; pub mod interfaces; pub mod ptr; +pub mod ptr_buffer; #[cfg(feature = "async")] #[cfg_attr(doc_cfg, doc(cfg(feature = "async")))] @@ -102,10 +103,7 @@ pub mod future; #[cfg_attr(doc_cfg, doc(cfg(feature = "async")))] pub use di_container::asynchronous::AsyncDIContainer; pub use di_container::blocking::DIContainer; -pub use syrette_macros::{declare_interface, injectable, named}; - -#[doc(hidden)] -pub mod private; +pub use syrette_macros::{injectable, named}; mod provider; mod util; @@ -157,8 +155,9 @@ mod test_utils; #[macro_export] macro_rules! di_container_bind { ($interface: path => $implementation: ty, $di_container: ident) => { - $di_container.bind::().to::<$implementation>().unwrap(); - - syrette::declare_interface!($implementation -> $interface); + $di_container + .bind::() + .to::<$implementation>() + .unwrap(); }; } diff --git a/src/private/cast/arc.rs b/src/private/cast/arc.rs deleted file mode 100644 index 1fbdf8b..0000000 --- a/src/private/cast/arc.rs +++ /dev/null @@ -1,93 +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::type_name; -use std::sync::Arc; - -use crate::private::cast::error::CastError; -use crate::private::cast::{get_caster, CastFromArc}; - -pub trait CastArc -{ - /// Casts an `Arc` with `Self` into an `Arc` with `Dest`. - fn cast(self: Arc) -> Result, CastError>; -} - -/// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and -/// `Send`. -impl CastArc for CastFromSelf -{ - fn cast(self: Arc) -> Result, CastError> - { - let caster = - get_caster::((*self).type_id()).map_err(CastError::GetCasterFailed)?; - - let cast_arc = caster - .opt_cast_arc - .ok_or(CastError::NotArcCastable(type_name::()))?; - - cast_arc(self.arc_any()).map_err(|err| CastError::CastFailed { - source: err, - from: type_name::(), - to: type_name::(), - }) - } -} - -#[cfg(test)] -mod tests -{ - use std::any::Any; - use std::fmt::{Debug, Display}; - use std::sync::Arc; - - use super::*; - use crate::test_utils::subjects; - - #[test] - fn can_cast_arc() - { - let concrete_ninja = Arc::new(subjects::Ninja); - - let abstract_ninja: Arc = concrete_ninja; - - let debug_ninja_result = abstract_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } - - #[test] - fn cannot_cast_arc_wrong() - { - let concrete_ninja = Arc::new(subjects::Ninja); - - let abstract_ninja: Arc = concrete_ninja; - - let display_ninja_result = abstract_ninja.cast::(); - - assert!(matches!( - display_ninja_result, - Err(CastError::GetCasterFailed(_)) - )); - } - - #[test] - fn can_cast_arc_from_any() - { - let concrete_ninja = Arc::new(subjects::Ninja); - - let any_ninja: Arc = concrete_ninja; - - let debug_ninja_result = any_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } -} diff --git a/src/private/cast/boxed.rs b/src/private/cast/boxed.rs deleted file mode 100644 index 074346c..0000000 --- a/src/private/cast/boxed.rs +++ /dev/null @@ -1,87 +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::type_name; - -use crate::private::cast::error::CastError; -use crate::private::cast::{get_caster, CastFrom}; - -pub trait CastBox -{ - /// Casts a `Box` with `Self` into a `Box` with `Dest`. - fn cast(self: Box) -> Result, CastError>; -} - -/// A blanket implementation of `CastBox` for traits extending `CastFrom`. -impl CastBox for CastFromSelf -{ - fn cast(self: Box) -> Result, CastError> - { - let caster = - get_caster::((*self).type_id()).map_err(CastError::GetCasterFailed)?; - - (caster.cast_box)(self.box_any()).map_err(|err| CastError::CastFailed { - source: err, - from: type_name::(), - to: type_name::(), - }) - } -} - -#[cfg(test)] -mod tests -{ - use std::any::Any; - use std::fmt::{Debug, Display}; - - use super::*; - use crate::test_utils::subjects; - - #[test] - fn can_cast_box() - { - let concrete_ninja = Box::new(subjects::Ninja); - - let abstract_ninja: Box = concrete_ninja; - - let debug_ninja_result = abstract_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } - - #[test] - fn cannot_cast_box_wrong() - { - let concrete_ninja = Box::new(subjects::Ninja); - - let abstract_ninja: Box = concrete_ninja; - - let display_ninja_result = abstract_ninja.cast::(); - - assert!(matches!( - display_ninja_result, - Err(CastError::GetCasterFailed(_)) - )); - } - - #[test] - fn can_cast_box_from_any() - { - let concrete_ninja = Box::new(subjects::Ninja); - - let any_ninja: Box = concrete_ninja; - - let debug_ninja_result = any_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } -} diff --git a/src/private/cast/error.rs b/src/private/cast/error.rs deleted file mode 100644 index c6ed01d..0000000 --- a/src/private/cast/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use crate::private::cast::{CasterError, GetCasterError}; - -#[derive(thiserror::Error, Debug)] -pub enum CastError -{ - #[error("Failed to get caster")] - GetCasterFailed(#[from] GetCasterError), - - #[error("Failed to cast from {from} to {to}")] - CastFailed - { - #[source] - source: CasterError, - from: &'static str, - to: &'static str, - }, - - #[error("'{0}' can't be cast to an Arc")] - NotArcCastable(&'static str), -} diff --git a/src/private/cast/mod.rs b/src/private/cast/mod.rs deleted file mode 100644 index ddff2a4..0000000 --- a/src/private/cast/mod.rs +++ /dev/null @@ -1,273 +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::rc::Rc; -use std::sync::Arc; - -use ahash::AHashMap; -use linkme::distributed_slice; -use once_cell::sync::Lazy; - -pub mod arc; -pub mod boxed; -pub mod error; -pub mod rc; - -pub type BoxedCaster = Box; - -/// A distributed slice gathering constructor functions for [`Caster`]s. -/// -/// A constructor function returns `TypeId` of a concrete type involved in the casting -/// and a `Box` of a type or trait backed by a [`Caster`]. -#[distributed_slice] -pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; - -/// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. -static CASTER_MAP: Lazy> = Lazy::new(|| { - CASTERS - .iter() - .map(|caster_fn| { - let (type_id, caster) = caster_fn(); - - ((type_id, (*caster).type_id()), caster) - }) - .collect() -}); - -type CastBoxFn = fn(from: Box) -> Result, CasterError>; - -type CastRcFn = fn(from: Rc) -> Result, CasterError>; - -type CastArcFn = - fn(from: Arc) -> Result, CasterError>; - -/// A `Caster` knows how to cast a type or trait to the type or trait `Dest`. Each -/// `Caster` instance is specific to a concrete type. That is, it knows how to cast to -/// single specific type or trait implemented by single specific type. -pub struct Caster -{ - /// Casts a `Box` holding a type or trait object for `Any` to another `Box` holding a - /// type or trait `Dest`. - pub cast_box: CastBoxFn, - - /// Casts an `Rc` holding a type or trait for `Any` to another `Rc` holding a type or - /// trait `Dest`. - pub cast_rc: CastRcFn, - - /// Casts an `Arc` holding a type or trait for `Any + Sync + Send + 'static` to - /// another `Arc` holding a type or trait for `Dest`. - pub opt_cast_arc: Option>, -} - -impl Caster -{ - pub fn new(cast_box: CastBoxFn, cast_rc: CastRcFn) -> Caster - { - Caster:: { - cast_box, - cast_rc, - opt_cast_arc: None, - } - } - - #[allow(clippy::similar_names)] - pub fn new_sync( - cast_box: CastBoxFn, - cast_rc: CastRcFn, - cast_arc: CastArcFn, - ) -> Caster - { - Caster:: { - cast_box, - cast_rc, - opt_cast_arc: Some(cast_arc), - } - } -} - -#[derive(Debug, thiserror::Error)] -pub enum CasterError -{ - #[error("Failed to cast Box")] - CastBoxFailed, - - #[error("Failed to cast Rc")] - CastRcFailed, - - #[error("Failed to cast Arc")] - CastArcFailed, -} - -/// Returns a `Caster` from a concrete type with the id `type_id` to a type or trait -/// `Dest`. -fn get_caster( - type_id: TypeId, -) -> Result<&'static Caster, GetCasterError> -{ - let any_caster = CASTER_MAP - .get(&(type_id, TypeId::of::>())) - .ok_or(GetCasterError::NotFound)?; - - any_caster - .downcast_ref::>() - .ok_or(GetCasterError::DowncastFailed) -} - -#[derive(Debug, thiserror::Error)] -pub enum GetCasterError -{ - #[error("Caster not found")] - NotFound, - - #[error("Failed to downcast caster")] - DowncastFailed, -} - -/// `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; -} - -/// This trait must be extended by a trait that is `Any + Sync + Send + 'static` -/// and wants to allow for casting into another trait behind references and smart pointers -/// especially including `Arc`. -/// -/// It is used for obtaining a trait object for [`Any + Sync + Send + 'static`] from an -/// object for its sub-trait, and blanket implemented for all `Sized + Sync + Send + -/// 'static` types. -/// -/// # Examples -/// ```ignore -/// trait Source: CastFromArc { -/// ... -/// } -/// ``` -pub trait CastFromArc: CastFrom + Sync + Send + 'static -{ - fn arc_any(self: Arc) -> Arc; -} - -impl CastFrom for Source -{ - 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 CastFromArc for Source -{ - fn arc_any(self: Arc) -> Arc - { - self - } -} - -impl CastFrom for dyn Any + Sync + Send + 'static -{ - fn box_any(self: Box) -> Box - { - self - } - - fn rc_any(self: Rc) -> Rc - { - self - } -} - -impl CastFromArc for dyn Any + Sync + Send + 'static -{ - fn arc_any(self: Arc) -> Arc - { - self - } -} - -#[cfg(test)] -mod tests -{ - use std::any::TypeId; - use std::fmt::Debug; - - use linkme::distributed_slice; - - use super::*; - use crate::test_utils::subjects; - - #[distributed_slice(super::CASTERS)] - static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster; - - fn create_test_caster() -> (TypeId, BoxedCaster) - { - let type_id = TypeId::of::(); - - let caster = Box::new(Caster:: { - cast_box: |from| { - let concrete = from - .downcast::() - .map_err(|_| CasterError::CastBoxFailed)?; - - Ok(concrete as Box) - }, - cast_rc: |from| { - let concrete = from - .downcast::() - .map_err(|_| CasterError::CastRcFailed)?; - - Ok(concrete as Rc) - }, - opt_cast_arc: Some(|from| { - let concrete = from - .downcast::() - .map_err(|_| CasterError::CastArcFailed)?; - - Ok(concrete as Arc) - }), - }); - (type_id, caster) - } -} diff --git a/src/private/cast/rc.rs b/src/private/cast/rc.rs deleted file mode 100644 index 11d137a..0000000 --- a/src/private/cast/rc.rs +++ /dev/null @@ -1,87 +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::type_name; -use std::rc::Rc; - -use crate::private::cast::error::CastError; -use crate::private::cast::{get_caster, CastFrom}; - -pub trait CastRc -{ - /// Casts an `Rc` with `Self `into a `Rc` with `Dest`. - fn cast(self: Rc) -> Result, CastError>; -} - -/// A blanket implementation of `CastRc` for traits extending `CastFrom`. -impl CastRc for CastFromSelf -{ - fn cast(self: Rc) -> Result, CastError> - { - let caster = - get_caster::((*self).type_id()).map_err(CastError::GetCasterFailed)?; - - (caster.cast_rc)(self.rc_any()).map_err(|err| CastError::CastFailed { - source: err, - from: type_name::(), - to: type_name::(), - }) - } -} - -#[cfg(test)] -mod tests -{ - use std::any::Any; - use std::fmt::{Debug, Display}; - - use super::*; - use crate::test_utils::subjects; - - #[test] - fn can_cast_rc() - { - let concrete_ninja = Rc::new(subjects::Ninja); - - let abstract_ninja: Rc = concrete_ninja; - - let debug_ninja_result = abstract_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } - - #[test] - fn cannot_cast_rc_wrong() - { - let concrete_ninja = Rc::new(subjects::Ninja); - - let abstract_ninja: Rc = concrete_ninja; - - let display_ninja_result = abstract_ninja.cast::(); - - assert!(matches!( - display_ninja_result, - Err(CastError::GetCasterFailed(_)) - )); - } - - #[test] - fn can_cast_rc_from_any() - { - let concrete_ninja = Rc::new(subjects::Ninja); - - let any_ninja: Rc = concrete_ninja; - - let debug_ninja_result = any_ninja.cast::(); - - assert!(debug_ninja_result.is_ok()); - } -} diff --git a/src/private/mod.rs b/src/private/mod.rs deleted file mode 100644 index 9b03ce8..0000000 --- a/src/private/mod.rs +++ /dev/null @@ -1,6 +0,0 @@ -//! This module contains items that's not in the public API but is used by the -//! library user with the expansions of the macros in the syrette_macros crate. - -pub mod cast; - -pub extern crate linkme; diff --git a/src/ptr_buffer.rs b/src/ptr_buffer.rs new file mode 100644 index 0000000..e1e2fc6 --- /dev/null +++ b/src/ptr_buffer.rs @@ -0,0 +1,341 @@ +//! Pointer buffer; + +use std::any::TypeId; +use std::mem::{size_of, MaybeUninit}; +use std::ptr::addr_of; +use std::rc::Rc; +use std::sync::Arc; + +/// Pointer buffer; +pub struct PtrBuffer +{ + buf: Box<[MaybeUninit]>, + type_id: TypeId, + kind: Kind, +} + +impl PtrBuffer +{ + /// AA. + #[must_use] + pub fn new_from(value: ValuePtr) -> Self + where + Value: ?Sized + 'static, + ValuePtr: Into>, + { + let value = value.into(); + + let kind = value.kind(); + + let buf = ptr_to_byte_buf(value.into_raw()); + + Self { + buf, + type_id: TypeId::of::(), + kind, + } + } + + pub(crate) fn cast_into_boxed(self) -> Option> + where + Dest: ?Sized + 'static, + { + if !matches!(self.kind, Kind::Box) { + return None; + } + + let dest_ptr = self.cast_into()?; + + // SAFETY: We know the pointer was retrieved using Box::into_raw in the + // new_from function since the kind is Kind::Box (checked above). We + // also know it was the exact same pointed to type since this is checked in the + // cast_into function + Some(unsafe { Box::from_raw(dest_ptr) }) + } + + pub(crate) fn cast_into_rc(self) -> Option> + where + Dest: ?Sized + 'static, + { + if !matches!(self.kind, Kind::Rc) { + return None; + } + + let dest_ptr = self.cast_into()?; + + // SAFETY: We know the pointer was retrieved using Rc::into_raw in the + // new_from function since the kind is Kind::Rc (checked above). We + // also know it was the exact same pointed to type since this is checked in the + // cast_into function + Some(unsafe { Rc::from_raw(dest_ptr) }) + } + + #[cfg(feature = "async")] + pub(crate) fn cast_into_arc(self) -> Option> + where + Dest: ?Sized + 'static, + { + if !matches!(self.kind, Kind::Arc) { + return None; + } + + let dest_ptr = self.cast_into()?; + + // SAFETY: We know the pointer was retrieved using Arc::into_raw in the + // new_from function since the kind is Kind::Arc (checked above). We + // also know it was the exact same pointed to type since this is checked in the + // cast_into function + Some(unsafe { Arc::from_raw(dest_ptr) }) + } + + fn cast_into(self) -> Option<*mut Dest> + where + Dest: ?Sized + 'static, + { + if TypeId::of::() != self.type_id { + return None; + } + + if size_of::<*mut Dest>() != self.buf.len() { + // Pointer kinds are different so continuing would cause UB. This should + // not be possible since the type IDs are the same but we check it just to + // be extra safe + return None; + } + + let mut ptr = MaybeUninit::<*mut Dest>::uninit(); + + // SAFETY: + // - We know the source buffer is valid for reads the number of bytes since it is + // ensured by the array primitive + // - We know the destination is valid for writes the number of bytes since we + // check above if the buffer length is the same as the size of *mut Dest + unsafe { + std::ptr::copy_nonoverlapping( + self.buf.as_ptr().cast::(), + ptr.as_mut_ptr().cast::(), + self.buf.len(), + ); + } + + // SAFETY: We initialize the value above by copying the buffer to it + Some(unsafe { ptr.assume_init() }) + } +} + +/// Smart pointers supported as input to [`PtrBuffer`]. +#[derive(Debug)] +#[non_exhaustive] +pub enum SmartPtr +{ + /// Box. + Box(Box), + + /// Rc. + Rc(Rc), + + /// Arc. + Arc(Arc), +} + +impl SmartPtr +{ + fn into_raw(self) -> *const Value + { + match self { + Self::Box(value) => Box::into_raw(value), + Self::Rc(value) => Rc::into_raw(value), + Self::Arc(value) => Arc::into_raw(value), + } + } + + fn kind(&self) -> Kind + { + match self { + Self::Box(_) => Kind::Box, + Self::Rc(_) => Kind::Rc, + Self::Arc(_) => Kind::Arc, + } + } +} + +impl From> for SmartPtr +where + Value: ?Sized + 'static, +{ + fn from(value: Box) -> Self + { + Self::Box(value) + } +} + +impl From> for SmartPtr +where + Value: ?Sized + 'static, +{ + fn from(value: Rc) -> Self + { + Self::Rc(value) + } +} + +impl From> for SmartPtr +where + Value: ?Sized + 'static, +{ + fn from(value: Arc) -> Self + { + Self::Arc(value) + } +} + +enum Kind +{ + Box, + Rc, + Arc, +} + +fn ptr_to_byte_buf(value_ptr: *const Value) -> Box<[MaybeUninit]> +where + Value: ?Sized + 'static, +{ + // Transform the full pointer (data pointer + (optional) metadata) into a byte + // slice + let value_ptr_bytes = unsafe { + std::slice::from_raw_parts::( + addr_of!(value_ptr).cast::(), + size_of::<*const Value>(), + ) + }; + + value_ptr_bytes + .iter() + .map(|byte| MaybeUninit::new(*byte)) + .collect::>() + .into() +} + +#[cfg(test)] +mod tests +{ + use std::mem::{size_of, transmute, MaybeUninit}; + use std::path::PathBuf; + use std::rc::Rc; + + use crate::ptr_buffer::{ptr_to_byte_buf, PtrBuffer}; + + trait Anything + { + fn get_value(&self) -> u32; + } + + struct Something; + + impl Anything for Something + { + fn get_value(&self) -> u32 + { + 1234 + } + } + + #[test] + fn works_with_thin() + { + let text = Box::new("Hello there".to_string()); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf + .cast_into_boxed::() + .map_or_else(|| false, |text| *text == "Hello there")); + } + + #[test] + fn works_with_dyn() + { + let text: Box = Box::new(Something); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf + .cast_into_boxed::() + .map_or_else(|| false, |anything| anything.get_value() == 1234)); + } + + #[test] + fn cast_box_when_wrong_kind_fails() + { + let text = Rc::new("Hello there".to_string()); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf.cast_into_boxed::().is_none()); + } + + #[test] + fn cast_rc_when_wrong_kind_fails() + { + let text = Box::new("Hello there".to_string()); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf.cast_into_rc::().is_none()); + } + + #[test] + #[cfg(feature = "async")] + fn cast_arc_when_wrong_kind_fails() + { + let text = Box::new("Hello there".to_string()); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf.cast_into_arc::().is_none()); + } + + #[test] + fn cast_into_fails_when_wrong_type() + { + let text = Box::new(123_456u64); + + let ptr_buf = PtrBuffer::new_from(text); + + assert!(ptr_buf.cast_into::().is_none()); + } + + #[test] + fn ptr_to_byte_buf_works() + { + let thin_ptr_addr = 123_456_789usize; + + assert_eq!( + unsafe { + slice_assume_init_ref(&ptr_to_byte_buf(thin_ptr_addr as *const u32)) + }, + thin_ptr_addr.to_ne_bytes() + ); + + let fat_ptr_addr_buf: [u8; size_of::<*const dyn Send>()] = + [26, 88, 91, 77, 2, 0, 0, 0, 12, 34, 56, 78, 90, 9, 98, 87]; + + let fat_ptr_addr: *const dyn Send = unsafe { transmute(fat_ptr_addr_buf) }; + + assert_eq!( + unsafe { slice_assume_init_ref(&ptr_to_byte_buf(fat_ptr_addr)) }, + &fat_ptr_addr_buf + ); + } + + /// TODO: Remove when `MaybeUninit::slice_assume_init_ref` is stabilized + const unsafe fn slice_assume_init_ref(slice: &[MaybeUninit]) -> &[T] + { + // SAFETY: casting `slice` to a `*const [T]` is safe since the caller guarantees + // that `slice` is initialized, and `MaybeUninit` is guaranteed to have + // the same layout as `T`. The pointer obtained is valid since it refers + // to memory owned by `slice` which is a reference and thus guaranteed to + // be valid for reads. + unsafe { &*(slice as *const [MaybeUninit] as *const [T]) } + } +} diff --git a/src/test_utils.rs b/src/test_utils.rs index 491e9b4..f0858b5 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -3,11 +3,10 @@ pub mod subjects //! Test subjects. use std::fmt::Debug; - - use syrette_macros::declare_interface; + use std::rc::Rc; + use std::sync::Arc; use crate::interfaces::injectable::Injectable; - use crate::private::cast::CastFromArc; use crate::ptr::TransientPtr; use_double!(crate::dependency_history::DependencyHistory); @@ -42,11 +41,9 @@ pub mod subjects } } - use crate as syrette; + use crate::ptr_buffer::PtrBuffer; use crate::util::use_double; - declare_interface!(UserManager -> IUserManager); - impl Injectable for UserManager { fn resolve( @@ -58,6 +55,27 @@ pub mod subjects { Ok(TransientPtr::new(Self::new())) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + let me: Box = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + let me: Rc = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + let me: Arc = self; + + PtrBuffer::new_from(me) + } } pub trait INumber @@ -109,8 +127,6 @@ pub mod subjects } } - declare_interface!(Number -> INumber); - impl Injectable for Number { fn resolve( @@ -122,12 +138,33 @@ pub mod subjects { Ok(TransientPtr::new(Self::new())) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + let me: Box = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + let me: Rc = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + let me: Arc = self; + + PtrBuffer::new_from(me) + } } #[derive(Debug)] pub struct Ninja; - pub trait INinja: CastFromArc {} + pub trait INinja {} impl INinja for Ninja {} } @@ -138,9 +175,10 @@ pub mod subjects_async //! Test subjects. use std::fmt::Debug; + use std::rc::Rc; + use std::sync::Arc; use async_trait::async_trait; - use syrette_macros::declare_interface; use crate::interfaces::async_injectable::AsyncInjectable; use crate::ptr::TransientPtr; @@ -177,11 +215,9 @@ pub mod subjects_async } } - use crate as syrette; + use crate::ptr_buffer::PtrBuffer; use crate::util::use_double; - declare_interface!(UserManager -> IUserManager); - #[async_trait] impl AsyncInjectable for UserManager { @@ -194,6 +230,27 @@ pub mod subjects_async { Ok(TransientPtr::new(Self::new())) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + let me: Box = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + let me: Rc = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + let me: Arc = self; + + PtrBuffer::new_from(me) + } } pub trait INumber: Send + Sync @@ -245,8 +302,6 @@ pub mod subjects_async } } - declare_interface!(Number -> INumber, threadsafe_sharable = true); - #[async_trait] impl AsyncInjectable for Number { @@ -259,6 +314,27 @@ pub mod subjects_async { Ok(TransientPtr::new(Self::new())) } + + fn into_ptr_buffer_box(self: Box) -> PtrBuffer + { + let me: Box = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_rc(self: Rc) -> PtrBuffer + { + let me: Rc = self; + + PtrBuffer::new_from(me) + } + + fn into_ptr_buffer_arc(self: Arc) -> PtrBuffer + { + let me: Arc = self; + + PtrBuffer::new_from(me) + } } } -- cgit v1.2.3-18-g5258