Skip to main content

wdl_format/config/
quotes.rs

1//! Quote style configuration.
2
3use std::fmt;
4use std::str::FromStr;
5
6use schemars::JsonSchema;
7use thiserror::Error;
8
9/// Double quote literal.
10const DOUBLE_QUOTE: &str = "\"";
11/// Single quote literal.
12const SINGLE_QUOTE: &str = "'";
13
14/// The quote style to use when formatting.
15#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, JsonSchema)]
16#[schemars(rename_all = "lowercase")]
17pub enum QuoteStyle {
18    /// Use double quotes for all literal strings.
19    Double,
20    /// Use single quotes for all literal strings.
21    Single,
22    /// Preserve the quote style found in the input.
23    #[default]
24    Preserve,
25}
26
27impl fmt::Display for QuoteStyle {
28    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29        match self {
30            Self::Double => f.write_str("double"),
31            Self::Single => f.write_str("single"),
32            Self::Preserve => f.write_str("preserve"),
33        }
34    }
35}
36
37/// An error returned when parsing an invalid [`QuoteStyle`] string.
38#[derive(Clone, Debug, Eq, Error, PartialEq)]
39#[error("invalid quote style `{0}`; expected one of: `double`, `single`, `preserve`")]
40pub struct ParseQuoteStyleError(String);
41
42impl FromStr for QuoteStyle {
43    type Err = ParseQuoteStyleError;
44
45    fn from_str(s: &str) -> Result<Self, Self::Err> {
46        match s.to_ascii_lowercase().as_str() {
47            "double" => Ok(Self::Double),
48            "single" => Ok(Self::Single),
49            "preserve" => Ok(Self::Preserve),
50            _ => Err(ParseQuoteStyleError(s.to_string())),
51        }
52    }
53}
54
55impl QuoteStyle {
56    /// Gets the quote string for this style. Returns `None` if style is
57    /// [`QuoteStyle::Preserve`].
58    pub fn as_str(&self) -> Option<&str> {
59        match self {
60            QuoteStyle::Preserve => None,
61            QuoteStyle::Double => Some(DOUBLE_QUOTE),
62            QuoteStyle::Single => Some(SINGLE_QUOTE),
63        }
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::*;
70
71    #[test]
72    fn test_double_quote() {
73        assert_eq!(QuoteStyle::Double.as_str(), Some("\""));
74    }
75
76    #[test]
77    fn test_single_quote() {
78        assert_eq!(QuoteStyle::Single.as_str(), Some("'"));
79    }
80
81    #[test]
82    fn test_preserve_quote() {
83        assert_eq!(QuoteStyle::Preserve.as_str(), None);
84    }
85
86    #[test]
87    fn test_default_is_preserve() {
88        assert!(matches!(QuoteStyle::default(), QuoteStyle::Preserve));
89    }
90
91    #[test]
92    fn from_str_accepts_valid_values() {
93        assert_eq!(
94            "preserve".parse::<QuoteStyle>().unwrap(),
95            QuoteStyle::Preserve
96        );
97        assert_eq!("double".parse::<QuoteStyle>().unwrap(), QuoteStyle::Double);
98        assert_eq!("single".parse::<QuoteStyle>().unwrap(), QuoteStyle::Single);
99    }
100
101    #[test]
102    fn from_str_rejects_invalid_value() {
103        let err = "bad".parse::<QuoteStyle>().unwrap_err();
104        assert_eq!(
105            err.to_string(),
106            "invalid quote style `bad`; expected one of: `double`, `single`, `preserve`"
107        );
108    }
109
110    #[test]
111    fn display_round_trips_through_from_str() {
112        for style in [QuoteStyle::Preserve, QuoteStyle::Double, QuoteStyle::Single] {
113            assert_eq!(style.to_string().parse::<QuoteStyle>().unwrap(), style);
114        }
115    }
116}