use std::fmt;
use std::ffi::OsStr;
use std::path::Path;
use std::str::FromStr;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Format {
Toml,
Json,
Yaml,
Ron,
Xml,
Url,
}
#[derive(Debug, Fail)]
#[fail(display = "Unknown format name {}", _0)]
pub struct UnknownFormatStringError(String);
impl fmt::Display for Format {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl Format {
pub fn is_supported(&self) -> bool {
match self {
Format::Toml => cfg!(feature = "toml"),
Format::Json => cfg!(feature = "json"),
Format::Yaml => cfg!(feature = "yaml"),
Format::Ron => cfg!(feature = "ron"),
Format::Xml => cfg!(feature = "xml"),
Format::Url => cfg!(feature = "url"),
}
}
}
impl FromStr for Format {
type Err = UnknownFormatStringError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match &s.to_lowercase()[..] {
"toml" => Ok(Format::Toml),
"json" => Ok(Format::Json),
"yaml" => Ok(Format::Yaml),
"ron" => Ok(Format::Ron),
"xml" => Ok(Format::Xml),
"url" => Ok(Format::Url),
s => Err(UnknownFormatStringError(s.to_string())),
}
}
}
pub fn supported_formats() -> Vec<Format> {
let mut f = Vec::new();
#[cfg(feature = "toml")]
f.push(Format::Toml);
#[cfg(feature = "json")]
f.push(Format::Json);
#[cfg(feature = "yaml")]
f.push(Format::Yaml);
#[cfg(feature = "ron")]
f.push(Format::Ron);
#[cfg(feature = "xml")]
f.push(Format::Xml);
#[cfg(feature = "url")]
f.push(Format::Url);
f
}
pub fn supported_extensions() -> Vec<&'static str> {
let mut e = Vec::new();
#[cfg(feature = "toml")]
e.push("toml");
#[cfg(feature = "json")]
e.push("json");
#[cfg(feature = "yaml")]
{
e.push("yml");
e.push("yaml");
}
#[cfg(feature = "ron")]
e.push("ron");
#[cfg(feature = "xml")]
e.push("xml");
e
}
pub fn guess_format<P>(path: P) -> Option<Format>
where
P: AsRef<Path>,
{
path.as_ref()
.extension()
.and_then(OsStr::to_str)
.and_then(guess_format_from_extension)
}
pub fn guess_format_from_extension(ext: &str) -> Option<Format> {
match ext {
"yml" | "yaml" => Some(Format::Yaml),
"json" => Some(Format::Json),
"toml" => Some(Format::Toml),
"ron" => Some(Format::Ron),
"xml" => Some(Format::Xml),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn extensions() {
for ext in supported_extensions() {
let stem = Path::new("test");
let from_ext = guess_format_from_extension(ext);
let from_path = guess_format(stem.with_extension(ext));
assert!(from_ext.is_some());
assert!(from_path.is_some());
assert_eq!(from_ext, from_path);
}
}
#[test]
fn display_format() {
let formats = vec![
(Format::Json, "Json"),
(Format::Toml, "Toml"),
(Format::Yaml, "Yaml"),
(Format::Ron, "Ron"),
(Format::Xml, "Xml"),
(Format::Url, "Url"),
];
for (f, n) in formats {
let d = format!("{}", f);
assert_eq!(&d, n);
}
}
#[test]
fn parse_format() {
let formats = vec![
(Format::Json, "Json"),
(Format::Toml, "Toml"),
(Format::Yaml, "Yaml"),
(Format::Ron, "Ron"),
(Format::Xml, "Xml"),
(Format::Url, "Url"),
];
for (f, n) in formats {
let parsed_format: Format = n.parse().unwrap();
assert_eq!(parsed_format, f);
}
}
#[test]
fn parse_format_invalid() {
let invalid_format_strings = vec!["", "j", "a", "hobbit", "josn", "yoml", "yml"];
for s in invalid_format_strings {
let p = s.parse::<Format>();
assert!(p.is_err());
}
}
}