use crate::common;
use crate::context;
use crate::DerivedPropertyValue;
use crate::{CodepointInfo, Error, UnexpectedError};
pub trait SpecificDerivedPropertyValue {
fn on_spaces(&self) -> DerivedPropertyValue;
fn on_symbols(&self) -> DerivedPropertyValue;
fn on_punctuation(&self) -> DerivedPropertyValue;
fn on_has_compat(&self) -> DerivedPropertyValue;
fn on_other_letter_digits(&self) -> DerivedPropertyValue;
}
#[allow(clippy::if_same_then_else)]
fn get_derived_property_value(
cp: u32,
obj: &dyn SpecificDerivedPropertyValue,
) -> DerivedPropertyValue {
match common::get_exception_val(cp) {
Some(val) => *val,
None => match common::get_backward_compatible_val(cp) {
Some(val) => *val,
None => {
if common::is_unassigned(cp) {
DerivedPropertyValue::Unassigned
} else if common::is_ascii7(cp) {
DerivedPropertyValue::PValid
} else if common::is_join_control(cp) {
DerivedPropertyValue::ContextJ
} else if common::is_old_hangul_jamo(cp) {
DerivedPropertyValue::Disallowed
} else if common::is_precis_ignorable_property(cp) {
DerivedPropertyValue::Disallowed
} else if common::is_control(cp) {
DerivedPropertyValue::Disallowed
} else if common::has_compat(cp) {
obj.on_has_compat()
} else if common::is_letter_digit(cp) {
DerivedPropertyValue::PValid
} else if common::is_other_letter_digit(cp) {
obj.on_other_letter_digits()
} else if common::is_space(cp) {
obj.on_spaces()
} else if common::is_symbol(cp) {
obj.on_symbols()
} else if common::is_punctuation(cp) {
obj.on_punctuation()
} else {
DerivedPropertyValue::Disallowed
}
}
},
}
}
fn allowed_by_context_rule(
label: &str,
val: DerivedPropertyValue,
cp: u32,
offset: usize,
) -> Result<(), Error> {
match context::get_context_rule(cp) {
None => Err(Error::Unexpected(UnexpectedError::MissingContextRule(
CodepointInfo::new(cp, offset, val),
))),
Some(rule) => match rule(label, offset) {
Ok(allowed) => {
if allowed {
Ok(())
} else {
Err(Error::BadCodepoint(CodepointInfo::new(cp, offset, val)))
}
}
Err(e) => match e {
context::ContextRuleError::NotApplicable => Err(Error::Unexpected(
UnexpectedError::ContextRuleNotApplicable(CodepointInfo::new(cp, offset, val)),
)),
context::ContextRuleError::Undefined => {
Err(Error::Unexpected(UnexpectedError::Undefined))
}
},
},
}
}
pub trait StringClass {
fn get_value_from_char(&self, c: char) -> DerivedPropertyValue;
fn get_value_from_codepoint(&self, cp: u32) -> DerivedPropertyValue;
fn allows<S>(&self, label: S) -> Result<(), Error>
where
S: AsRef<str>,
{
for (offset, c) in label.as_ref().chars().enumerate() {
let val = self.get_value_from_char(c);
match val {
DerivedPropertyValue::PValid | DerivedPropertyValue::SpecClassPval => Ok(()),
DerivedPropertyValue::SpecClassDis
| DerivedPropertyValue::Disallowed
| DerivedPropertyValue::Unassigned => Err(Error::BadCodepoint(CodepointInfo::new(
c as u32, offset, val,
))),
DerivedPropertyValue::ContextJ | DerivedPropertyValue::ContextO => {
allowed_by_context_rule(label.as_ref(), val, c as u32, offset)
}
}?
}
Ok(())
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct IdentifierClass {}
impl SpecificDerivedPropertyValue for IdentifierClass {
fn on_has_compat(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassDis
}
fn on_other_letter_digits(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassDis
}
fn on_spaces(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassDis
}
fn on_symbols(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassDis
}
fn on_punctuation(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassDis
}
}
impl StringClass for IdentifierClass {
fn get_value_from_char(&self, c: char) -> DerivedPropertyValue {
get_derived_property_value(c as u32, self)
}
fn get_value_from_codepoint(&self, cp: u32) -> DerivedPropertyValue {
get_derived_property_value(cp, self)
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
pub struct FreeformClass {}
impl SpecificDerivedPropertyValue for FreeformClass {
fn on_has_compat(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassPval
}
fn on_other_letter_digits(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassPval
}
fn on_spaces(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassPval
}
fn on_symbols(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassPval
}
fn on_punctuation(&self) -> DerivedPropertyValue {
DerivedPropertyValue::SpecClassPval
}
}
impl StringClass for FreeformClass {
fn get_value_from_char(&self, c: char) -> DerivedPropertyValue {
get_derived_property_value(c as u32, self)
}
fn get_value_from_codepoint(&self, cp: u32) -> DerivedPropertyValue {
get_derived_property_value(cp, self)
}
}
#[cfg(test)]
mod test_string_classes {
use super::*;
pub struct TestClass {}
impl StringClass for TestClass {
fn get_value_from_char(&self, c: char) -> DerivedPropertyValue {
self.get_value_from_codepoint(c as u32)
}
fn get_value_from_codepoint(&self, cp: u32) -> DerivedPropertyValue {
match cp {
0x0061 => DerivedPropertyValue::PValid, 0x0062 => DerivedPropertyValue::SpecClassPval, 0x0063 => DerivedPropertyValue::SpecClassDis, 0x0064 => DerivedPropertyValue::ContextJ, 0x0065 => DerivedPropertyValue::ContextO, 0x0066 => DerivedPropertyValue::Disallowed, 0x006c => DerivedPropertyValue::PValid, 0x200d => DerivedPropertyValue::ContextJ, 0x094d => DerivedPropertyValue::PValid, 0x00b7 => DerivedPropertyValue::ContextO, _ => DerivedPropertyValue::Unassigned,
}
}
}
#[test]
fn test_allows_code_point() {
let id = TestClass {};
assert_eq!(id.allows("\u{61}"), Ok(()));
assert_eq!(id.allows("\u{62}"), Ok(()));
assert_eq!(
id.allows("\u{63}"),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x63,
position: 0,
property: DerivedPropertyValue::SpecClassDis
}))
);
assert_eq!(
id.allows("\u{0066}"),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x66,
position: 0,
property: DerivedPropertyValue::Disallowed
}))
);
assert_eq!(
id.allows("\u{67}"),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x67,
position: 0,
property: DerivedPropertyValue::Unassigned
}))
);
assert_eq!(
id.allows("\u{64}"),
Err(Error::Unexpected(UnexpectedError::MissingContextRule(
CodepointInfo {
cp: 0x64,
position: 0,
property: DerivedPropertyValue::ContextJ
}
)))
);
assert_eq!(
id.allows("a\u{200d}"),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x200d,
position: 1,
property: DerivedPropertyValue::ContextJ
}))
);
assert_eq!(
id.allows("\u{200d}"),
Err(Error::Unexpected(UnexpectedError::Undefined))
);
assert_eq!(id.allows("\u{94d}\u{200d}"), Ok(()));
assert_eq!(
id.allows("\u{65}"),
Err(Error::Unexpected(UnexpectedError::MissingContextRule(
CodepointInfo {
cp: 0x65,
position: 0,
property: DerivedPropertyValue::ContextO
}
)))
);
assert_eq!(
id.allows("a\u{00b7}b"),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x00b7,
position: 1,
property: DerivedPropertyValue::ContextO
}))
);
assert_eq!(
id.allows("\u{00b7}"),
Err(Error::Unexpected(UnexpectedError::Undefined))
);
assert_eq!(id.allows("\u{006c}\u{00b7}\u{006c}"), Ok(()));
}
#[test]
fn test_allowed_by_context_rule() {
assert_eq!(
allowed_by_context_rule("test", DerivedPropertyValue::ContextO, 0xffff, 0),
Err(Error::Unexpected(UnexpectedError::MissingContextRule(
CodepointInfo {
cp: 0xffff,
position: 0,
property: DerivedPropertyValue::ContextO
}
)))
);
assert_eq!(
allowed_by_context_rule(
"\u{006c}\u{00b7}\u{006c}",
DerivedPropertyValue::ContextO,
0x00b7,
1
),
Ok(())
);
assert_eq!(
allowed_by_context_rule(
"\u{006c}\u{00b7}a",
DerivedPropertyValue::ContextO,
0x00b7,
1
),
Err(Error::BadCodepoint(CodepointInfo {
cp: 0x00b7,
position: 1,
property: DerivedPropertyValue::ContextO
}))
);
assert_eq!(
allowed_by_context_rule("\u{00b7}", DerivedPropertyValue::ContextO, 0x00b7, 0),
Err(Error::Unexpected(UnexpectedError::Undefined))
);
assert_eq!(
allowed_by_context_rule("\u{0066}", DerivedPropertyValue::ContextO, 0x00b7, 0),
Err(Error::Unexpected(
UnexpectedError::ContextRuleNotApplicable(CodepointInfo {
cp: 0x00b7,
position: 0,
property: DerivedPropertyValue::ContextO
})
))
);
}
}