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
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]

pub mod borrowed;

#[cfg(feature = "parser")]
mod parser;

#[cfg(feature = "parser")]
pub use crate::parser::ClusterIter;

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Cluster {
    pub header: String,
    pub verified: bool,
    pub level: usize,
    pub entries: Vec<Entry>,
    pub notes: Vec<String>,
}

impl Cluster {
    pub fn infer(&mut self) {
        for entry in self.entries.iter_mut() {
            entry.infer();
        }
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Entry {
    pub variants: Vec<Variant>,
    pub pos: Option<Pos>,
    pub archaic: bool,
    pub description: Option<String>,
    pub note: Option<String>,
    pub comment: Option<String>,
}

impl Entry {
    pub fn infer(&mut self) {
        imply(
            &mut self.variants,
            Category::BritishIse,
            Category::BritishIze,
        );
        imply(&mut self.variants, Category::BritishIze, Category::Canadian);
        imply(
            &mut self.variants,
            Category::BritishIse,
            Category::Australian,
        );
    }
}

fn imply(variants: &mut [Variant], required: Category, missing: Category) {
    let missing_exists = variants
        .iter()
        .any(|v| v.types.iter().any(|t| t.category == missing));
    if missing_exists {
        return;
    }

    for variant in variants.iter_mut() {
        let types: Vec<_> = variant
            .types
            .iter()
            .filter(|t| t.category == required)
            .cloned()
            .map(|mut t| {
                t.category = missing;
                t
            })
            .collect();
        variant.types.extend(types);
    }
}

#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Variant {
    pub types: Vec<Type>,
    pub word: String,
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Type {
    pub category: Category,
    pub tag: Option<Tag>,
    pub num: Option<usize>,
}

#[cfg_attr(feature = "flags", enumflags2::bitflags)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum Category {
    American = 0x01,
    BritishIse = 0x02,
    BritishIze = 0x04,
    Canadian = 0x08,
    Australian = 0x10,
    Other = 0x20,
}

#[cfg(feature = "flags")]
pub type CategorySet = enumflags2::BitFlags<Category>;

#[cfg_attr(feature = "flags", enumflags2::bitflags)]
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(u8)]
pub enum Tag {
    Eq = 0x01,
    Variant = 0x02,
    Seldom = 0x04,
    Possible = 0x08,
    Improper = 0x10,
}

#[cfg(feature = "flags")]
pub type TagSet = enumflags2::BitFlags<Tag>;

#[cfg_attr(feature = "flags", enumflags2::bitflags)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[repr(u8)]
pub enum Pos {
    Noun = 0x01,
    Verb = 0x02,
    Adjective = 0x04,
    Adverb = 0x08,
    AdjectiveOrAdverb = 0x10,
    Interjection = 0x20,
    Preposition = 0x40,
}

#[cfg(feature = "flags")]
pub type PosSet = enumflags2::BitFlags<Pos>;