use std::convert::TryFrom;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
use crate::error::{Error, ErrorKind};
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Default, Serialize, Deserialize, TypedBuilder)]
#[serde(rename_all = "camelCase")]
#[builder(field_defaults(setter(into)))]
#[non_exhaustive]
pub struct Collation {
pub locale: String,
#[builder(default)]
pub strength: Option<CollationStrength>,
#[builder(default)]
pub case_level: Option<bool>,
#[builder(default)]
pub case_first: Option<CollationCaseFirst>,
#[builder(default)]
pub numeric_ordering: Option<bool>,
#[builder(default)]
pub alternate: Option<CollationAlternate>,
#[builder(default)]
pub max_variable: Option<CollationMaxVariable>,
#[builder(default)]
pub normalization: Option<bool>,
#[builder(default)]
pub backwards: Option<bool>,
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum CollationStrength {
Primary,
Secondary,
Tertiary,
Quaternary,
Identical,
}
impl From<CollationStrength> for u32 {
fn from(strength: CollationStrength) -> Self {
match strength {
CollationStrength::Primary => 1,
CollationStrength::Secondary => 2,
CollationStrength::Tertiary => 3,
CollationStrength::Quaternary => 4,
CollationStrength::Identical => 5,
}
}
}
impl TryFrom<u32> for CollationStrength {
type Error = Error;
fn try_from(level: u32) -> Result<Self, Self::Error> {
Ok(match level {
1 => CollationStrength::Primary,
2 => CollationStrength::Secondary,
3 => CollationStrength::Tertiary,
4 => CollationStrength::Quaternary,
5 => CollationStrength::Identical,
_ => {
return Err(ErrorKind::InvalidArgument {
message: (format!("invalid collation strength: {}", level)),
}
.into())
}
})
}
}
impl Serialize for CollationStrength {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let level = u32::from(*self);
serializer.serialize_i32(level as i32)
}
}
impl<'de> Deserialize<'de> for CollationStrength {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let level = u32::deserialize(deserializer)?;
Self::try_from(level).map_err(serde::de::Error::custom)
}
}
impl std::fmt::Display for CollationStrength {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&u32::from(*self), f)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum CollationCaseFirst {
Upper,
Lower,
Off,
}
impl std::str::FromStr for CollationCaseFirst {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"upper" => Ok(CollationCaseFirst::Upper),
"lower" => Ok(CollationCaseFirst::Lower),
"off" => Ok(CollationCaseFirst::Off),
_ => Err(ErrorKind::InvalidArgument {
message: format!("invalid CollationCaseFirst: {}", s),
}
.into()),
}
}
}
impl CollationCaseFirst {
pub fn as_str(&self) -> &'static str {
match self {
CollationCaseFirst::Upper => "upper",
CollationCaseFirst::Lower => "lower",
CollationCaseFirst::Off => "off",
}
}
}
impl std::fmt::Display for CollationCaseFirst {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum CollationAlternate {
NonIgnorable,
Shifted,
}
impl std::str::FromStr for CollationAlternate {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"non-ignorable" => Ok(CollationAlternate::NonIgnorable),
"shifted" => Ok(CollationAlternate::Shifted),
_ => Err(ErrorKind::InvalidArgument {
message: format!("invalid collation alternate: {}", s),
}
.into()),
}
}
}
impl CollationAlternate {
pub fn as_str(&self) -> &'static str {
match self {
CollationAlternate::NonIgnorable => "non-ignorable",
CollationAlternate::Shifted => "shifted",
}
}
}
impl std::fmt::Display for CollationAlternate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[non_exhaustive]
pub enum CollationMaxVariable {
Punct,
Space,
}
impl std::str::FromStr for CollationMaxVariable {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"punct" => Ok(CollationMaxVariable::Punct),
"space" => Ok(CollationMaxVariable::Space),
_ => Err(ErrorKind::InvalidArgument {
message: format!("invalid collation max variable: {}", s),
}
.into()),
}
}
}
impl CollationMaxVariable {
pub fn as_str(&self) -> &'static str {
match self {
CollationMaxVariable::Punct => "punct",
CollationMaxVariable::Space => "space",
}
}
}
impl std::fmt::Display for CollationMaxVariable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(self.as_str(), f)
}
}