use std::fmt;
use std::str::FromStr;
use thiserror::Error;
const UNIX_NEWLINE: &str = "\n";
const WINDOWS_NEWLINE: &str = "\r\n";
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum NewlineStyle {
#[default]
Auto,
Unix,
Windows,
}
impl fmt::Display for NewlineStyle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Auto => f.write_str("auto"),
Self::Unix => f.write_str("unix"),
Self::Windows => f.write_str("windows"),
}
}
}
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[error("invalid newline style `{0}`; expected one of: `auto`, `unix`, `windows`")]
pub struct ParseNewlineStyleError(String);
impl FromStr for NewlineStyle {
type Err = ParseNewlineStyleError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &*s.to_ascii_lowercase() {
"auto" => Ok(Self::Auto),
"unix" => Ok(Self::Unix),
"windows" => Ok(Self::Windows),
_ => Err(ParseNewlineStyleError(s.to_string())),
}
}
}
impl NewlineStyle {
pub fn as_str(&self) -> &str {
match self {
NewlineStyle::Auto => {
if cfg!(windows) {
WINDOWS_NEWLINE
} else {
UNIX_NEWLINE
}
}
NewlineStyle::Unix => UNIX_NEWLINE,
NewlineStyle::Windows => WINDOWS_NEWLINE,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unix_newline() {
assert_eq!(NewlineStyle::Unix.as_str(), "\n");
}
#[test]
fn test_windows_newline() {
assert_eq!(NewlineStyle::Windows.as_str(), "\r\n");
}
#[test]
fn test_auto_newline() {
let newline = NewlineStyle::Auto.as_str();
assert!(newline == "\n" || newline == "\r\n");
}
#[test]
fn test_default_is_auto() {
assert!(matches!(NewlineStyle::default(), NewlineStyle::Auto));
}
#[test]
fn from_str_accepts_valid_values() {
assert_eq!("auto".parse::<NewlineStyle>().unwrap(), NewlineStyle::Auto);
assert_eq!("unix".parse::<NewlineStyle>().unwrap(), NewlineStyle::Unix);
assert_eq!(
"windows".parse::<NewlineStyle>().unwrap(),
NewlineStyle::Windows
);
assert_eq!("AUTO".parse::<NewlineStyle>().unwrap(), NewlineStyle::Auto);
}
#[test]
fn from_str_rejects_invalid_value() {
let err = "bad".parse::<NewlineStyle>().unwrap_err();
assert_eq!(
err.to_string(),
"invalid newline style `bad`; expected one of: `auto`, `unix`, `windows`"
);
}
#[test]
fn display_round_trips_through_from_str() {
for style in [
NewlineStyle::Auto,
NewlineStyle::Unix,
NewlineStyle::Windows,
] {
assert_eq!(style.to_string().parse::<NewlineStyle>().unwrap(), style);
}
}
}