1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
//! Fast UTF8 data references.

use crate::types::Puff;
use axum::body::Bytes as AxumBytes;
use axum::response::{IntoResponse, Response};
use bb8_redis::redis::{
    ErrorKind, FromRedisValue, RedisError, RedisResult, RedisWrite, ToRedisArgs, Value,
};
use compact_str::{CompactString, ToCompactString};
use pyo3::types::PyString;
use pyo3::{FromPyObject, IntoPy, Py, PyAny, PyObject, PyResult, Python, ToPyObject};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::cmp::Ordering;
use std::fmt::{Display, Formatter};
use std::ops::{Add, Deref};
use std::str::{from_utf8, FromStr};

/// Fast UTF8 data references.
///
/// Puff's Text type uses [compact_str::CompactString] under the hood. This inlines small strings
/// for maximum performance and storage as well as prevents copying 'static strings. The trade off
/// for better string memory management comes at the cost of mutability. `Text` types are immutable.
///
/// ```
/// use puff_rs::types::Text;
///
/// let text = Text::from("Hello");
/// ```
///
/// Standard rust `Strings` are meant to be mutable. They are available as a `TextBuilder`:
///
/// ```
/// use puff_rs::types::{TextBuilder, Text};
///
/// let mut buff = TextBuilder::new();
/// buff.push_str("hello");
/// buff.push_str(" ");
/// buff.push_str("world");
/// let t: Text = buff.into_text();
/// ```
#[derive(Hash, PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
pub struct Text(CompactString);

impl Display for Text {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        self.0.fmt(f)
    }
}

impl PartialOrd for Text {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        self.0.as_str().partial_cmp(other.0.as_str())
    }
}

impl Ord for Text {
    fn cmp(&self, other: &Self) -> Ordering {
        self.0.as_str().cmp(other.0.as_str())
    }
}

impl Text {
    pub fn new() -> Text {
        Text(CompactString::from(""))
    }

    pub fn from_utf8(s: &[u8]) -> Option<Text> {
        from_utf8(s).ok().map(|f| Text::from(f))
    }

    pub fn into_string(self) -> String {
        return self.0.into();
    }

    pub fn as_str(&self) -> &str {
        self.0.as_str()
    }
}

impl IntoPy<Py<PyAny>> for Text {
    fn into_py(self, py: Python<'_>) -> Py<PyAny> {
        PyString::new(py, self.0.as_str()).into_py(py)
    }
}

impl ToPyObject for Text {
    fn to_object(&self, py: Python<'_>) -> PyObject {
        self.0.as_str().to_object(py)
    }
}

impl Add for Text {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        Text(self.0 + &other.0)
    }
}

impl Add<&str> for Text {
    type Output = Self;

    fn add(self, other: &str) -> Self {
        let mut s = self.0.to_string();
        s.push_str(other);
        Text(CompactString::from(s))
    }
}

// impl AddAssign<&str> for Text {
//     fn add_assign(&mut self, rhs: &str) {
//         self.0 = self.0.clone().add(rhs)
//     }
// }
//
// impl AddAssign<&Text> for Text {
//     fn add_assign(&mut self, rhs: &Text) {
//         self.0 = self.0.clone().add(rhs.0.as_str())
//     }
// }
//
// impl AddAssign for Text {
//     fn add_assign(&mut self, rhs: Self) {
//         self.0 = self.0.clone().add(&rhs.0)
//     }
// }
//
// impl From<u32> for Text {
//     fn from(x: u32) -> Self {
//         Text(x.to_compact_string())
//     }
// }

pub trait ToText {
    fn to_text(self) -> Text;
}

impl<T: ToCompactString> ToText for T {
    fn to_text(self) -> Text {
        Text(self.to_compact_string())
    }
}

impl<'a> From<&'a str> for Text {
    fn from(s: &'a str) -> Self {
        Text(CompactString::from(s))
    }
}

impl From<String> for Text {
    fn from(s: String) -> Self {
        Text(CompactString::from(s))
    }
}

impl<'a> From<&'a String> for Text {
    fn from(s: &'a String) -> Self {
        Text(CompactString::from(s))
    }
}

impl<'a> From<Cow<'a, str>> for Text {
    fn from(cow: Cow<'a, str>) -> Self {
        Text(CompactString::from(cow))
    }
}

impl From<Box<str>> for Text {
    fn from(b: Box<str>) -> Self {
        Text(CompactString::from(b))
    }
}

impl From<Text> for String {
    fn from(s: Text) -> Self {
        s.0.into()
    }
}

impl FromStr for Text {
    type Err = core::convert::Infallible;
    fn from_str(s: &str) -> Result<Text, Self::Err> {
        Ok(Text(CompactString::from_str(s)?))
    }
}

impl<'source> FromPyObject<'source> for Text {
    fn extract(ob: &'source PyAny) -> PyResult<Self> {
        let s = ob.downcast::<PyString>()?;
        Ok(Text::from(s.to_str()?))
    }
}

impl IntoResponse for Text {
    fn into_response(self) -> Response {
        AxumBytes::copy_from_slice(self.0.as_bytes()).into_response()
    }
}
//
// impl From<String> for Text {
//     fn from(x: String) -> Self {
//         Text(x.to_compact_string())
//     }
// }
//
// impl From<&'static str> for Text {
//     fn from(x: &'static str) -> Self {
//         Text(x.to_compact_string())
//     }
// }

impl Puff for Text {}

impl Deref for Text {
    type Target = str;

    fn deref(&self) -> &Self::Target {
        self.0.as_str()
    }
}

// impl Serialize for Text {
//     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
//         self.0.serialize(serializer)
//     }
// }

//
// impl<'de> Deserialize<'de> for Text {
//     fn deserialize<D>(deserializer: D) -> Result<Text, D::Error>
//     where
//         D: Deserializer<'de>,
//     {
//         let cs = CompactString::deserialize(deserializer)?;
//         Ok(Text(cs))
//     }
// }

impl ToRedisArgs for Text {
    fn write_redis_args<W>(&self, out: &mut W)
    where
        W: ?Sized + RedisWrite,
    {
        out.write_arg(self.as_bytes())
    }
}

impl FromRedisValue for Text {
    fn from_redis_value(v: &Value) -> RedisResult<Self> {
        match v {
            Value::Data(ref bytes) => Ok(from_utf8(bytes)?.into()),
            Value::Okay => Ok("OK".into()),
            Value::Status(ref val) => Ok(val.to_string().into()),
            val => Err(RedisError::from((
                ErrorKind::TypeError,
                "Response was of incompatible type",
                format!(
                    "Response type not string compatible. (response was {:?})",
                    val
                ),
            ))),
        }
    }
}