Skip to main content

json_gettext/value/
value_impl.rs

1#[cfg(feature = "rocket")]
2use std::io::Cursor;
3use std::{
4    convert::Infallible,
5    fmt::{self, Display, Formatter},
6    str::FromStr,
7};
8
9#[cfg(feature = "rocket")]
10use rocket::form::{self, FromFormField, ValueField};
11#[cfg(feature = "rocket")]
12use rocket::request::{FromParam, Request};
13#[cfg(feature = "rocket")]
14use rocket::response::{self, Responder, Response};
15use serde::{
16    Deserialize, Deserializer, Serialize, Serializer,
17    de::{Error as DeError, MapAccess, SeqAccess, Visitor},
18};
19
20use super::JSONGetTextValueError;
21use crate::serde_json::{self, Map, Value, to_value};
22
23/// Represents any valid JSON value. Reference can also be wrapped.
24#[derive(Debug, Clone, PartialEq, Eq)]
25pub enum JSONGetTextValue<'a> {
26    Str(&'a str),
27    JSONValue(Value),
28    JSONValueRef(&'a Value),
29}
30
31impl<'a> JSONGetTextValue<'a> {
32    /// Wrap a borrowed string slice without copying. Use `from_string` to store an owned string instead.
33    #[inline]
34    pub fn from_str_ref<S: AsRef<str> + ?Sized>(s: &'a S) -> JSONGetTextValue<'a> {
35        JSONGetTextValue::Str(s.as_ref())
36    }
37
38    #[inline]
39    pub fn from_string<S: Into<String>>(s: S) -> JSONGetTextValue<'static> {
40        JSONGetTextValue::JSONValue(Value::String(s.into()))
41    }
42
43    #[inline]
44    pub fn from_json_str<S: AsRef<str>>(
45        s: S,
46    ) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
47        JSONGetTextValue::parse_json(s)
48    }
49
50    #[inline]
51    pub fn from_bool(b: bool) -> JSONGetTextValue<'static> {
52        JSONGetTextValue::JSONValue(Value::Bool(b))
53    }
54
55    #[inline]
56    pub fn from_i8(n: i8) -> JSONGetTextValue<'static> {
57        JSONGetTextValue::JSONValue(to_value(n).unwrap())
58    }
59
60    #[inline]
61    pub fn from_i16(n: i16) -> JSONGetTextValue<'static> {
62        JSONGetTextValue::JSONValue(to_value(n).unwrap())
63    }
64
65    #[inline]
66    pub fn from_i32(n: i32) -> JSONGetTextValue<'static> {
67        JSONGetTextValue::JSONValue(to_value(n).unwrap())
68    }
69
70    #[inline]
71    pub fn from_i64(n: i64) -> JSONGetTextValue<'static> {
72        JSONGetTextValue::JSONValue(to_value(n).unwrap())
73    }
74
75    #[inline]
76    pub fn from_i128(n: i128) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
77        Ok(JSONGetTextValue::JSONValue(
78            to_value(n).map_err(|_| JSONGetTextValueError::IntegerOutOfRange)?,
79        ))
80    }
81
82    #[inline]
83    pub fn from_isize(n: isize) -> JSONGetTextValue<'static> {
84        JSONGetTextValue::JSONValue(to_value(n).unwrap())
85    }
86
87    #[inline]
88    pub fn from_u8(n: u8) -> JSONGetTextValue<'static> {
89        JSONGetTextValue::JSONValue(to_value(n).unwrap())
90    }
91
92    #[inline]
93    pub fn from_u16(n: u16) -> JSONGetTextValue<'static> {
94        JSONGetTextValue::JSONValue(to_value(n).unwrap())
95    }
96
97    #[inline]
98    pub fn from_u32(n: u32) -> JSONGetTextValue<'static> {
99        JSONGetTextValue::JSONValue(to_value(n).unwrap())
100    }
101
102    #[inline]
103    pub fn from_u64(n: u64) -> JSONGetTextValue<'static> {
104        JSONGetTextValue::JSONValue(to_value(n).unwrap())
105    }
106
107    #[inline]
108    pub fn from_u128(n: u128) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
109        Ok(JSONGetTextValue::JSONValue(
110            to_value(n).map_err(|_| JSONGetTextValueError::IntegerOutOfRange)?,
111        ))
112    }
113
114    #[inline]
115    pub fn from_usize(n: usize) -> JSONGetTextValue<'static> {
116        JSONGetTextValue::JSONValue(to_value(n).unwrap())
117    }
118
119    #[inline]
120    pub fn from_f32(n: f32) -> JSONGetTextValue<'static> {
121        JSONGetTextValue::JSONValue(to_value(n).unwrap())
122    }
123
124    #[inline]
125    pub fn from_f64(n: f64) -> JSONGetTextValue<'static> {
126        JSONGetTextValue::JSONValue(to_value(n).unwrap())
127    }
128
129    #[inline]
130    pub fn from_json_value(v: Value) -> JSONGetTextValue<'static> {
131        JSONGetTextValue::JSONValue(v)
132    }
133
134    #[inline]
135    pub fn from_json_value_ref(v: &'a Value) -> JSONGetTextValue<'a> {
136        JSONGetTextValue::JSONValueRef(v)
137    }
138
139    #[inline]
140    pub fn from_serializable<T: Serialize>(
141        v: T,
142    ) -> Result<JSONGetTextValue<'static>, serde_json::Error> {
143        Ok(JSONGetTextValue::JSONValue(to_value(v)?))
144    }
145
146    #[inline]
147    pub fn null() -> JSONGetTextValue<'static> {
148        JSONGetTextValue::JSONValue(Value::Null)
149    }
150}
151
152impl<'a> JSONGetTextValue<'a> {
153    /// Convert to a string for JSON format.
154    pub fn to_json_string(&self) -> String {
155        match self {
156            // `serde_json` applies proper JSON string escaping (e.g. control characters become `\u00XX`).
157            JSONGetTextValue::Str(s) => serde_json::to_string(s).unwrap(),
158            JSONGetTextValue::JSONValue(v) => v.to_string(),
159            JSONGetTextValue::JSONValueRef(v) => v.to_string(),
160        }
161    }
162
163    /// Convert to a string slice if it is possible (if it is a string).
164    #[inline]
165    pub fn as_str(&self) -> Option<&str> {
166        match self {
167            JSONGetTextValue::Str(s) => Some(s),
168            JSONGetTextValue::JSONValue(v) => match v {
169                Value::String(s) => Some(s),
170                _ => None,
171            },
172            JSONGetTextValue::JSONValueRef(v) => match v {
173                Value::String(s) => Some(s),
174                _ => None,
175            },
176        }
177    }
178
179    /// Get the wrapped `serde_json::Value` if this is not a borrowed string slice.
180    #[inline]
181    pub fn as_json_value(&self) -> Option<&Value> {
182        match self {
183            JSONGetTextValue::Str(_) => None,
184            JSONGetTextValue::JSONValue(v) => Some(v),
185            JSONGetTextValue::JSONValueRef(v) => Some(v),
186        }
187    }
188
189    /// Get the boolean if it is a boolean.
190    #[inline]
191    pub fn as_bool(&self) -> Option<bool> {
192        self.as_json_value().and_then(Value::as_bool)
193    }
194
195    /// Get the value as an `i64` if it is an integer that fits.
196    #[inline]
197    pub fn as_i64(&self) -> Option<i64> {
198        self.as_json_value().and_then(Value::as_i64)
199    }
200
201    /// Get the value as a `u64` if it is an integer that fits.
202    #[inline]
203    pub fn as_u64(&self) -> Option<u64> {
204        self.as_json_value().and_then(Value::as_u64)
205    }
206
207    /// Get the value as an `f64` if it is a number.
208    #[inline]
209    pub fn as_f64(&self) -> Option<f64> {
210        self.as_json_value().and_then(Value::as_f64)
211    }
212
213    /// Returns `true` if this value is JSON `null`.
214    #[inline]
215    pub fn is_null(&self) -> bool {
216        matches!(self.as_json_value(), Some(Value::Null))
217    }
218
219    /// Clone the reference of this `JSONGetTextValue` instance.
220    #[inline]
221    pub fn clone_borrowed(&self) -> JSONGetTextValue<'_> {
222        match self {
223            JSONGetTextValue::Str(s) => JSONGetTextValue::Str(s),
224            JSONGetTextValue::JSONValue(v) => JSONGetTextValue::JSONValueRef(v),
225            JSONGetTextValue::JSONValueRef(v) => JSONGetTextValue::JSONValueRef(v),
226        }
227    }
228}
229
230impl<'a> PartialEq<JSONGetTextValue<'a>> for str {
231    #[inline]
232    fn eq(&self, other: &JSONGetTextValue) -> bool {
233        match other {
234            JSONGetTextValue::Str(s) => s.eq(&self),
235            JSONGetTextValue::JSONValue(v) => v.eq(&self),
236            JSONGetTextValue::JSONValueRef(v) => v.eq(&self),
237        }
238    }
239}
240
241impl<'a> PartialEq<JSONGetTextValue<'a>> for &'a str {
242    #[inline]
243    fn eq(&self, other: &JSONGetTextValue) -> bool {
244        match other {
245            JSONGetTextValue::Str(s) => s.eq(self),
246            JSONGetTextValue::JSONValue(v) => v.eq(self),
247            JSONGetTextValue::JSONValueRef(v) => v.eq(self),
248        }
249    }
250}
251
252impl<'a> PartialEq<str> for JSONGetTextValue<'a> {
253    #[inline]
254    fn eq(&self, other: &str) -> bool {
255        match self {
256            JSONGetTextValue::Str(s) => s.eq(&other),
257            JSONGetTextValue::JSONValue(v) => v.eq(&other),
258            JSONGetTextValue::JSONValueRef(v) => v.eq(&other),
259        }
260    }
261}
262
263impl<'a> Display for JSONGetTextValue<'a> {
264    #[inline]
265    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
266        match self {
267            JSONGetTextValue::Str(s) => s.fmt(f),
268            JSONGetTextValue::JSONValue(v) => match v.as_str() {
269                Some(s) => s.fmt(f),
270                None => v.fmt(f),
271            },
272            JSONGetTextValue::JSONValueRef(v) => match v.as_str() {
273                Some(s) => s.fmt(f),
274                None => v.fmt(f),
275            },
276        }
277    }
278}
279
280impl<'a> From<&'a str> for JSONGetTextValue<'a> {
281    #[inline]
282    fn from(v: &'a str) -> JSONGetTextValue<'a> {
283        JSONGetTextValue::from_str_ref(v)
284    }
285}
286
287impl From<String> for JSONGetTextValue<'static> {
288    #[inline]
289    fn from(v: String) -> JSONGetTextValue<'static> {
290        JSONGetTextValue::from_string(v)
291    }
292}
293
294impl From<bool> for JSONGetTextValue<'static> {
295    #[inline]
296    fn from(v: bool) -> JSONGetTextValue<'static> {
297        JSONGetTextValue::from_bool(v)
298    }
299}
300
301impl From<i8> for JSONGetTextValue<'static> {
302    #[inline]
303    fn from(v: i8) -> JSONGetTextValue<'static> {
304        JSONGetTextValue::from_i8(v)
305    }
306}
307
308impl From<i16> for JSONGetTextValue<'static> {
309    #[inline]
310    fn from(v: i16) -> JSONGetTextValue<'static> {
311        JSONGetTextValue::from_i16(v)
312    }
313}
314
315impl From<i32> for JSONGetTextValue<'static> {
316    #[inline]
317    fn from(v: i32) -> JSONGetTextValue<'static> {
318        JSONGetTextValue::from_i32(v)
319    }
320}
321
322impl From<i64> for JSONGetTextValue<'static> {
323    #[inline]
324    fn from(v: i64) -> JSONGetTextValue<'static> {
325        JSONGetTextValue::from_i64(v)
326    }
327}
328
329impl From<isize> for JSONGetTextValue<'static> {
330    #[inline]
331    fn from(v: isize) -> JSONGetTextValue<'static> {
332        JSONGetTextValue::from_isize(v)
333    }
334}
335
336impl TryFrom<i128> for JSONGetTextValue<'static> {
337    type Error = JSONGetTextValueError;
338
339    #[inline]
340    fn try_from(v: i128) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
341        JSONGetTextValue::from_i128(v)
342    }
343}
344
345impl From<u8> for JSONGetTextValue<'static> {
346    #[inline]
347    fn from(v: u8) -> JSONGetTextValue<'static> {
348        JSONGetTextValue::from_u8(v)
349    }
350}
351
352impl From<u16> for JSONGetTextValue<'static> {
353    #[inline]
354    fn from(v: u16) -> JSONGetTextValue<'static> {
355        JSONGetTextValue::from_u16(v)
356    }
357}
358
359impl From<u32> for JSONGetTextValue<'static> {
360    #[inline]
361    fn from(v: u32) -> JSONGetTextValue<'static> {
362        JSONGetTextValue::from_u32(v)
363    }
364}
365
366impl From<u64> for JSONGetTextValue<'static> {
367    #[inline]
368    fn from(v: u64) -> JSONGetTextValue<'static> {
369        JSONGetTextValue::from_u64(v)
370    }
371}
372
373impl From<usize> for JSONGetTextValue<'static> {
374    #[inline]
375    fn from(v: usize) -> JSONGetTextValue<'static> {
376        JSONGetTextValue::from_usize(v)
377    }
378}
379
380impl TryFrom<u128> for JSONGetTextValue<'static> {
381    type Error = JSONGetTextValueError;
382
383    #[inline]
384    fn try_from(v: u128) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
385        JSONGetTextValue::from_u128(v)
386    }
387}
388
389impl From<f32> for JSONGetTextValue<'static> {
390    #[inline]
391    fn from(v: f32) -> JSONGetTextValue<'static> {
392        JSONGetTextValue::from_f32(v)
393    }
394}
395
396impl From<f64> for JSONGetTextValue<'static> {
397    #[inline]
398    fn from(v: f64) -> JSONGetTextValue<'static> {
399        JSONGetTextValue::from_f64(v)
400    }
401}
402
403impl From<Value> for JSONGetTextValue<'static> {
404    #[inline]
405    fn from(v: Value) -> JSONGetTextValue<'static> {
406        JSONGetTextValue::from_json_value(v)
407    }
408}
409
410impl<'a> From<&'a Value> for JSONGetTextValue<'a> {
411    #[inline]
412    fn from(v: &'a Value) -> JSONGetTextValue<'a> {
413        JSONGetTextValue::from_json_value_ref(v)
414    }
415}
416
417impl FromStr for JSONGetTextValue<'static> {
418    type Err = Infallible;
419
420    #[inline]
421    fn from_str(s: &str) -> Result<Self, Self::Err> {
422        Ok(JSONGetTextValue::from_string(s))
423    }
424}
425
426impl<'a> JSONGetTextValue<'a> {
427    #[inline]
428    pub fn parse_json<S: AsRef<str>>(
429        s: S,
430    ) -> Result<JSONGetTextValue<'static>, JSONGetTextValueError> {
431        Ok(JSONGetTextValue::JSONValue(serde_json::from_str(s.as_ref())?))
432    }
433}
434
435impl<'a> Serialize for JSONGetTextValue<'a> {
436    #[inline]
437    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
438    where
439        S: Serializer, {
440        match self {
441            JSONGetTextValue::Str(s) => s.serialize(serializer),
442            JSONGetTextValue::JSONValue(v) => v.serialize(serializer),
443            JSONGetTextValue::JSONValueRef(v) => v.serialize(serializer),
444        }
445    }
446}
447
448struct JSONGetTextValueVisitor;
449
450impl<'de> Visitor<'de> for JSONGetTextValueVisitor {
451    type Value = JSONGetTextValue<'de>;
452
453    #[inline]
454    fn visit_i128<E>(self, v: i128) -> Result<JSONGetTextValue<'static>, E>
455    where
456        E: DeError, {
457        JSONGetTextValue::from_i128(v).map_err(DeError::custom)
458    }
459
460    #[inline]
461    fn visit_u128<E>(self, v: u128) -> Result<JSONGetTextValue<'static>, E>
462    where
463        E: DeError, {
464        JSONGetTextValue::from_u128(v).map_err(DeError::custom)
465    }
466
467    #[inline]
468    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
469        formatter.write_str("a json value")
470    }
471
472    #[inline]
473    fn visit_bool<E>(self, v: bool) -> Result<JSONGetTextValue<'static>, E>
474    where
475        E: DeError, {
476        Ok(JSONGetTextValue::from_bool(v))
477    }
478
479    #[inline]
480    fn visit_i64<E>(self, v: i64) -> Result<JSONGetTextValue<'static>, E>
481    where
482        E: DeError, {
483        Ok(JSONGetTextValue::from_i64(v))
484    }
485
486    #[inline]
487    fn visit_u64<E>(self, v: u64) -> Result<JSONGetTextValue<'static>, E>
488    where
489        E: DeError, {
490        Ok(JSONGetTextValue::from_u64(v))
491    }
492
493    #[inline]
494    fn visit_f64<E>(self, v: f64) -> Result<JSONGetTextValue<'static>, E>
495    where
496        E: DeError, {
497        Ok(JSONGetTextValue::from_f64(v))
498    }
499
500    #[inline]
501    fn visit_str<E>(self, v: &str) -> Result<JSONGetTextValue<'static>, E>
502    where
503        E: DeError, {
504        Ok(JSONGetTextValue::from_string(v.to_string()))
505    }
506
507    #[inline]
508    fn visit_borrowed_str<E>(self, v: &'de str) -> Result<JSONGetTextValue<'de>, E>
509    where
510        E: DeError, {
511        Ok(JSONGetTextValue::from_str_ref(v))
512    }
513
514    #[inline]
515    fn visit_string<E>(self, v: String) -> Result<JSONGetTextValue<'static>, E>
516    where
517        E: DeError, {
518        Ok(JSONGetTextValue::from_string(v))
519    }
520
521    #[inline]
522    fn visit_none<E>(self) -> Result<JSONGetTextValue<'static>, E>
523    where
524        E: DeError, {
525        Ok(JSONGetTextValue::null())
526    }
527
528    #[inline]
529    fn visit_seq<A>(self, mut seq: A) -> Result<JSONGetTextValue<'static>, A::Error>
530    where
531        A: SeqAccess<'de>, {
532        let mut v = match seq.size_hint() {
533            Some(size) => Vec::with_capacity(size),
534            None => Vec::new(),
535        };
536
537        while let Some(e) = seq.next_element()? {
538            v.push(e);
539        }
540
541        Ok(JSONGetTextValue::from_json_value(Value::Array(v)))
542    }
543
544    #[inline]
545    fn visit_map<A>(self, mut map: A) -> Result<JSONGetTextValue<'static>, A::Error>
546    where
547        A: MapAccess<'de>, {
548        let mut v = match map.size_hint() {
549            Some(size) => Map::with_capacity(size),
550            None => Map::new(),
551        };
552
553        while let Some((k, e)) = map.next_entry()? {
554            v.insert(k, e);
555        }
556
557        Ok(JSONGetTextValue::from_json_value(Value::Object(v)))
558    }
559}
560
561impl<'de> Deserialize<'de> for JSONGetTextValue<'de> {
562    #[inline]
563    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
564    where
565        D: Deserializer<'de>, {
566        deserializer.deserialize_str(JSONGetTextValueVisitor)
567    }
568}
569
570#[cfg(feature = "rocket")]
571impl<'r, 'o: 'r> Responder<'r, 'o> for JSONGetTextValue<'o> {
572    fn respond_to(self, _: &'r Request<'_>) -> response::Result<'o> {
573        let mut response = Response::build();
574
575        let s = self.to_json_string();
576
577        response
578            .raw_header("Content-Type", "application/json")
579            .raw_header("Content-Length", format!("{}", s.len()))
580            .sized_body(s.len(), Cursor::new(s));
581
582        response.ok()
583    }
584}
585
586#[cfg(feature = "rocket")]
587impl<'a> FromParam<'a> for JSONGetTextValue<'a> {
588    type Error = JSONGetTextValueError;
589
590    fn from_param(param: &'a str) -> Result<Self, Self::Error> {
591        JSONGetTextValue::parse_json(param)
592    }
593}
594
595#[cfg(feature = "rocket")]
596#[rocket::async_trait]
597impl<'v> FromFormField<'v> for JSONGetTextValue<'v> {
598    fn from_value(field: ValueField<'v>) -> form::Result<'v, Self> {
599        Ok(JSONGetTextValue::parse_json(field.value).map_err(form::Error::custom)?)
600    }
601}