use std::fmt;
use std::path::Path;
use std::str::FromStr;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Dialect {
Json,
IJson,
Jsonc,
Jwcc,
Json5,
JsonLines,
}
impl Dialect {
pub const ALL: [Dialect; 6] = [
Dialect::Json,
Dialect::IJson,
Dialect::Jsonc,
Dialect::Jwcc,
Dialect::Json5,
Dialect::JsonLines,
];
pub fn name(self) -> &'static str {
match self {
Dialect::Json => "json",
Dialect::IJson => "i-json",
Dialect::Jsonc => "jsonc",
Dialect::Jwcc => "jwcc",
Dialect::Json5 => "json5",
Dialect::JsonLines => "jsonl",
}
}
pub fn for_path(path: &Path) -> Option<Dialect> {
let name = path.file_name()?.to_str()?;
if name == ".babelrc" {
return Some(Dialect::Json5);
}
let (_, ext) = name.rsplit_once('.')?;
match ext {
"json" | "json5" | "geojson" | "jsonld" | "webmanifest" => Some(Dialect::Json5),
"jsonc" | "code-workspace" | "code-snippets" => Some(Dialect::Jsonc),
"hujson" | "jwcc" => Some(Dialect::Jwcc),
"jsonl" | "ndjson" => Some(Dialect::JsonLines),
_ => None,
}
}
pub(crate) fn comments(self) -> bool {
matches!(self, Dialect::Jsonc | Dialect::Jwcc | Dialect::Json5)
}
pub(crate) fn trailing_commas(self) -> bool {
matches!(self, Dialect::Jwcc | Dialect::Json5)
}
pub(crate) fn json5(self) -> bool {
self == Dialect::Json5
}
}
impl fmt::Display for Dialect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnknownDialect(pub String);
impl fmt::Display for UnknownDialect {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let names: Vec<&str> = Dialect::ALL.iter().map(|d| d.name()).collect();
write!(
f,
"unknown dialect `{}`; expected one of {}",
self.0,
names.join(", ")
)
}
}
impl std::error::Error for UnknownDialect {}
impl FromStr for Dialect {
type Err = UnknownDialect;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"ndjson" | "json-lines" => Ok(Dialect::JsonLines),
"hujson" => Ok(Dialect::Jwcc),
"ijson" => Ok(Dialect::IJson),
_ => Dialect::ALL
.into_iter()
.find(|d| d.name() == s)
.ok_or_else(|| UnknownDialect(s.to_owned())),
}
}
}