use std::fmt;
use std::str::FromStr;
use thiserror::Error;
#[derive(Debug, Clone, PartialEq, Eq, Error)]
#[error(
"unknown convention `{0}`; valid values: \
`snake_case`, `CamelCase`, `camelCase`, `SCREAMING_SNAKE_CASE`, `kebab-case`"
)]
pub struct UnknownConvention(pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Convention {
SnakeCase,
CamelCase,
LowerCamelCase,
ScreamingSnakeCase,
KebabCase,
}
impl Convention {
#[inline]
#[must_use]
pub const fn as_str(&self) -> &'static str {
match self {
Self::SnakeCase => "snake_case",
Self::CamelCase => "CamelCase",
Self::LowerCamelCase => "camelCase",
Self::ScreamingSnakeCase => "SCREAMING_SNAKE_CASE",
Self::KebabCase => "kebab-case",
}
}
#[must_use]
pub fn is_valid(&self, stem: &str) -> bool {
let Some(first) = stem.chars().next() else {
return false;
};
match self {
Self::SnakeCase => {
first.is_ascii_lowercase()
&& stem
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_')
&& !stem.contains("__")
&& !stem.ends_with('_')
}
Self::CamelCase => {
first.is_ascii_uppercase() && stem.chars().all(|c| c.is_ascii_alphanumeric())
}
Self::LowerCamelCase => {
first.is_ascii_lowercase() && stem.chars().all(|c| c.is_ascii_alphanumeric())
}
Self::ScreamingSnakeCase => {
first.is_ascii_uppercase()
&& stem
.chars()
.all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
&& !stem.contains("__")
&& !stem.ends_with('_')
}
Self::KebabCase => {
first.is_ascii_lowercase()
&& stem
.chars()
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
&& !stem.contains("--")
&& !stem.ends_with('-')
}
}
}
}
impl FromStr for Convention {
type Err = UnknownConvention;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"snake_case" => Ok(Self::SnakeCase),
"CamelCase" | "PascalCase" => Ok(Self::CamelCase),
"camelCase" => Ok(Self::LowerCamelCase),
"SCREAMING_SNAKE_CASE" => Ok(Self::ScreamingSnakeCase),
"kebab-case" => Ok(Self::KebabCase),
other => Err(UnknownConvention(other.to_owned())),
}
}
}
impl fmt::Display for Convention {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}