1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::DerivedPropertyValue;
use std::fmt;

/// Represents any kind of error that may happen when
/// preparing, enforcing or comparing internationalized
/// strings
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
    /// Invalid label
    Invalid,
    /// Detected a disallowed Unicode code pint in the label.
    /// [`CodepointInfo`] contains information about the code point.
    BadCodepoint(CodepointInfo),
    /// Error used to deal with any unexpected condition not directly
    /// covered by any other category.
    Unexpected(UnexpectedError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::Invalid => write!(f, "invalid label"),
            Error::BadCodepoint(info) => write!(f, "bad codepoint: {}", info),
            Error::Unexpected(unexpected) => write!(f, "unexpected: {}", unexpected),
        }
    }
}

impl std::error::Error for Error {}

/// Error that contains information regarding the wrong Unicode code point
#[derive(Debug, PartialEq, Eq)]
pub struct CodepointInfo {
    /// Unicode code point
    pub cp: u32,
    /// The position of the Unicode code point in the label
    pub position: usize,
    /// The derived property value
    pub property: DerivedPropertyValue,
}

impl CodepointInfo {
    /// Creates a new `CodepointInfo` `struct`
    pub fn new(cp: u32, position: usize, property: DerivedPropertyValue) -> Self {
        Self {
            cp,
            position,
            property,
        }
    }
}

impl fmt::Display for CodepointInfo {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "code point {:#06x}, position: {}, property: {}",
            self.cp, self.position, self.property
        )
    }
}

/// Internal errors that group unusual error conditions that mostly
/// have to do with the processing of wrong labels, unexpected Unicode
/// code points if tested against another version defined in PRECIS, etc.
#[derive(Debug, PartialEq, Eq)]
pub enum UnexpectedError {
    /// Error caused when trying to apply a context rule over
    /// an invalid code point.
    ContextRuleNotApplicable(CodepointInfo),
    /// The code point requires a context rule that is not implemented.
    /// [`CodepointInfo`] contains information about the code point.
    MissingContextRule(CodepointInfo),
    /// Error caused when trying to apply a context rule that is not defined
    /// by the PRECIS profile.
    ProfileRuleNotApplicable,
    /// Unexpected error condition such as an attempt to access to a character before
    /// the start of a label or after the end of a label.
    Undefined,
}

impl fmt::Display for UnexpectedError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            UnexpectedError::ContextRuleNotApplicable(info) => {
                write!(f, "context rule not applicable [{}]", info)
            }
            UnexpectedError::MissingContextRule(info) => {
                write!(f, "missing context rule [{}]", info)
            }
            UnexpectedError::ProfileRuleNotApplicable => write!(f, "profile rule not appplicable"),
            UnexpectedError::Undefined => write!(f, "undefined"),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn fmt_error() {
        format!("{}", Error::Invalid);
        format!(
            "{}",
            Error::BadCodepoint(CodepointInfo {
                cp: 0,
                position: 0,
                property: DerivedPropertyValue::PValid
            })
        );
        format!("{}", Error::Unexpected(UnexpectedError::Undefined));
    }

    #[test]
    fn fmt_unexpected_error() {
        format!("{}", UnexpectedError::Undefined);
        format!("{}", UnexpectedError::ProfileRuleNotApplicable);
        format!(
            "{}",
            UnexpectedError::MissingContextRule(CodepointInfo {
                cp: 0,
                position: 0,
                property: DerivedPropertyValue::PValid
            })
        );
        format!(
            "{}",
            UnexpectedError::ContextRuleNotApplicable(CodepointInfo {
                cp: 0,
                position: 0,
                property: DerivedPropertyValue::PValid
            })
        );
    }
}