1use super::Pair;
5use crate::core::{EString, ParseFragment, ToEString};
6use std::fmt::Write;
7
8#[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}