aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock21
-rw-r--r--Cargo.toml1
-rw-r--r--macros/src/caster.rs158
-rw-r--r--macros/src/declare_interface_args.rs177
-rw-r--r--macros/src/injectable/implementation.rs105
-rw-r--r--macros/src/injectable/macro_args.rs11
-rw-r--r--macros/src/lib.rs126
-rw-r--r--macros/src/util/syn_path.rs12
-rw-r--r--src/di_container/asynchronous/mod.rs50
-rw-r--r--src/di_container/blocking/mod.rs32
-rw-r--r--src/interfaces/async_injectable.rs30
-rw-r--r--src/interfaces/injectable.rs30
-rw-r--r--src/lib.rs13
-rw-r--r--src/private/cast/arc.rs93
-rw-r--r--src/private/cast/boxed.rs87
-rw-r--r--src/private/cast/error.rs20
-rw-r--r--src/private/cast/mod.rs273
-rw-r--r--src/private/cast/rc.rs87
-rw-r--r--src/private/mod.rs6
-rw-r--r--src/ptr_buffer.rs341
-rw-r--r--src/test_utils.rs106
21 files changed, 645 insertions, 1134 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 64e414a..08c447c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -180,26 +180,6 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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
- *
- * <https://github.com/CodeChain-io/intertrait>
- * <https://crates.io/crates/intertrait/0.2.2>
- *
- * Licensed under either of
- *
- * Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- * MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-
- * 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<MacroFlag, Token![,]>,
-}
-
-impl Parse for DeclareInterfaceArgs
-{
- fn parse(input: ParseStream) -> syn::Result<Self>
- {
- let implementation: TypePath = input.parse()?;
-
- input.parse::<Token![->]>()?;
-
- let interface: TypePath = input.parse()?;
-
- let flags = if input.peek(Token![,]) {
- input.parse::<Token![,]>()?;
-
- let flags = Punctuated::<MacroFlag, Token![,]>::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::<DeclareInterfaceArgs>(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::<DeclareInterfaceArgs>(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::<DeclareInterfaceArgs>(input_args).is_err());
- }
-
- #[test]
- fn cannot_parse_with_duplicate_flag()
- {
- assert!(
- // Formatting is weird without this comment
- parse2::<DeclareInterfaceArgs>(quote! {
- Foobar -> IFoobar, threadsafe_sharable = true, threadsafe_sharable = true
- })
- .is_err()
- );
-
- assert!(
- // Formatting is weird without this comment
- parse2::<DeclareInterfaceArgs>(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<proc_macro2::TokenStream>,
+ 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::<Vec<_>>();
@@ -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<proc_macro2::TokenStream>,
+ 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<dyn #interface_trait> = 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<Self>) -> #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::<InjectableMacroArgs>(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`<br>
/// Don't hide the impl of the [`Injectable`] trait from documentation.
///
-/// #### `no_declare_concrete_interface`
-/// **Value:** boolean literal<br>
-/// **Default:** `false`<br>
-/// Disable declaring the concrete type as the interface when no interface trait argument
-/// is given.
-///
/// #### `async`
/// <span class="cf">Available on <strong>crate feature <code>async</code></strong> only.
/// </span>
@@ -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::<Interface>().map_err(|_| {
- AsyncDIContainerError::CastFailed {
+ AsyncProvidable::Transient(transient_binding) => {
+ let ptr_buf = transient_binding.into_ptr_buffer_box();
+
+ ptr_buf
+ .cast_into_boxed::<Interface>()
+ .ok_or_else(|| AsyncDIContainerError::CastFailed {
interface: type_name::<Interface>(),
binding_kind: "transient",
- }
- })?,
- )),
+ })
+ .map(SomePtr::Transient)
+ }
AsyncProvidable::Singleton(singleton_binding) => {
- Ok(SomePtr::ThreadsafeSingleton(
- singleton_binding
- .cast::<Interface>()
- .map_err(|err| match err {
- CastError::NotArcCastable(_) => {
- AsyncDIContainerError::InterfaceNotAsync(type_name::<
- Interface,
- >(
- ))
- }
- CastError::CastFailed {
- source: _,
- from: _,
- to: _,
- }
- | CastError::GetCasterFailed(_) => {
- AsyncDIContainerError::CastFailed {
- interface: type_name::<Interface>(),
- binding_kind: "singleton",
- }
- }
- })?,
- ))
+ let ptr_buf = singleton_binding.into_ptr_buffer_arc();
+
+ ptr_buf
+ .cast_into_arc::<Interface>()
+ .ok_or_else(|| AsyncDIContainerError::CastFailed {
+ interface: type_name::<Interface>(),
+ 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::<Interface>(binding_options, dependency_history)?;
match binding_providable {
- Providable::Transient(transient_binding) => Ok(SomePtr::Transient(
- transient_binding.cast::<Interface>().map_err(|_| {
- DIContainerError::CastFailed {
+ Providable::Transient(transient_binding) => {
+ let ptr_buf = transient_binding.into_ptr_buffer_box();
+
+ ptr_buf
+ .cast_into_boxed::<Interface>()
+ .ok_or_else(|| DIContainerError::CastFailed {
interface: type_name::<Interface>(),
binding_kind: "transient",
- }
- })?,
- )),
- Providable::Singleton(singleton_binding) => Ok(SomePtr::Singleton(
- singleton_binding.cast::<Interface>().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::<Interface>()
+ .ok_or_else(|| DIContainerError::CastFailed {
interface: type_name::<Interface>(),
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<DIContainerT>: CastFromArc
+pub trait AsyncInjectable<DIContainerT>: 'static + Send + Sync
{
/// Resolves the dependencies of the injectable.
///
@@ -24,6 +26,15 @@ pub trait AsyncInjectable<DIContainerT>: CastFromArc
where
Self: Sized + 'fut,
'di_container: 'fut;
+
+ /// A.
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer;
+
+ /// A.
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer;
+
+ /// A.
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer;
}
impl<DIContainerT> Debug for dyn AsyncInjectable<DIContainerT>
@@ -48,4 +59,19 @@ where
{
Box::pin(ready(Ok(TransientPtr::new(Self::default()))))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ PtrBuffer::new_from(self)
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ PtrBuffer::new_from(self)
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> 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<DIContainerT>: CastFrom
+pub trait Injectable<DIContainerT>: 'static
{
/// Resolves the dependencies of the injectable.
///
@@ -21,6 +23,15 @@ pub trait Injectable<DIContainerT>: CastFrom
) -> Result<TransientPtr<Self>, InjectableError>
where
Self: Sized;
+
+ /// A.
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer;
+
+ /// A.
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer;
+
+ /// A.
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer;
}
impl<DIContainerT> Debug for dyn Injectable<DIContainerT>
@@ -42,4 +53,19 @@ where
{
Ok(TransientPtr::new(Self::default()))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ PtrBuffer::new_from(SmartPtr::Box(self))
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ PtrBuffer::new_from(SmartPtr::Rc(self))
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> 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::<dyn $interface>().to::<$implementation>().unwrap();
-
- syrette::declare_interface!($implementation -> $interface);
+ $di_container
+ .bind::<dyn $interface>()
+ .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
-//!
-//! <https://github.com/CodeChain-io/intertrait>
-//! <https://crates.io/crates/intertrait/0.2.2>
-//!
-//! Licensed under either of
-//!
-//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
-//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-//!
-//! 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<Dest: ?Sized + 'static>(self: Arc<Self>) -> Result<Arc<Dest>, CastError>;
-}
-
-/// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and
-/// `Send`.
-impl<CastFromSelf: ?Sized + CastFromArc> CastArc for CastFromSelf
-{
- fn cast<Dest: ?Sized + 'static>(self: Arc<Self>) -> Result<Arc<Dest>, CastError>
- {
- let caster =
- get_caster::<Dest>((*self).type_id()).map_err(CastError::GetCasterFailed)?;
-
- let cast_arc = caster
- .opt_cast_arc
- .ok_or(CastError::NotArcCastable(type_name::<Dest>()))?;
-
- cast_arc(self.arc_any()).map_err(|err| CastError::CastFailed {
- source: err,
- from: type_name::<Self>(),
- to: type_name::<Dest>(),
- })
- }
-}
-
-#[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<dyn subjects::INinja> = concrete_ninja;
-
- let debug_ninja_result = abstract_ninja.cast::<dyn Debug>();
-
- assert!(debug_ninja_result.is_ok());
- }
-
- #[test]
- fn cannot_cast_arc_wrong()
- {
- let concrete_ninja = Arc::new(subjects::Ninja);
-
- let abstract_ninja: Arc<dyn subjects::INinja> = concrete_ninja;
-
- let display_ninja_result = abstract_ninja.cast::<dyn Display>();
-
- 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<dyn Any + Send + Sync> = concrete_ninja;
-
- let debug_ninja_result = any_ninja.cast::<dyn Debug>();
-
- 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
-//!
-//! <https://github.com/CodeChain-io/intertrait>
-//! <https://crates.io/crates/intertrait/0.2.2>
-//!
-//! Licensed under either of
-//!
-//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
-//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-//!
-//! 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<Dest: ?Sized + 'static>(self: Box<Self>) -> Result<Box<Dest>, CastError>;
-}
-
-/// A blanket implementation of `CastBox` for traits extending `CastFrom`.
-impl<CastFromSelf: ?Sized + CastFrom> CastBox for CastFromSelf
-{
- fn cast<Dest: ?Sized + 'static>(self: Box<Self>) -> Result<Box<Dest>, CastError>
- {
- let caster =
- get_caster::<Dest>((*self).type_id()).map_err(CastError::GetCasterFailed)?;
-
- (caster.cast_box)(self.box_any()).map_err(|err| CastError::CastFailed {
- source: err,
- from: type_name::<Self>(),
- to: type_name::<Dest>(),
- })
- }
-}
-
-#[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<dyn subjects::INinja> = concrete_ninja;
-
- let debug_ninja_result = abstract_ninja.cast::<dyn Debug>();
-
- assert!(debug_ninja_result.is_ok());
- }
-
- #[test]
- fn cannot_cast_box_wrong()
- {
- let concrete_ninja = Box::new(subjects::Ninja);
-
- let abstract_ninja: Box<dyn subjects::INinja> = concrete_ninja;
-
- let display_ninja_result = abstract_ninja.cast::<dyn Display>();
-
- 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<dyn Any> = concrete_ninja;
-
- let debug_ninja_result = any_ninja.cast::<dyn Debug>();
-
- 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
-//!
-//! <https://github.com/CodeChain-io/intertrait>
-//! <https://crates.io/crates/intertrait/0.2.2>
-//!
-//! Licensed under either of
-//!
-//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
-//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-//!
-//! 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<dyn Any + Send + Sync>;
-
-/// 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<AHashMap<(TypeId, TypeId), BoxedCaster>> = Lazy::new(|| {
- CASTERS
- .iter()
- .map(|caster_fn| {
- let (type_id, caster) = caster_fn();
-
- ((type_id, (*caster).type_id()), caster)
- })
- .collect()
-});
-
-type CastBoxFn<Dest> = fn(from: Box<dyn Any>) -> Result<Box<Dest>, CasterError>;
-
-type CastRcFn<Dest> = fn(from: Rc<dyn Any>) -> Result<Rc<Dest>, CasterError>;
-
-type CastArcFn<Dest> =
- fn(from: Arc<dyn Any + Sync + Send + 'static>) -> Result<Arc<Dest>, 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<Dest: ?Sized + 'static>
-{
- /// Casts a `Box` holding a type or trait object for `Any` to another `Box` holding a
- /// type or trait `Dest`.
- pub cast_box: CastBoxFn<Dest>,
-
- /// Casts an `Rc` holding a type or trait for `Any` to another `Rc` holding a type or
- /// trait `Dest`.
- pub cast_rc: CastRcFn<Dest>,
-
- /// 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<CastArcFn<Dest>>,
-}
-
-impl<Dest: ?Sized + 'static> Caster<Dest>
-{
- pub fn new(cast_box: CastBoxFn<Dest>, cast_rc: CastRcFn<Dest>) -> Caster<Dest>
- {
- Caster::<Dest> {
- cast_box,
- cast_rc,
- opt_cast_arc: None,
- }
- }
-
- #[allow(clippy::similar_names)]
- pub fn new_sync(
- cast_box: CastBoxFn<Dest>,
- cast_rc: CastRcFn<Dest>,
- cast_arc: CastArcFn<Dest>,
- ) -> Caster<Dest>
- {
- Caster::<Dest> {
- 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<Dest>` from a concrete type with the id `type_id` to a type or trait
-/// `Dest`.
-fn get_caster<Dest: ?Sized + 'static>(
- type_id: TypeId,
-) -> Result<&'static Caster<Dest>, GetCasterError>
-{
- let any_caster = CASTER_MAP
- .get(&(type_id, TypeId::of::<Caster<Dest>>()))
- .ok_or(GetCasterError::NotFound)?;
-
- any_caster
- .downcast_ref::<Caster<Dest>>()
- .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<Self>) -> Box<dyn Any>;
-
- /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait.
- fn rc_any(self: Rc<Self>) -> Rc<dyn Any>;
-}
-
-/// 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<Self>) -> Arc<dyn Any + Sync + Send + 'static>;
-}
-
-impl<Source: Sized + Any + 'static> CastFrom for Source
-{
- fn box_any(self: Box<Self>) -> Box<dyn Any>
- {
- self
- }
-
- fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
- {
- self
- }
-}
-
-impl CastFrom for dyn Any + 'static
-{
- fn box_any(self: Box<Self>) -> Box<dyn Any>
- {
- self
- }
-
- fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
- {
- self
- }
-}
-
-impl<Source: Sized + Sync + Send + 'static> CastFromArc for Source
-{
- fn arc_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send + 'static>
- {
- self
- }
-}
-
-impl CastFrom for dyn Any + Sync + Send + 'static
-{
- fn box_any(self: Box<Self>) -> Box<dyn Any>
- {
- self
- }
-
- fn rc_any(self: Rc<Self>) -> Rc<dyn Any>
- {
- self
- }
-}
-
-impl CastFromArc for dyn Any + Sync + Send + 'static
-{
- fn arc_any(self: Arc<Self>) -> Arc<dyn Any + Sync + Send + 'static>
- {
- 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::<subjects::Ninja>();
-
- let caster = Box::new(Caster::<dyn Debug> {
- cast_box: |from| {
- let concrete = from
- .downcast::<subjects::Ninja>()
- .map_err(|_| CasterError::CastBoxFailed)?;
-
- Ok(concrete as Box<dyn Debug>)
- },
- cast_rc: |from| {
- let concrete = from
- .downcast::<subjects::Ninja>()
- .map_err(|_| CasterError::CastRcFailed)?;
-
- Ok(concrete as Rc<dyn Debug>)
- },
- opt_cast_arc: Some(|from| {
- let concrete = from
- .downcast::<subjects::Ninja>()
- .map_err(|_| CasterError::CastArcFailed)?;
-
- Ok(concrete as Arc<dyn Debug>)
- }),
- });
- (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
-//!
-//! <https://github.com/CodeChain-io/intertrait>
-//! <https://crates.io/crates/intertrait/0.2.2>
-//!
-//! Licensed under either of
-//!
-//! Apache License, Version 2.0 (LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
-//! MIT license (LICENSE-MIT or <http://opensource.org/licenses/MIT>)
-//!
-//! 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<Dest: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<Dest>, CastError>;
-}
-
-/// A blanket implementation of `CastRc` for traits extending `CastFrom`.
-impl<CastFromSelf: ?Sized + CastFrom> CastRc for CastFromSelf
-{
- fn cast<Dest: ?Sized + 'static>(self: Rc<Self>) -> Result<Rc<Dest>, CastError>
- {
- let caster =
- get_caster::<Dest>((*self).type_id()).map_err(CastError::GetCasterFailed)?;
-
- (caster.cast_rc)(self.rc_any()).map_err(|err| CastError::CastFailed {
- source: err,
- from: type_name::<Self>(),
- to: type_name::<Dest>(),
- })
- }
-}
-
-#[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<dyn subjects::INinja> = concrete_ninja;
-
- let debug_ninja_result = abstract_ninja.cast::<dyn Debug>();
-
- assert!(debug_ninja_result.is_ok());
- }
-
- #[test]
- fn cannot_cast_rc_wrong()
- {
- let concrete_ninja = Rc::new(subjects::Ninja);
-
- let abstract_ninja: Rc<dyn subjects::INinja> = concrete_ninja;
-
- let display_ninja_result = abstract_ninja.cast::<dyn Display>();
-
- 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<dyn Any> = concrete_ninja;
-
- let debug_ninja_result = any_ninja.cast::<dyn Debug>();
-
- 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<u8>]>,
+ type_id: TypeId,
+ kind: Kind,
+}
+
+impl PtrBuffer
+{
+ /// AA.
+ #[must_use]
+ pub fn new_from<Value, ValuePtr>(value: ValuePtr) -> Self
+ where
+ Value: ?Sized + 'static,
+ ValuePtr: Into<SmartPtr<Value>>,
+ {
+ let value = value.into();
+
+ let kind = value.kind();
+
+ let buf = ptr_to_byte_buf(value.into_raw());
+
+ Self {
+ buf,
+ type_id: TypeId::of::<Value>(),
+ kind,
+ }
+ }
+
+ pub(crate) fn cast_into_boxed<Dest>(self) -> Option<Box<Dest>>
+ 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<Dest>(self) -> Option<Rc<Dest>>
+ 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<Dest>(self) -> Option<Arc<Dest>>
+ 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<Dest>(self) -> Option<*mut Dest>
+ where
+ Dest: ?Sized + 'static,
+ {
+ if TypeId::of::<Dest>() != 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::<u8>(),
+ ptr.as_mut_ptr().cast::<u8>(),
+ 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<Value: ?Sized + 'static>
+{
+ /// Box.
+ Box(Box<Value>),
+
+ /// Rc.
+ Rc(Rc<Value>),
+
+ /// Arc.
+ Arc(Arc<Value>),
+}
+
+impl<Value: ?Sized + 'static> SmartPtr<Value>
+{
+ 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<Value> From<Box<Value>> for SmartPtr<Value>
+where
+ Value: ?Sized + 'static,
+{
+ fn from(value: Box<Value>) -> Self
+ {
+ Self::Box(value)
+ }
+}
+
+impl<Value> From<Rc<Value>> for SmartPtr<Value>
+where
+ Value: ?Sized + 'static,
+{
+ fn from(value: Rc<Value>) -> Self
+ {
+ Self::Rc(value)
+ }
+}
+
+impl<Value> From<Arc<Value>> for SmartPtr<Value>
+where
+ Value: ?Sized + 'static,
+{
+ fn from(value: Arc<Value>) -> Self
+ {
+ Self::Arc(value)
+ }
+}
+
+enum Kind
+{
+ Box,
+ Rc,
+ Arc,
+}
+
+fn ptr_to_byte_buf<Value>(value_ptr: *const Value) -> Box<[MaybeUninit<u8>]>
+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::<u8>(
+ addr_of!(value_ptr).cast::<u8>(),
+ size_of::<*const Value>(),
+ )
+ };
+
+ value_ptr_bytes
+ .iter()
+ .map(|byte| MaybeUninit::new(*byte))
+ .collect::<Vec<_>>()
+ .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::<String>()
+ .map_or_else(|| false, |text| *text == "Hello there"));
+ }
+
+ #[test]
+ fn works_with_dyn()
+ {
+ let text: Box<dyn Anything> = Box::new(Something);
+
+ let ptr_buf = PtrBuffer::new_from(text);
+
+ assert!(ptr_buf
+ .cast_into_boxed::<dyn Anything>()
+ .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::<String>().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::<String>().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::<String>().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::<PathBuf>().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<T>(slice: &[MaybeUninit<T>]) -> &[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<T>] 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<DIContainerT> Injectable<DIContainerT> for UserManager
{
fn resolve(
@@ -58,6 +55,27 @@ pub mod subjects
{
Ok(TransientPtr::new(Self::new()))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ let me: Box<dyn IUserManager> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ let me: Rc<dyn IUserManager> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer
+ {
+ let me: Arc<dyn IUserManager> = self;
+
+ PtrBuffer::new_from(me)
+ }
}
pub trait INumber
@@ -109,8 +127,6 @@ pub mod subjects
}
}
- declare_interface!(Number -> INumber);
-
impl<DIContainerT> Injectable<DIContainerT> for Number
{
fn resolve(
@@ -122,12 +138,33 @@ pub mod subjects
{
Ok(TransientPtr::new(Self::new()))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ let me: Box<dyn INumber> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ let me: Rc<dyn INumber> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer
+ {
+ let me: Arc<dyn INumber> = 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<DIContainerType> AsyncInjectable<DIContainerType> for UserManager
{
@@ -194,6 +230,27 @@ pub mod subjects_async
{
Ok(TransientPtr::new(Self::new()))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ let me: Box<dyn IUserManager> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ let me: Rc<dyn IUserManager> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer
+ {
+ let me: Arc<dyn IUserManager> = 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<DIContainerType> AsyncInjectable<DIContainerType> for Number
{
@@ -259,6 +314,27 @@ pub mod subjects_async
{
Ok(TransientPtr::new(Self::new()))
}
+
+ fn into_ptr_buffer_box(self: Box<Self>) -> PtrBuffer
+ {
+ let me: Box<dyn INumber> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_rc(self: Rc<Self>) -> PtrBuffer
+ {
+ let me: Rc<dyn INumber> = self;
+
+ PtrBuffer::new_from(me)
+ }
+
+ fn into_ptr_buffer_arc(self: Arc<Self>) -> PtrBuffer
+ {
+ let me: Arc<dyn INumber> = self;
+
+ PtrBuffer::new_from(me)
+ }
}
}