Skip to main content

flarmnet/lx/
encode.rs

1use crate::File;
2use crate::lx::cipher;
3use quick_xml;
4use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event};
5use std::io::{Cursor, Write};
6use thiserror::Error;
7
8#[derive(Error, Debug)]
9pub enum EncodeError {
10    #[error(transparent)]
11    Xml(#[from] quick_xml::Error),
12}
13
14/// Encodes a FlarmNet file in LX format.
15///
16/// # Examples
17///
18/// ```
19/// # use flarmnet::Record;
20/// let file = flarmnet::File {
21///     version: 123,
22///     records: vec![
23///         Record {
24///             flarm_id: "3EE3C7".to_string(),
25///             pilot_name: "Tobias Bieniek".to_string(),
26///             airfield: "EDKA".to_string(),
27///             plane_type: "LS6a".to_string(),
28///             registration: "D-0816".to_string(),
29///             call_sign: "SG".to_string(),
30///             frequency: "130.530".to_string(),
31///         }
32///     ]
33/// };
34///
35/// let result = flarmnet::lx::encode_file(&file);
36/// assert!(result.is_ok());
37/// ```
38pub fn encode_file(file: &File) -> Result<Vec<u8>, EncodeError> {
39    let mut writer = Writer::new(Cursor::new(Vec::new()));
40    writer.write(file)?;
41
42    let xml = writer.into_inner().into_inner();
43
44    Ok(xml)
45}
46
47#[derive(Clone)]
48pub struct Writer<W: Write> {
49    xml_writer: quick_xml::Writer<cipher::Writer<W>>,
50}
51
52impl<W: Write> Writer<W> {
53    pub fn new(inner: W) -> Self {
54        let cipher_writer = cipher::Writer::new(inner);
55        let xml_writer = quick_xml::Writer::new(cipher_writer);
56
57        Self { xml_writer }
58    }
59
60    pub fn write(&mut self, file: &File) -> Result<(), EncodeError> {
61        let writer = &mut self.xml_writer;
62
63        writer.write_event(Event::Decl(BytesDecl::new("1.0", Some("UTF-8"), None)))?;
64        writer.write_event(Event::Text(BytesText::from_escaped("\n")))?;
65
66        let version = format!("{:06x?}", file.version);
67        writer.write_event(Event::Start(
68            BytesStart::new("FLARMNET")
69                .with_attributes(vec![("Version".as_bytes(), version.as_bytes())]),
70        ))?;
71        writer.write_event(Event::Text(BytesText::from_escaped("\n")))?;
72
73        for record in &file.records {
74            writer
75                .write_event(Event::Start(BytesStart::new("FLARMDATA").with_attributes(
76                    vec![("FlarmID".as_bytes(), record.flarm_id.as_bytes())],
77                )))?;
78
79            if !record.pilot_name.is_empty() {
80                writer.write_event(Event::Start(BytesStart::new("NAME")))?;
81                writer.write_event(Event::Text(BytesText::new(&record.pilot_name)))?;
82                writer.write_event(Event::End(BytesEnd::new("NAME")))?;
83            }
84
85            if !record.airfield.is_empty() {
86                writer.write_event(Event::Start(BytesStart::new("AIRFIELD")))?;
87                writer.write_event(Event::Text(BytesText::new(&record.airfield)))?;
88                writer.write_event(Event::End(BytesEnd::new("AIRFIELD")))?;
89            }
90
91            if !record.plane_type.is_empty() {
92                writer.write_event(Event::Start(BytesStart::new("TYPE")))?;
93                writer.write_event(Event::Text(BytesText::new(&record.plane_type)))?;
94                writer.write_event(Event::End(BytesEnd::new("TYPE")))?;
95            }
96
97            if !record.registration.is_empty() {
98                writer.write_event(Event::Start(BytesStart::new("REG")))?;
99                writer.write_event(Event::Text(BytesText::new(&record.registration)))?;
100                writer.write_event(Event::End(BytesEnd::new("REG")))?;
101            }
102
103            if !record.call_sign.is_empty() {
104                writer.write_event(Event::Start(BytesStart::new("COMPID")))?;
105                writer.write_event(Event::Text(BytesText::new(&record.call_sign)))?;
106                writer.write_event(Event::End(BytesEnd::new("COMPID")))?;
107            }
108
109            if !record.frequency.is_empty() {
110                writer.write_event(Event::Start(BytesStart::new("FREQUENCY")))?;
111                writer.write_event(Event::Text(BytesText::new(&record.frequency)))?;
112                writer.write_event(Event::End(BytesEnd::new("FREQUENCY")))?;
113            }
114
115            writer.write_event(Event::End(BytesEnd::new("FLARMDATA")))?;
116            writer.write_event(Event::Text(BytesText::from_escaped("\n")))?;
117        }
118        writer.write_event(Event::End(BytesEnd::new("FLARMNET")))?;
119
120        Ok(())
121    }
122
123    pub fn into_inner(self) -> W {
124        self.xml_writer.into_inner().into_inner()
125    }
126}