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!(), + }; + } +}