use snafu::prelude::*;
use std::convert::{Infallible, TryFrom};
use std::fmt::{Debug, Display, Formatter};
use std::str::FromStr;
use strum_macros::{Display, VariantNames};
#[cfg(feature = "pythonmodule")]
use pyo3::prelude::*;
#[cfg(feature = "wasm")]
use wasm_bindgen::prelude::*;
#[derive(Snafu)]
#[snafu(visibility(pub))]
pub enum Error {
#[snafu(display("Invalid input language '{input}'"))]
Locale { input: String },
#[snafu(display("Invalid target case '{input}'"))]
Case { input: String },
#[snafu(display("Invalid preferred style guide '{input}'"))]
StyleGuide { input: String },
#[snafu(display("Invalid style options '{input}'"))]
StyleOptions { input: String },
}
impl Debug for Error {
fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
Display::fmt(self, fmt)
}
}
impl From<Infallible> for Error {
fn from(_: Infallible) -> Self {
unreachable!()
}
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "pythonmodule", pyclass(eq))]
#[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
pub struct Word {
pub word: String,
}
#[derive(Default, Display, VariantNames, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "pythonmodule", pyclass(eq))]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[strum(serialize_all = "lowercase")]
#[non_exhaustive]
pub enum Locale {
#[default]
EN,
TR,
ES,
}
#[derive(Default, Display, VariantNames, Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "pythonmodule", pyclass(eq))]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[strum(serialize_all = "lowercase")]
#[non_exhaustive]
pub enum Case {
Lower,
Sentence,
#[default]
Title,
Upper,
}
#[derive(Display, VariantNames, Debug, Clone, PartialEq)]
#[cfg_attr(feature = "pythonmodule", pyclass(eq))]
#[cfg_attr(feature = "wasm", wasm_bindgen)]
#[strum(serialize_all = "lowercase")]
#[non_exhaustive]
#[derive(Default)]
pub enum StyleGuide {
#[strum(serialize = "ap")]
AssociatedPress,
#[strum(serialize = "cmos")]
ChicagoManualOfStyle,
#[strum(serialize = "gruber")]
DaringFireball,
#[strum(serialize = "default")]
#[default]
LanguageDefault,
#[strum(serialize = "tdk")]
TurkishLanguageInstitute,
#[strum(serialize = "rae")]
RealAcademiaEspanola,
#[strum(serialize = "fundeu")]
FundeuRealAcademiaEspanola,
}
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "pythonmodule", pyclass(eq))]
#[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
pub struct StyleOptions {
pub overrides: Option<Vec<Word>>,
}
impl FromStr for StyleOptions {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_ascii_lowercase().as_str() {
"default" | "none" | "" => Ok(StyleOptions::default()),
input => StyleOptionsSnafu { input }.fail()?,
}
}
}
impl TryFrom<&str> for StyleOptions {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<String> for StyleOptions {
type Error = Error;
fn try_from(s: String) -> Result<Self> {
Self::from_str(&s)
}
}
impl TryFrom<&String> for StyleOptions {
type Error = Error;
fn try_from(s: &String) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<&[u8]> for StyleOptions {
type Error = Error;
fn try_from(s: &[u8]) -> Result<Self> {
let s = String::from_utf8_lossy(s);
Self::from_str(&s)
}
}
#[derive(Debug)]
pub struct StyleOptionsBuilder {
overrides: Option<Vec<Word>>,
}
impl Default for StyleOptionsBuilder {
fn default() -> Self {
Self::new()
}
}
impl StyleOptionsBuilder {
pub fn new() -> Self {
Self { overrides: None }
}
pub fn overrides(mut self, words: Vec<impl Into<Word>>) -> Self {
let words: Vec<Word> = words.into_iter().map(|w| w.into()).collect();
self.overrides = Some(words);
self
}
pub fn build(self) -> StyleOptions {
StyleOptions {
overrides: self.overrides,
}
}
}
impl FromStr for Locale {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_ascii_lowercase().as_str() {
"en" | "english" | "en_en" => Ok(Locale::EN),
"es" | "spanish" | "es_es" | "espanol" | "español" => Ok(Locale::ES),
"tr" | "turkish" | "tr_tr" | "turkce" | "türkçe" => Ok(Locale::TR),
input => LocaleSnafu { input }.fail()?,
}
}
}
impl TryFrom<&str> for Locale {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<String> for Locale {
type Error = Error;
fn try_from(s: String) -> Result<Self> {
Self::from_str(&s)
}
}
impl TryFrom<&String> for Locale {
type Error = Error;
fn try_from(s: &String) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<&[u8]> for Locale {
type Error = Error;
fn try_from(s: &[u8]) -> Result<Self> {
let s = String::from_utf8_lossy(s);
Self::from_str(&s)
}
}
impl FromStr for Case {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_ascii_lowercase().as_str().trim_end_matches("case") {
"lower" => Ok(Case::Lower),
"sentence" => Ok(Case::Sentence),
"title" => Ok(Case::Title),
"upper" => Ok(Case::Upper),
input => CaseSnafu { input }.fail()?,
}
}
}
impl TryFrom<&str> for Case {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<String> for Case {
type Error = Error;
fn try_from(s: String) -> Result<Self> {
Self::from_str(&s)
}
}
impl TryFrom<&String> for Case {
type Error = Error;
fn try_from(s: &String) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<&[u8]> for Case {
type Error = Error;
fn try_from(s: &[u8]) -> Result<Self> {
let s = String::from_utf8_lossy(s);
Self::from_str(&s)
}
}
impl FromStr for StyleGuide {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
match s.to_ascii_lowercase().as_str() {
"daringfireball" | "gruber" | "fireball" => Ok(StyleGuide::DaringFireball),
"associatedpress" | "ap" => Ok(StyleGuide::AssociatedPress),
"chicagoManualofstyle" | "chicago" | "cmos" => Ok(StyleGuide::ChicagoManualOfStyle),
"fundeu" | "fundeurealacademiaespanola" => Ok(StyleGuide::FundeuRealAcademiaEspanola),
"rae" | "realacademiaespanola" => Ok(StyleGuide::RealAcademiaEspanola),
"tdk" | "turkishlanguageinstitute" => Ok(StyleGuide::TurkishLanguageInstitute),
"default" | "languagedefault" | "language" | "none" | "" => {
Ok(StyleGuide::LanguageDefault)
}
input => StyleGuideSnafu { input }.fail()?,
}
}
}
impl TryFrom<&str> for StyleGuide {
type Error = Error;
fn try_from(s: &str) -> Result<Self> {
Self::from_str(s)
}
}
impl TryFrom<String> for StyleGuide {
type Error = Error;
fn try_from(s: String) -> Result<Self> {
Self::from_str(&s)
}
}
impl TryFrom<&String> for StyleGuide {
type Error = Error;
fn try_from(s: &String) -> Result<Self> {
Self::from_str(s)
}
}
impl From<Option<StyleGuide>> for StyleGuide {
fn from(style: Option<StyleGuide>) -> Self {
match style {
Some(style) => style,
None => StyleGuide::LanguageDefault,
}
}
}
impl TryFrom<&[u8]> for StyleGuide {
type Error = Error;
fn try_from(s: &[u8]) -> Result<Self> {
let s = String::from_utf8_lossy(s);
Self::from_str(&s)
}
}