From 67b01b750d6704c7c9ca1accab2e41c1fd8cdc74 Mon Sep 17 00:00:00 2001
From: brxken128 <77554505+brxken128@users.noreply.github.com>
Date: Tue, 26 Jul 2022 11:42:17 +0100
Subject: [PATCH] structs: add trio
---
src/structs.rs | 2 +
src/structs/trio.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 128 insertions(+)
create mode 100644 src/structs/trio.rs
diff --git a/src/structs.rs b/src/structs.rs
index 00f1e8d..86c0232 100644
--- a/src/structs.rs
+++ b/src/structs.rs
@@ -5,6 +5,8 @@
mod pair;
mod sep_vec;
+mod trio;
pub use pair::*;
pub use sep_vec::*;
+pub use trio::*;
diff --git a/src/structs/trio.rs b/src/structs/trio.rs
new file mode 100644
index 0000000..65aa650
--- /dev/null
+++ b/src/structs/trio.rs
@@ -0,0 +1,126 @@
+//! Contains the implementations to parse triple-tuple type
+//!
+
+use crate::core::EString;
+use std::fmt::Write;
+
+/// The error type for operations interacting with parsing tuples. Possibly returned from
+/// ``EString::parse``
+#[derive(Debug)]
+pub enum Error {
+ /// The specified input string is not split.
+ Split,
+
+ /// The specified substring of the split input string is not parsed
+ Parse(u8),
+}
+
+impl std::fmt::Display for Error {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Error::Split => f.write_str("Cannot split input string"),
+ Error::Parse(n) => write!(f, "Cannot parse {} substring", n),
+ }
+ }
+}
+
+impl std::error::Error for Error {}
+
+/// Wrapper for trio (A, B, C) tuple to split string by separators (`S1` and `S2`).
+///
+/// **NOTE**: Required the enabling of the `tuple` feature.
+///
+/// # Examples
+///
+/// ```rust
+/// use estring::{Trio, EString};
+///
+/// type EqTrio = Trio;
+///
+/// fn main() -> Result<(), estring::ParseError> {
+/// let res = EString::from("one=two=free").parse::>()?;
+/// assert_eq!(res, Trio("one", "two", "free"));
+/// Ok(())
+/// }
+/// ```
+///
+#[derive(Debug, PartialEq, Clone)]
+pub struct Trio(pub A, pub B, pub C);
+
+impl From<(A, B, C)> for Trio {
+ #[inline]
+ fn from((a, b, c): (A, B, C)) -> Self {
+ Self(a, b, c)
+ }
+}
+
+impl std::fmt::Display for Trio
+where
+ A: std::fmt::Display,
+ B: std::fmt::Display,
+ C: std::fmt::Display,
+{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str(&self.0.to_string())?;
+ f.write_char(S1)?;
+ f.write_str(&self.1.to_string())?;
+ f.write_char(S2)?;
+ f.write_str(&self.2.to_string())
+ }
+}
+
+impl TryFrom for Trio
+where
+ A: TryFrom,
+ B: TryFrom,
+ C: TryFrom,
+{
+ type Error = Error;
+
+ fn try_from(value: EString) -> Result {
+ value.split_once(S1).ok_or(Error::Split).and_then(|(a, b)| {
+ let a = A::try_from(EString::from(a)).map_err(|_| Error::Parse(0))?;
+ b.split_once(S2).ok_or(Error::Split).and_then(|(b, c)| {
+ let b = B::try_from(EString::from(b)).map_err(|_| Error::Parse(1))?;
+ let c = C::try_from(EString::from(c)).map_err(|_| Error::Parse(2))?;
+ Ok(Self(a, b, c))
+ })
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::structs::SepVec;
+
+ #[test]
+ fn should_parse_into_trio() {
+ type EqTrio = Trio;
+ let estr = EString::from("hello=world=hello");
+ match estr.parse::>() {
+ Ok(res) => assert_eq!((res.0, res.1, res.2), ("hello", "world", "hello")),
+ _ => unreachable!(),
+ };
+ }
+
+ #[test]
+ fn should_parse_into_trio_with_alternate_delims() {
+ type EqTrio = Trio;
+ let estr = EString::from("hello-world^hello");
+ match estr.parse::>() {
+ Ok(res) => assert_eq!((res.0, res.1, res.2), ("hello", "world", "hello")),
+ _ => unreachable!(),
+ };
+ }
+
+ #[test]
+ fn should_parse_rest_as_trio() {
+ type EqTrio = Trio;
+ let estr = EString::from("hello=world=hello=world=hello");
+ match estr.parse::>>() {
+ Ok(res) => assert_eq!(res, Trio("hello", "world", Trio("hello", "world", "hello"))),
+ _ => unreachable!(),
+ };
+ }
+}