replaceable_implementations/
lib.rs1#![doc = include_str!("../README.md")]
2#![deny(clippy::unwrap_used)]
3
4use counters::Counters;
5use proc_macro2::{Span, TokenStream};
6use quote::{ToTokens, quote};
7use std::env::VarError;
8use syn::{Ident, ItemImpl, Path};
9
10mod counters;
11
12static IMPLEMENTATION_COUNTERS: Counters = Counters::new();
13
14#[must_use]
19pub fn setup(capacity: u32) -> TokenStream {
20 let mut output = quote! {
21 #[doc(hidden)]
24 pub trait Is<T> {}
25 impl<T> Is<T> for T {}
26 };
27
28 (0..capacity).for_each(|index| {
29 let ident = Ident::new(&format!("Switch{index}"), Span::call_site());
30 output.extend(quote! {
31 #[doc(hidden)]
32 pub struct #ident<T, const BOOL: bool>(core::marker::PhantomData<T>);
33 });
34 });
35 output
36}
37
38pub fn initial_implementation(
44 path_to_setup: &Path,
45 implementation: ItemImpl,
46) -> Result<ItemImpl, syn::Error> {
47 make_replaceable(path_to_setup, 0, implementation)
48}
49
50pub fn replace_implementation(
60 path_to_setup: &Path,
61 id: String,
62 has_initial_implementation: bool,
63) -> Result<(u16, impl FnOnce(ItemImpl) -> TokenStream), VarError> {
64 let previous_implementations =
65 IMPLEMENTATION_COUNTERS.fetch_add(id, has_initial_implementation.into())?;
66 let replace = move |implementation: ItemImpl| -> TokenStream {
67 let implementation =
68 match make_replaceable(path_to_setup, previous_implementations, implementation) {
69 Ok(implementation) => implementation,
70 Err(error) => return error.to_compile_error(),
71 };
72
73 if previous_implementations == 0 {
75 implementation.to_token_stream()
76 } else {
77 let switch_previous = Ident::new(
78 &format!("Switch{}", previous_implementations - 1),
79 Span::call_site(),
80 );
81 quote! {
82 #implementation
83 impl<T> core::marker::Unpin for #path_to_setup::#switch_previous<T, false> {}
84 }
85 }
86 };
87 Ok((previous_implementations, replace))
88}
89
90fn make_replaceable(
91 path_to_setup: &Path,
92 previous_implementations: u16,
93 mut implementation: ItemImpl,
94) -> Result<ItemImpl, syn::Error> {
95 let switch_current = Ident::new(
97 &format!("Switch{previous_implementations}"),
98 Span::call_site(),
99 );
100
101 let kidnapped_type = &*implementation.self_ty;
102 let kidnapped = syn::parse2(quote! {
103 Kidnapped: #path_to_setup::Is<#kidnapped_type>
104 })?;
105
106 implementation
107 .generics
108 .params
109 .push(syn::GenericParam::Type(kidnapped));
110
111 *implementation.self_ty = syn::parse2(quote! {Kidnapped})?;
112
113 let predicate = syn::parse2(
114 quote! {#path_to_setup::#switch_current<Kidnapped, true>: core::marker::Unpin},
115 )?;
116 implementation
117 .generics
118 .make_where_clause()
119 .predicates
120 .push(predicate);
121
122 Ok(implementation)
123}