estring/structs/
trio.rs

1//! Contains the implementations to parse triple-tuple type
2//!
3
4use super::Pair;
5use crate::core::{EString, ParseFragment, ToEString};
6use std::fmt::Write;
7
8/// Wrapper for trio (A, B, C) tuple to split string by separators (`S1` and `S2`).
9///
10/// **NOTE**: Required the enabling of the `structs` feature.
11///
12/// # Examples
13///
14/// ```rust
15/// use estring::{Trio, EString};
16///
17/// fn main() -> estring::Result<()> {
18///     let res = EString::from("one+two=free").parse::<Trio<&str, '+', &str, '=', &str>>()?;
19///     assert_eq!(res, Trio("one", "two", "free"));
20///     Ok(())
21/// }
22/// ```
23///
24#[derive(Debug, PartialEq, Clone)]
25pub struct Trio<A, const S1: char, B, const S2: char, C>(pub A, pub B, pub C);
26
27impl<A, B, C, const S1: char, const S2: char> From<(A, B, C)> for Trio<A, S1, B, S2, C> {
28    #[inline]
29    fn from((a, b, c): (A, B, C)) -> Self {
30        Self(a, b, c)
31    }
32}
33
34impl<A, B, C, const S1: char, const S2: char> std::fmt::Display for Trio<A, S1, B, S2, C>
35where
36    A: std::fmt::Display,
37    B: std::fmt::Display,
38    C: std::fmt::Display,
39{
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.write_str(&self.0.to_string())?;
42        f.write_char(S1)?;
43        f.write_str(&self.1.to_string())?;
44        f.write_char(S2)?;
45        f.write_str(&self.2.to_string())
46    }
47}
48
49impl<A, B, C, const S1: char, const S2: char> ToEString for Trio<A, S1, B, S2, C>
50where
51    A: ToEString,
52    B: ToEString,
53    C: ToEString,
54{
55    fn to_estring(&self) -> EString {
56        let mut res = String::new();
57        write!(
58            res,
59            "{}{}{}{}{}",
60            self.0.to_estring(),
61            S1,
62            self.1.to_estring(),
63            S2,
64            self.2.to_estring()
65        )
66        .ok()
67        .expect("Cannot parse Pair to EString");
68        EString(res)
69    }
70}
71
72impl<A, const S1: char, B, const S2: char, C> ParseFragment for Trio<A, S1, B, S2, C>
73where
74    A: ParseFragment,
75    B: ParseFragment,
76    C: ParseFragment,
77{
78    fn parse_frag(value: EString) -> crate::Result<Self> {
79        Pair::<A, S1, EString>::parse_frag(value).and_then(|Pair(a, rest)| {
80            Pair::<B, S2, C>::parse_frag(rest).map(|Pair(b, c)| Self(a, b, c))
81        })
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    type EqTrio<A, B, C> = Trio<A, '=', B, '=', C>;
90
91    #[test]
92    fn should_parse_into_trio() {
93        let estr = EString::from("hello=world=hello");
94        match estr.parse::<EqTrio<&str, &str, &str>>() {
95            Ok(res) => assert_eq!((res.0, res.1, res.2), ("hello", "world", "hello")),
96            _ => unreachable!(),
97        };
98    }
99
100    #[test]
101    fn should_parse_into_trio_with_alternate_delims() {
102        let estr = EString::from("hello-world^hello");
103        match estr.parse::<Trio<&str, '-', &str, '^', &str>>() {
104            Ok(res) => assert_eq!((res.0, res.1, res.2), ("hello", "world", "hello")),
105            _ => unreachable!(),
106        };
107    }
108
109    #[test]
110    fn should_parse_rest_as_trio() {
111        let estr = EString::from("hello=world=hello=world=hello");
112        match estr.parse::<EqTrio<&str, &str, EqTrio<&str, &str, &str>>>() {
113            Ok(res) => assert_eq!(res, Trio("hello", "world", Trio("hello", "world", "hello"))),
114            _ => unreachable!(),
115        };
116    }
117
118    #[test]
119    fn should_format_trio() {
120        let trio = Trio::<_, '+', _, '-', _>::from(("foo", "baz", "bar"));
121        assert_eq!(
122            trio.clone().to_estring(),
123            EString(String::from("foo+baz-bar"))
124        );
125
126        let trio_in_trio = Trio::<_, '*', _, '=', _>::from(("foo", "baz", trio));
127        assert_eq!(
128            trio_in_trio.clone().to_estring(),
129            EString(String::from("foo*baz=foo+baz-bar"))
130        );
131    }
132}