disco_quick/
label.rs

1use crate::parser::{Parser, ParserError};
2use crate::reader::XmlReader;
3use crate::shared::Image;
4use crate::util::get_attr_id;
5use log::debug;
6use quick_xml::events::Event;
7use std::fmt;
8use std::mem::take;
9
10#[derive(Clone, Debug, Default)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Label {
13    pub id: u32,
14    pub name: String,
15    pub contactinfo: Option<String>,
16    pub profile: Option<String>,
17    pub parent_label: Option<LabelInfo>,
18    pub sublabels: Vec<LabelInfo>,
19    pub urls: Vec<String>,
20    pub data_quality: String,
21    pub images: Vec<Image>,
22}
23
24#[derive(Clone, Debug, Default)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct LabelInfo {
27    pub id: u32,
28    pub name: String,
29}
30
31impl fmt::Display for Label {
32    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33        write!(f, "{}", self.name)
34    }
35}
36
37pub struct LabelsReader {
38    buf: Vec<u8>,
39    reader: XmlReader,
40    parser: LabelParser,
41}
42
43impl LabelsReader {
44    pub fn new(reader: XmlReader, buf: Vec<u8>) -> Self {
45        Self {
46            buf,
47            reader,
48            parser: LabelParser::new(),
49        }
50    }
51}
52
53impl Iterator for LabelsReader {
54    type Item = Label;
55    fn next(&mut self) -> Option<Self::Item> {
56        loop {
57            match self.reader.read_event_into(&mut self.buf).unwrap() {
58                Event::Eof => {
59                    return None;
60                }
61                ev => self.parser.process(ev).unwrap(),
62            };
63            if self.parser.item_ready {
64                return Some(self.parser.take());
65            }
66            self.buf.clear();
67        }
68    }
69}
70
71#[derive(Debug, Default)]
72enum ParserState {
73    #[default]
74    Label,
75    Name,
76    Id,
77    Images,
78    Contactinfo,
79    Profile,
80    ParentLabel,
81    Sublabels,
82    Sublabel,
83    Urls,
84    DataQuality,
85}
86
87#[derive(Debug, Default)]
88pub struct LabelParser {
89    state: ParserState,
90    current_item: Label,
91    current_sublabel_id: Option<u32>,
92    current_parent_id: Option<u32>,
93    item_ready: bool,
94}
95
96impl Parser for LabelParser {
97    type Item = Label;
98    fn new() -> Self {
99        Self::default()
100    }
101    fn take(&mut self) -> Self::Item {
102        self.item_ready = false;
103        take(&mut self.current_item)
104    }
105
106    fn process(&mut self, ev: Event) -> Result<(), ParserError> {
107        self.state = match self.state {
108            ParserState::Label => match ev {
109                Event::Start(e) if e.local_name().as_ref() == b"label" => ParserState::Label,
110
111                Event::Start(e) => match e.local_name().as_ref() {
112                    b"name" => ParserState::Name,
113                    b"id" => ParserState::Id,
114                    b"contactinfo" => ParserState::Contactinfo,
115                    b"profile" => ParserState::Profile,
116                    b"parentLabel" => {
117                        self.current_parent_id = Some(get_attr_id(e));
118                        ParserState::ParentLabel
119                    }
120                    b"sublabels" => ParserState::Sublabels,
121                    b"urls" => ParserState::Urls,
122                    b"images" => ParserState::Images,
123                    b"data_quality" => ParserState::DataQuality,
124                    _ => ParserState::Label,
125                },
126                Event::End(e) if e.local_name().as_ref() == b"label" => {
127                    self.item_ready = true;
128                    ParserState::Label
129                }
130
131                _ => ParserState::Label,
132            },
133
134            ParserState::Id => match ev {
135                Event::Text(e) => {
136                    self.current_item.id = e.unescape()?.parse()?;
137                    debug!("Began parsing Label {}", self.current_item.id);
138                    ParserState::Id
139                }
140                _ => ParserState::Label,
141            },
142
143            ParserState::Name => match ev {
144                Event::Text(e) => {
145                    self.current_item.name = e.unescape()?.to_string();
146                    ParserState::Name
147                }
148                _ => ParserState::Label,
149            },
150
151            ParserState::Images => match ev {
152                Event::Empty(e) if e.local_name().as_ref() == b"image" => {
153                    let image = Image::from_event(e);
154                    self.current_item.images.push(image);
155                    ParserState::Images
156                }
157                Event::End(e) if e.local_name().as_ref() == b"images" => ParserState::Label,
158
159                _ => ParserState::Images,
160            },
161
162            ParserState::Contactinfo => match ev {
163                Event::Text(e) => {
164                    self.current_item.contactinfo = Some(e.unescape()?.to_string());
165                    ParserState::Contactinfo
166                }
167                _ => ParserState::Label,
168            },
169
170            ParserState::Profile => match ev {
171                Event::Text(e) => {
172                    self.current_item.profile = Some(e.unescape()?.to_string());
173                    ParserState::Profile
174                }
175                _ => ParserState::Label,
176            },
177
178            ParserState::ParentLabel => match ev {
179                Event::Text(e) => {
180                    let parent_label = LabelInfo {
181                        id: self.current_parent_id.unwrap(),
182                        name: e.unescape()?.to_string(),
183                    };
184                    self.current_item.parent_label = Some(parent_label);
185                    self.current_parent_id = None;
186                    ParserState::ParentLabel
187                }
188                _ => ParserState::Label,
189            },
190
191            ParserState::Sublabels => match ev {
192                Event::Start(e) if e.local_name().as_ref() == b"label" => {
193                    self.current_sublabel_id = Some(get_attr_id(e));
194                    ParserState::Sublabel
195                }
196                Event::End(e) if e.local_name().as_ref() == b"sublabels" => ParserState::Label,
197
198                _ => ParserState::Sublabels,
199            },
200
201            ParserState::Sublabel => match ev {
202                Event::Text(e) => {
203                    let sublabel = LabelInfo {
204                        id: self.current_sublabel_id.unwrap(),
205                        name: e.unescape()?.to_string(),
206                    };
207                    self.current_item.sublabels.push(sublabel);
208                    self.current_sublabel_id = None;
209                    ParserState::Sublabels
210                }
211                _ => ParserState::Sublabels,
212            },
213
214            ParserState::Urls => match ev {
215                Event::Text(e) => {
216                    self.current_item.urls.push(e.unescape()?.to_string());
217                    ParserState::Urls
218                }
219                Event::End(e) if e.local_name().as_ref() == b"urls" => ParserState::Label,
220
221                _ => ParserState::Urls,
222            },
223
224            ParserState::DataQuality => match ev {
225                Event::Text(e) => {
226                    self.current_item.data_quality = e.unescape()?.to_string();
227                    ParserState::DataQuality
228                }
229                _ => ParserState::Label,
230            },
231        };
232
233        Ok(())
234    }
235}