Skip to main content

token_value_map/
token.rs

1//! A string token type used as a key in token-value maps.
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6use std::fmt;
7
8/// A string token used as a key in [`TokenValueMap`](crate::TokenValueMap) and
9/// [`GenericTokenValueMap`](crate::GenericTokenValueMap).
10///
11/// When the `ustr` feature is enabled, wraps [`Ustr`](ustr::Ustr) for
12/// O(1) equality via interned string comparison. Otherwise wraps [`String`].
13///
14/// Archives as [`ArchivedString`](https://docs.rs/rkyv/latest/rkyv/string/struct.ArchivedString.html)
15/// via manual [`Archive`](https://docs.rs/rkyv/latest/rkyv/trait.Archive.html) impl,
16/// so `rkyv` support works regardless of whether the `ustr` backend provides its
17/// own rkyv support.
18#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
19#[cfg_attr(feature = "ustr", derive(Copy))]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[cfg_attr(feature = "serde", serde(transparent))]
22#[repr(transparent)]
23pub struct Token(
24    #[cfg(feature = "ustr")] pub ustr::Ustr,
25    #[cfg(not(feature = "ustr"))] pub std::string::String,
26);
27
28// AIDEV-NOTE: Manual rkyv impl archives Token as ArchivedString regardless of
29// inner type (Ustr or String). This avoids requiring Ustr: Archive, which the
30// crates.io ustr (1.x) does not provide.
31#[cfg(feature = "rkyv")]
32const _: () = {
33    use rkyv::{
34        Archive, Deserialize, Place, Serialize, SerializeUnsized,
35        rancor::{Fallible, Source},
36        string::{ArchivedString, StringResolver},
37    };
38
39    impl Archive for Token {
40        type Archived = ArchivedString;
41        type Resolver = StringResolver;
42
43        fn resolve(&self, resolver: Self::Resolver, out: Place<Self::Archived>) {
44            ArchivedString::resolve_from_str(self.as_str(), resolver, out);
45        }
46    }
47
48    impl<S: Fallible + ?Sized> Serialize<S> for Token
49    where
50        S::Error: Source,
51        str: SerializeUnsized<S>,
52    {
53        fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
54            ArchivedString::serialize_from_str(self.as_str(), serializer)
55        }
56    }
57
58    impl<D: Fallible + ?Sized> Deserialize<Token, D> for ArchivedString {
59        fn deserialize(&self, _: &mut D) -> Result<Token, D::Error> {
60            Ok(Token::from(self.as_str()))
61        }
62    }
63};
64
65// AIDEV-NOTE: Manual Facet impl marks Token as opaque without requiring
66// Facet on the inner type (Ustr or String). The derive macro would add
67// a Facet bound on the field, which fails when crates.io ustr lacks facet.
68// All vtable fns go through Token::as_str() / Token::new() so facet always
69// sees the string content, never the raw Ustr pointer/index.
70#[cfg(feature = "facet")]
71const _: () = {
72    use facet::{
73        Def, Facet, OxPtrConst, OxPtrUninit, ParseError, PtrConst, Shape, ShapeBuilder,
74        TryFromOutcome, Type, UserType, VTableIndirect,
75    };
76
77    unsafe fn display_token(
78        source: OxPtrConst,
79        f: &mut core::fmt::Formatter<'_>,
80    ) -> Option<core::fmt::Result> {
81        unsafe { Some(write!(f, "{}", source.get::<Token>())) }
82    }
83
84    unsafe fn partial_eq_token(a: OxPtrConst, b: OxPtrConst) -> Option<bool> {
85        unsafe { Some(a.get::<Token>() == b.get::<Token>()) }
86    }
87
88    unsafe fn try_from_token(
89        target: OxPtrUninit,
90        src_shape: &'static Shape,
91        src: PtrConst,
92    ) -> TryFromOutcome {
93        unsafe {
94            if src_shape.id == <&str as Facet>::SHAPE.id {
95                target.put(Token::new(src.get::<&str>()));
96                TryFromOutcome::Converted
97            } else if src_shape.id == <std::string::String as Facet>::SHAPE.id {
98                target.put(Token::from(src.read::<std::string::String>()));
99                TryFromOutcome::Converted
100            } else {
101                TryFromOutcome::Unsupported
102            }
103        }
104    }
105
106    /// Reconstruct a [`Token`] from its string representation.
107    unsafe fn parse_token(s: &str, target: OxPtrUninit) -> Option<Result<(), ParseError>> {
108        unsafe {
109            target.put(Token::new(s));
110            Some(Ok(()))
111        }
112    }
113
114    static TOKEN_VTABLE: VTableIndirect = VTableIndirect {
115        display: Some(display_token),
116        partial_eq: Some(partial_eq_token),
117        try_from: Some(try_from_token),
118        parse: Some(parse_token),
119        ..VTableIndirect::EMPTY
120    };
121
122    unsafe impl Facet<'_> for Token {
123        const SHAPE: &'static Shape = &const {
124            ShapeBuilder::for_sized::<Token>("Token")
125                .module_path("token_value_map::token")
126                .ty(Type::User(UserType::Opaque))
127                .def(Def::Scalar)
128                .vtable_indirect(&TOKEN_VTABLE)
129                .eq()
130                .build()
131        };
132    }
133};
134
135impl Token {
136    /// Creates a new `Token` from a string slice.
137    pub fn new(s: &str) -> Self {
138        Self::from(s)
139    }
140
141    /// Returns the underlying string slice.
142    pub fn as_str(&self) -> &str {
143        #[cfg(feature = "ustr")]
144        {
145            self.0.as_str()
146        }
147        #[cfg(not(feature = "ustr"))]
148        {
149            &self.0
150        }
151    }
152}
153
154impl fmt::Debug for Token {
155    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156        write!(f, "Token({:?})", self.as_str())
157    }
158}
159
160impl fmt::Display for Token {
161    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162        f.write_str(self.as_str())
163    }
164}
165
166impl std::ops::Deref for Token {
167    type Target = str;
168
169    fn deref(&self) -> &str {
170        self.as_str()
171    }
172}
173
174impl AsRef<str> for Token {
175    fn as_ref(&self) -> &str {
176        self.as_str()
177    }
178}
179
180impl std::borrow::Borrow<str> for Token {
181    fn borrow(&self) -> &str {
182        self.as_str()
183    }
184}
185
186impl From<&str> for Token {
187    fn from(s: &str) -> Self {
188        Self(s.into())
189    }
190}
191
192impl From<std::string::String> for Token {
193    fn from(s: std::string::String) -> Self {
194        #[cfg(feature = "ustr")]
195        {
196            Self(ustr::ustr(&s))
197        }
198        #[cfg(not(feature = "ustr"))]
199        {
200            Self(s)
201        }
202    }
203}
204
205#[cfg(feature = "ustr")]
206impl From<ustr::Ustr> for Token {
207    fn from(u: ustr::Ustr) -> Self {
208        Self(u)
209    }
210}
211
212#[cfg(feature = "ustr")]
213impl From<Token> for ustr::Ustr {
214    fn from(t: Token) -> Self {
215        t.0
216    }
217}