use std::fmt;
use std::str::FromStr;
use nom::{
IResult, Parser,
character::complete::{char, digit1},
combinator::all_consuming,
sequence::preceded,
};
use serde::{Deserialize, Serialize};
use crate::error::{Error, map_add_intent};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PaneId(pub String);
impl FromStr for PaneId {
type Err = Error;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let desc = "PaneId";
let intent = "##{pane_id}";
let (_, pane_id) = all_consuming(parse::pane_id)
.parse(input)
.map_err(|e| map_add_intent(desc, intent, e))?;
Ok(pane_id)
}
}
impl From<&u16> for PaneId {
fn from(value: &u16) -> Self {
Self(format!("%{value}"))
}
}
impl PaneId {
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for PaneId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
pub(crate) mod parse {
use super::{IResult, PaneId, Parser, char, digit1, preceded};
pub(crate) fn pane_id(input: &str) -> IResult<&str, PaneId> {
let (input, digit) = preceded(char('%'), digit1).parse(input)?;
let id = format!("%{digit}");
Ok((input, PaneId(id)))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_pane_id_fn() {
let actual = parse::pane_id("%43");
let expected = Ok(("", PaneId("%43".into())));
assert_eq!(actual, expected);
let actual = parse::pane_id("%4");
let expected = Ok(("", PaneId("%4".into())));
assert_eq!(actual, expected);
}
#[test]
fn test_parse_pane_id_struct() {
let actual = PaneId::from_str("%43");
assert!(actual.is_ok());
assert_eq!(actual.unwrap(), PaneId("%43".into()));
let actual = PaneId::from_str("4:38");
assert!(matches!(
actual,
Err(Error::ParseError {
desc: "PaneId",
intent: "##{pane_id}",
err: _
})
));
}
#[test]
fn test_parse_pane_id_with_large_number() {
let pane_id = PaneId::from_str("%99999").unwrap();
assert_eq!(pane_id.as_str(), "%99999");
}
#[test]
fn test_parse_pane_id_fails_on_wrong_prefix() {
assert!(PaneId::from_str("@1").is_err());
assert!(PaneId::from_str("$1").is_err());
}
#[test]
fn test_parse_pane_id_fails_on_no_prefix() {
assert!(PaneId::from_str("123").is_err());
}
#[test]
fn test_parse_pane_id_fails_on_empty() {
assert!(PaneId::from_str("").is_err());
assert!(PaneId::from_str("%").is_err());
}
#[test]
fn test_parse_pane_id_fails_on_non_numeric() {
assert!(PaneId::from_str("%abc").is_err());
assert!(PaneId::from_str("%12abc").is_err());
}
#[test]
fn test_parse_pane_id_fails_on_extra_content() {
assert!(PaneId::from_str("%12:extra").is_err());
}
#[test]
fn test_pane_id_as_str() {
let pane_id = PaneId::from_str("%42").unwrap();
assert_eq!(pane_id.as_str(), "%42");
}
#[test]
fn test_pane_id_display() {
let pane_id = PaneId::from_str("%42").unwrap();
assert_eq!(format!("{}", pane_id), "%42");
}
#[test]
fn test_pane_id_from_u16() {
let pane_id = PaneId::from(&42u16);
assert_eq!(pane_id.as_str(), "%42");
}
#[test]
fn test_pane_id_from_u16_zero() {
let pane_id = PaneId::from(&0u16);
assert_eq!(pane_id.as_str(), "%0");
}
}