lilliput_core/value/
string.rs

1#[cfg(any(test, feature = "testing"))]
2use proptest::prelude::*;
3#[cfg(any(test, feature = "testing"))]
4use proptest_derive::Arbitrary;
5
6/// Represents a string.
7#[cfg_attr(any(test, feature = "testing"), derive(Arbitrary))]
8#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
9pub struct StringValue(pub String);
10
11impl StringValue {
12    /// Returns a reference to the internal string.
13    pub fn as_str(&self) -> &str {
14        &self.0
15    }
16
17    /// Returns the internal string, consuming `self`.
18    pub fn into_string(self) -> String {
19        self.0
20    }
21
22    /// Returns the length of the internal string.
23    pub fn len(&self) -> usize {
24        self.0.len()
25    }
26
27    /// Returns `true`, if the internal string is empty, otherwise `false`.
28    pub fn is_empty(&self) -> bool {
29        self.0.is_empty()
30    }
31}
32
33impl From<String> for StringValue {
34    fn from(value: String) -> Self {
35        Self(value)
36    }
37}
38
39impl<'a> From<&'a StringValue> for &'a str {
40    fn from(value: &'a StringValue) -> Self {
41        &value.0
42    }
43}
44
45impl From<StringValue> for String {
46    fn from(value: StringValue) -> Self {
47        value.0
48    }
49}
50
51impl std::fmt::Debug for StringValue {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        if f.alternate() {
54            write!(f, "{:#?}", self.0)
55        } else {
56            write!(f, "{:?}", self.0)
57        }
58    }
59}
60
61impl std::fmt::Display for StringValue {
62    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63        write!(f, "{}", self.0)
64    }
65}
66
67#[cfg(feature = "serde")]
68impl serde::Serialize for StringValue {
69    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70    where
71        S: serde::Serializer,
72    {
73        self.0.serialize(serializer)
74    }
75}
76
77#[cfg(feature = "serde")]
78impl<'de> serde::Deserialize<'de> for StringValue {
79    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
80    where
81        D: serde::Deserializer<'de>,
82    {
83        Ok(Self(String::deserialize(deserializer)?))
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use proptest::prelude::*;
90    use test_log::test;
91
92    use crate::{
93        config::EncoderConfig,
94        decoder::Decoder,
95        encoder::Encoder,
96        io::{SliceReader, VecWriter},
97        value::Value,
98    };
99
100    use super::*;
101
102    #[test]
103    fn display() {
104        assert_eq!(
105            format!("{}", StringValue::from("lorem ipsum".to_owned())),
106            "lorem ipsum"
107        );
108    }
109
110    #[test]
111    fn debug() {
112        assert_eq!(
113            format!("{:?}", StringValue::from("lorem ipsum".to_owned())),
114            "\"lorem ipsum\""
115        );
116
117        assert_eq!(
118            format!("{:#?}", StringValue::from("lorem ipsum".to_owned())),
119            "\"lorem ipsum\""
120        );
121    }
122
123    proptest! {
124        #[test]
125        fn encode_decode_roundtrip(value in StringValue::arbitrary(), config in EncoderConfig::arbitrary()) {
126            let mut encoded: Vec<u8> = Vec::new();
127            let writer = VecWriter::new(&mut encoded);
128            let mut encoder = Encoder::new(writer, config);
129            encoder.encode_str(value.as_str()).unwrap();
130
131            prop_assert!(encoded.len() <= 1 + 8 + value.len());
132
133            let reader = SliceReader::new(&encoded);
134            let mut decoder = Decoder::from_reader(reader);
135            let decoded = decoder.decode_string().unwrap();
136            prop_assert_eq!(&decoded, value.as_str());
137
138            let reader = SliceReader::new(&encoded);
139            let mut decoder = Decoder::from_reader(reader);
140            let decoded = decoder.decode_value().unwrap();
141            let Value::String(decoded) = decoded else {
142                panic!("expected string value");
143            };
144            prop_assert_eq!(&decoded, &value);
145        }
146    }
147}