facet_core/impls/alloc/
string.rs

1use crate::{
2    Def, Facet, PtrConst, Shape, ShapeBuilder, Type, TypeOpsDirect, UserType, VTableDirect,
3    type_ops_direct, vtable_direct,
4};
5
6#[inline(always)]
7unsafe fn string_truthy(value: PtrConst) -> bool {
8    !unsafe { value.get::<alloc::string::String>() }.is_empty()
9}
10
11// TypeOps lifted out - shared static
12static STRING_TYPE_OPS: TypeOpsDirect = TypeOpsDirect {
13    is_truthy: Some(string_truthy),
14    ..type_ops_direct!(alloc::string::String => Default, Clone)
15};
16
17/// Try to convert from &str or String to String
18///
19/// # Safety
20/// `dst` must be valid for writes, `src` must point to valid data of type described by `src_shape`
21unsafe fn string_try_from(
22    dst: *mut alloc::string::String,
23    src_shape: &'static Shape,
24    src: PtrConst,
25) -> Result<(), alloc::string::String> {
26    // Check if source is &str
27    if src_shape.id == <&str as crate::Facet>::SHAPE.id {
28        let str_ref: &str = unsafe { src.get::<&str>() };
29        unsafe { dst.write(alloc::string::String::from(str_ref)) };
30        return Ok(());
31    }
32
33    // Check if source is String (clone it)
34    if src_shape.id == <alloc::string::String as crate::Facet>::SHAPE.id {
35        let src_string: &alloc::string::String =
36            unsafe { &*(src.as_byte_ptr() as *const alloc::string::String) };
37        unsafe { dst.write(src_string.clone()) };
38        return Ok(());
39    }
40
41    Err(alloc::format!(
42        "cannot convert {} to String",
43        src_shape.type_identifier
44    ))
45}
46
47unsafe impl Facet<'_> for alloc::string::String {
48    // String implements: Display, Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, FromStr
49    const SHAPE: &'static Shape = &const {
50        const VTABLE: VTableDirect = vtable_direct!(alloc::string::String =>
51            FromStr,
52            Display,
53            Debug,
54            Hash,
55            PartialEq,
56            PartialOrd,
57            Ord,
58            [try_from = string_try_from],
59        );
60
61        ShapeBuilder::for_sized::<alloc::string::String>("String")
62            .ty(Type::User(UserType::Opaque))
63            .def(Def::Scalar)
64            .vtable_direct(&VTABLE)
65            .type_ops_direct(&STRING_TYPE_OPS)
66            .eq()
67            .send()
68            .sync()
69            .build()
70    };
71}
72
73#[cfg(test)]
74mod tests {
75    use core::ptr::NonNull;
76
77    use crate::Facet;
78    use alloc::string::String;
79
80    #[test]
81    fn test_string_has_parse() {
82        // Check that String has a parse function in its vtable
83        let shape = String::SHAPE;
84        assert!(
85            shape.vtable.has_parse(),
86            "String should have parse function"
87        );
88    }
89
90    #[test]
91    fn test_string_parse() {
92        // Test that we can parse a string into a String
93        let shape = String::SHAPE;
94
95        // Allocate memory for the String
96        let layout = shape.layout.sized_layout().unwrap();
97        let ptr = unsafe { alloc::alloc::alloc(layout) };
98        let Some(ptr) = NonNull::new(ptr) else {
99            alloc::alloc::handle_alloc_error(layout)
100        };
101        let ptr_mut = crate::PtrMut::new(ptr.as_ptr());
102
103        // Parse the string using the new API
104        let result = unsafe { shape.call_parse("hello world", ptr_mut) };
105        assert!(result.is_some(), "String should have parse function");
106        assert!(result.unwrap().is_ok());
107
108        // Get the parsed value
109        let parsed = unsafe { ptr_mut.get::<String>() };
110        assert_eq!(parsed, &String::from("hello world"));
111
112        // Clean up
113        unsafe {
114            shape.call_drop_in_place(ptr_mut).unwrap();
115            alloc::alloc::dealloc(ptr.as_ptr(), layout);
116        }
117    }
118}