use crate::{
elements::{CASE_MASK, TERTIARY_MASK},
preferences::CollationCaseFirst,
preferences::CollationNumericOrdering,
CollatorPreferences,
};
#[derive(Eq, PartialEq, Debug, Copy, Clone, PartialOrd, Ord)]
#[repr(u8)]
#[non_exhaustive]
pub enum Strength {
Primary = 0,
Secondary = 1,
Tertiary = 2,
Quaternary = 3,
Identical = 7,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone, PartialOrd, Ord)]
#[repr(u8)]
#[non_exhaustive]
pub enum AlternateHandling {
NonIgnorable = 0,
Shifted = 1,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[repr(u8)] #[non_exhaustive]
pub enum MaxVariable {
Space = 0,
Punctuation = 1,
Symbol = 2,
Currency = 3,
}
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
#[repr(u8)]
#[non_exhaustive]
pub enum CaseLevel {
Off = 0,
On = 1,
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone, Default)]
pub struct CollatorOptions {
pub strength: Option<Strength>,
pub alternate_handling: Option<AlternateHandling>,
pub max_variable: Option<MaxVariable>,
pub case_level: Option<CaseLevel>,
}
impl CollatorOptions {
pub const fn default() -> Self {
Self {
strength: None,
alternate_handling: None,
max_variable: None,
case_level: None,
}
}
}
impl From<ResolvedCollatorOptions> for CollatorOptions {
fn from(options: ResolvedCollatorOptions) -> CollatorOptions {
Self {
strength: Some(options.strength),
alternate_handling: Some(options.alternate_handling),
max_variable: Some(options.max_variable),
case_level: Some(options.case_level),
}
}
}
impl From<ResolvedCollatorOptions> for CollatorPreferences {
fn from(options: ResolvedCollatorOptions) -> CollatorPreferences {
CollatorPreferences {
case_first: Some(options.case_first),
numeric_ordering: Some(options.numeric),
..Default::default()
}
}
}
#[non_exhaustive]
#[derive(Debug, Copy, Clone)]
pub struct ResolvedCollatorOptions {
pub strength: Strength,
pub alternate_handling: AlternateHandling,
pub case_first: CollationCaseFirst,
pub max_variable: MaxVariable,
pub case_level: CaseLevel,
pub numeric: CollationNumericOrdering,
}
impl From<CollatorOptionsBitField> for ResolvedCollatorOptions {
fn from(options: CollatorOptionsBitField) -> ResolvedCollatorOptions {
Self {
strength: options.strength(),
alternate_handling: options.alternate_handling(),
case_first: options.case_first(),
max_variable: options.max_variable(),
case_level: if options.case_level() {
CaseLevel::On
} else {
CaseLevel::Off
},
numeric: if options.numeric() {
CollationNumericOrdering::True
} else {
CollationNumericOrdering::False
},
}
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) struct CollatorOptionsBitField(u32);
impl Default for CollatorOptionsBitField {
fn default() -> Self {
Self::default()
}
}
impl CollatorOptionsBitField {
const STRENGTH_MASK: u32 = 0b111;
const ALTERNATE_HANDLING_MASK: u32 = 1 << 3;
const MAX_VARIABLE_MASK: u32 = 0b01100000;
const MAX_VARIABLE_SHIFT: u32 = 5;
const UPPER_FIRST_MASK: u32 = 1 << 8;
const CASE_FIRST_MASK: u32 = 1 << 9;
const CASE_LEVEL_MASK: u32 = 1 << 10;
const BACKWARD_SECOND_LEVEL_MASK: u32 = 1 << 11;
const NUMERIC_MASK: u32 = 1 << 12;
const EXPLICIT_STRENGTH_MASK: u32 = 1 << 31;
const EXPLICIT_MAX_VARIABLE_MASK: u32 = 1 << 30;
const EXPLICIT_ALTERNATE_HANDLING_MASK: u32 = 1 << 29;
const EXPLICIT_CASE_LEVEL_MASK: u32 = 1 << 28;
const EXPLICIT_CASE_FIRST_MASK: u32 = 1 << 27;
const EXPLICIT_BACKWARD_SECOND_LEVEL_MASK: u32 = 1 << 26;
const EXPLICIT_NUMERIC_MASK: u32 = 1 << 25;
pub const fn default() -> Self {
Self(Strength::Tertiary as u32)
}
pub fn strength(self) -> Strength {
let mut bits = self.0 & CollatorOptionsBitField::STRENGTH_MASK;
if !(bits <= 3 || bits == 7) {
debug_assert!(false, "Bad value for strength.");
bits = 3;
}
unsafe { core::mem::transmute(bits as u8) }
}
pub fn set_strength(&mut self, strength: Option<Strength>) {
self.0 &= !CollatorOptionsBitField::STRENGTH_MASK;
if let Some(strength) = strength {
self.0 |= CollatorOptionsBitField::EXPLICIT_STRENGTH_MASK;
self.0 |= strength as u32;
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_STRENGTH_MASK;
}
}
pub fn max_variable(self) -> MaxVariable {
unsafe {
core::mem::transmute(
((self.0 & CollatorOptionsBitField::MAX_VARIABLE_MASK)
>> CollatorOptionsBitField::MAX_VARIABLE_SHIFT) as u8,
)
}
}
pub fn set_max_variable(&mut self, max_variable: Option<MaxVariable>) {
self.0 &= !CollatorOptionsBitField::MAX_VARIABLE_MASK;
if let Some(max_variable) = max_variable {
self.0 |= CollatorOptionsBitField::EXPLICIT_MAX_VARIABLE_MASK;
self.0 |= (max_variable as u32) << CollatorOptionsBitField::MAX_VARIABLE_SHIFT;
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_MAX_VARIABLE_MASK;
}
}
pub fn alternate_handling(self) -> AlternateHandling {
if (self.0 & CollatorOptionsBitField::ALTERNATE_HANDLING_MASK) != 0 {
AlternateHandling::Shifted
} else {
AlternateHandling::NonIgnorable
}
}
pub fn set_alternate_handling(&mut self, alternate_handling: Option<AlternateHandling>) {
self.0 &= !CollatorOptionsBitField::ALTERNATE_HANDLING_MASK;
if let Some(alternate_handling) = alternate_handling {
self.0 |= CollatorOptionsBitField::EXPLICIT_ALTERNATE_HANDLING_MASK;
if alternate_handling == AlternateHandling::Shifted {
self.0 |= CollatorOptionsBitField::ALTERNATE_HANDLING_MASK;
}
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_ALTERNATE_HANDLING_MASK;
}
}
pub fn case_level(self) -> bool {
(self.0 & CollatorOptionsBitField::CASE_LEVEL_MASK) != 0
}
pub fn set_case_level(&mut self, case_level: Option<bool>) {
self.0 &= !CollatorOptionsBitField::CASE_LEVEL_MASK;
if let Some(case_level) = case_level {
self.0 |= CollatorOptionsBitField::EXPLICIT_CASE_LEVEL_MASK;
if case_level {
self.0 |= CollatorOptionsBitField::CASE_LEVEL_MASK;
}
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_CASE_LEVEL_MASK;
}
}
pub fn set_case_level_from_enum(&mut self, case_level: Option<CaseLevel>) {
match case_level {
Some(CaseLevel::On) => {
self.set_case_level(Some(true));
}
Some(CaseLevel::Off) => {
self.set_case_level(Some(false));
}
_ => self.set_case_level(None),
}
}
fn case_first(self) -> CollationCaseFirst {
if (self.0 & CollatorOptionsBitField::CASE_FIRST_MASK) != 0 {
if (self.0 & CollatorOptionsBitField::UPPER_FIRST_MASK) != 0 {
CollationCaseFirst::Upper
} else {
CollationCaseFirst::Lower
}
} else {
CollationCaseFirst::False
}
}
pub fn set_case_first(&mut self, case_first: Option<CollationCaseFirst>) {
self.0 &=
!(CollatorOptionsBitField::CASE_FIRST_MASK | CollatorOptionsBitField::UPPER_FIRST_MASK);
if let Some(case_first) = case_first {
self.0 |= CollatorOptionsBitField::EXPLICIT_CASE_FIRST_MASK;
match case_first {
CollationCaseFirst::False => {}
CollationCaseFirst::Lower => {
self.0 |= CollatorOptionsBitField::CASE_FIRST_MASK;
}
CollationCaseFirst::Upper => {
self.0 |= CollatorOptionsBitField::CASE_FIRST_MASK;
self.0 |= CollatorOptionsBitField::UPPER_FIRST_MASK;
}
_ => {
debug_assert!(false, "unknown variant `{case_first:?}`");
}
}
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_CASE_FIRST_MASK;
}
}
pub fn backward_second_level(self) -> bool {
(self.0 & CollatorOptionsBitField::BACKWARD_SECOND_LEVEL_MASK) != 0
}
pub fn set_backward_second_level(&mut self, backward_second_level: Option<bool>) {
self.0 &= !CollatorOptionsBitField::BACKWARD_SECOND_LEVEL_MASK;
if let Some(backward_second_level) = backward_second_level {
self.0 |= CollatorOptionsBitField::EXPLICIT_BACKWARD_SECOND_LEVEL_MASK;
if backward_second_level {
self.0 |= CollatorOptionsBitField::BACKWARD_SECOND_LEVEL_MASK;
}
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_BACKWARD_SECOND_LEVEL_MASK;
}
}
pub fn numeric(self) -> bool {
(self.0 & CollatorOptionsBitField::NUMERIC_MASK) != 0
}
pub fn set_numeric(&mut self, numeric: Option<bool>) {
self.0 &= !CollatorOptionsBitField::NUMERIC_MASK;
if let Some(numeric) = numeric {
self.0 |= CollatorOptionsBitField::EXPLICIT_NUMERIC_MASK;
if numeric {
self.0 |= CollatorOptionsBitField::NUMERIC_MASK;
}
} else {
self.0 &= !CollatorOptionsBitField::EXPLICIT_NUMERIC_MASK;
}
}
pub fn set_numeric_from_enum(&mut self, numeric: Option<CollationNumericOrdering>) {
match numeric {
Some(CollationNumericOrdering::True) => {
self.set_numeric(Some(true));
}
Some(CollationNumericOrdering::False) => {
self.set_numeric(Some(false));
}
Some(_) => {
debug_assert!(false, "unknown variant `{numeric:?}`");
self.set_numeric(Some(false));
}
None => self.set_numeric(None),
}
}
pub(crate) fn tertiary_mask(self) -> Option<u16> {
if self.strength() <= Strength::Secondary {
None
} else if (self.0
& (CollatorOptionsBitField::CASE_FIRST_MASK | CollatorOptionsBitField::CASE_LEVEL_MASK))
== CollatorOptionsBitField::CASE_FIRST_MASK
{
Some(CASE_MASK | TERTIARY_MASK)
} else {
Some(TERTIARY_MASK)
}
}
pub(crate) fn upper_first(self) -> bool {
(self.0 & CollatorOptionsBitField::UPPER_FIRST_MASK) != 0
}
pub fn set_defaults(&mut self, other: CollatorOptionsBitField) {
if self.0 & CollatorOptionsBitField::EXPLICIT_STRENGTH_MASK == 0 {
self.0 &= !CollatorOptionsBitField::STRENGTH_MASK;
self.0 |= other.0 & CollatorOptionsBitField::STRENGTH_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_STRENGTH_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_MAX_VARIABLE_MASK == 0 {
self.0 &= !CollatorOptionsBitField::MAX_VARIABLE_MASK;
self.0 |= other.0 & CollatorOptionsBitField::MAX_VARIABLE_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_MAX_VARIABLE_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_ALTERNATE_HANDLING_MASK == 0 {
self.0 &= !CollatorOptionsBitField::ALTERNATE_HANDLING_MASK;
self.0 |= other.0 & CollatorOptionsBitField::ALTERNATE_HANDLING_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_ALTERNATE_HANDLING_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_CASE_LEVEL_MASK == 0 {
self.0 &= !CollatorOptionsBitField::CASE_LEVEL_MASK;
self.0 |= other.0 & CollatorOptionsBitField::CASE_LEVEL_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_CASE_LEVEL_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_CASE_FIRST_MASK == 0 {
self.0 &= !(CollatorOptionsBitField::CASE_FIRST_MASK
| CollatorOptionsBitField::UPPER_FIRST_MASK);
self.0 |= other.0
& (CollatorOptionsBitField::CASE_FIRST_MASK
| CollatorOptionsBitField::UPPER_FIRST_MASK);
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_CASE_FIRST_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_BACKWARD_SECOND_LEVEL_MASK == 0 {
self.0 &= !CollatorOptionsBitField::BACKWARD_SECOND_LEVEL_MASK;
self.0 |= other.0 & CollatorOptionsBitField::BACKWARD_SECOND_LEVEL_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_BACKWARD_SECOND_LEVEL_MASK;
}
if self.0 & CollatorOptionsBitField::EXPLICIT_NUMERIC_MASK == 0 {
self.0 &= !CollatorOptionsBitField::NUMERIC_MASK;
self.0 |= other.0 & CollatorOptionsBitField::NUMERIC_MASK;
self.0 |= other.0 & CollatorOptionsBitField::EXPLICIT_NUMERIC_MASK;
}
}
}
impl From<CollatorOptions> for CollatorOptionsBitField {
fn from(options: CollatorOptions) -> CollatorOptionsBitField {
let mut result = Self::default();
result.set_strength(options.strength);
result.set_max_variable(options.max_variable);
result.set_alternate_handling(options.alternate_handling);
result.set_case_level_from_enum(options.case_level);
result
}
}