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
use crate::{
    pos_error::PosError,
    shared::{attr, text_uint, SharedError},
};
use kanjidic_types::{KangXi, Radical, RadicalKind, TryFromPrimitiveError};
use roxmltree::Node;
use std::convert::TryFrom;
use thiserror::Error;

#[derive(Debug, PartialEq, Eq, Clone, Error)]
pub enum RadicalError {
    #[error("(Radical) Shared: {0}")]
    Shared(#[from] SharedError),
    #[error("(Radical) Radical is not in a valid range: {0}")]
    OutOfRange(#[from] TryFromPrimitiveError<KangXi>),
    #[error("(Radical) Not a recognized radical kind: {0}")]
    Kind(PosError),
}

pub fn from(node: Node) -> Result<Radical, RadicalError> {
    let kang_xi_number: u8 = text_uint(&node)?;
    let kang_xi = KangXi::try_from(kang_xi_number)?;
    let tag = attr(&node, "rad_type")?;
    match tag {
        "classical" => Ok(Radical {
            kind: RadicalKind::Classical,
            radical: kang_xi,
        }),
        "nelson_c" => Ok(Radical {
            kind: RadicalKind::Nelson,
            radical: kang_xi,
        }),
        _ => Err(RadicalError::Kind(PosError::from(&node))),
    }
}

#[cfg(test)]
mod tests {
    use super::from;
    use crate::test_shared::DOC;
    use kanjidic_types::{KangXi, Radical, RadicalKind};

    #[test]
    fn parse_radical() {
        let node = DOC
            .descendants()
            .find(|node| node.has_tag_name("rad_value"))
            .unwrap();
        let radical = from(node);
        assert_eq!(
            radical,
            Ok(Radical {
                kind: RadicalKind::Classical,
                radical: KangXi::Two,
            })
        )
    }
}