sccp_protocol/phone/xml/
document.rs1use quick_xml::events::Event;
9use quick_xml::reader::Reader;
10use serde::Serialize;
11use serde::de::DeserializeOwned;
12
13use super::{
14 CiscoIpPhoneDirectory, CiscoIpPhoneExecute, CiscoIpPhoneIconFileMenu, CiscoIpPhoneIconMenu,
15 CiscoIpPhoneImageList, CiscoIpPhoneInput, CiscoIpPhoneMenu, CiscoIpPhoneStatus,
16 CiscoIpPhoneStatusFile, CiscoIpPhoneText, PHONE_BACKGROUND_LIST_MAX_BYTES,
17 PHONE_DIRECTORY_MAX_BYTES, PHONE_EXECUTE_MAX_BYTES, PHONE_INPUT_MAX_BYTES,
18 PHONE_MENU_MAX_BYTES, PHONE_STATUS_MAX_BYTES, PHONE_TEXT_MAX_BYTES, PhoneXmlError,
19 decoding_reader, from_bytes, to_string,
20};
21
22mod sealed {
23 pub trait Sealed {}
24}
25
26pub trait PhoneXmlDocument: sealed::Sealed + Sized + Serialize + DeserializeOwned {
33 const ROOT: &'static [u8];
35 const MAXIMUM_BYTES: usize;
37
38 fn validate_document(&self) -> Result<(), PhoneXmlError>;
40
41 fn parse_xml(document: &[u8]) -> Result<Self, PhoneXmlError> {
43 Self::parse_xml_with_limit(document, Self::MAXIMUM_BYTES)
44 }
45
46 fn parse_xml_with_limit(document: &[u8], maximum_bytes: usize) -> Result<Self, PhoneXmlError> {
51 let parsed: Self = from_bytes(document, maximum_bytes)?;
52 validate_root(document, Self::ROOT)?;
53 parsed.validate_document()?;
54 Ok(parsed)
55 }
56
57 fn serialize_xml(&self) -> Result<String, PhoneXmlError> {
59 self.serialize_xml_with_limit(Self::MAXIMUM_BYTES)
60 }
61
62 fn serialize_xml_with_limit(&self, maximum_bytes: usize) -> Result<String, PhoneXmlError> {
64 self.validate_document()?;
65 to_string(self, maximum_bytes)
66 }
67}
68
69macro_rules! impl_phone_xml_document {
70 ($document:ty, $root:literal, $maximum:expr) => {
71 impl sealed::Sealed for $document {}
72
73 impl PhoneXmlDocument for $document {
74 const ROOT: &'static [u8] = $root;
75 const MAXIMUM_BYTES: usize = $maximum;
76
77 fn validate_document(&self) -> Result<(), PhoneXmlError> {
78 <$document>::validate(self)
79 }
80 }
81
82 impl $document {
83 pub fn from_xml(document: &[u8]) -> Result<Self, PhoneXmlError> {
85 <Self as PhoneXmlDocument>::parse_xml(document)
86 }
87
88 pub fn from_xml_with_limit(
90 document: &[u8],
91 maximum_bytes: usize,
92 ) -> Result<Self, PhoneXmlError> {
93 <Self as PhoneXmlDocument>::parse_xml_with_limit(document, maximum_bytes)
94 }
95
96 pub fn to_xml(&self) -> Result<String, PhoneXmlError> {
98 <Self as PhoneXmlDocument>::serialize_xml(self)
99 }
100
101 pub fn to_xml_with_limit(&self, maximum_bytes: usize) -> Result<String, PhoneXmlError> {
103 <Self as PhoneXmlDocument>::serialize_xml_with_limit(self, maximum_bytes)
104 }
105 }
106 };
107}
108
109impl_phone_xml_document!(CiscoIpPhoneText, b"CiscoIPPhoneText", PHONE_TEXT_MAX_BYTES);
110impl_phone_xml_document!(
111 CiscoIpPhoneInput,
112 b"CiscoIPPhoneInput",
113 PHONE_INPUT_MAX_BYTES
114);
115impl_phone_xml_document!(
116 CiscoIpPhoneExecute,
117 b"CiscoIPPhoneExecute",
118 PHONE_EXECUTE_MAX_BYTES
119);
120impl_phone_xml_document!(
121 CiscoIpPhoneImageList,
122 b"CiscoIPPhoneImageList",
123 PHONE_BACKGROUND_LIST_MAX_BYTES
124);
125impl_phone_xml_document!(
126 CiscoIpPhoneStatus,
127 b"CiscoIPPhoneStatus",
128 PHONE_STATUS_MAX_BYTES
129);
130impl_phone_xml_document!(
131 CiscoIpPhoneStatusFile,
132 b"CiscoIPPhoneStatusFile",
133 PHONE_STATUS_MAX_BYTES
134);
135impl_phone_xml_document!(
136 CiscoIpPhoneDirectory,
137 b"CiscoIPPhoneDirectory",
138 PHONE_DIRECTORY_MAX_BYTES
139);
140impl_phone_xml_document!(CiscoIpPhoneMenu, b"CiscoIPPhoneMenu", PHONE_MENU_MAX_BYTES);
141impl_phone_xml_document!(
142 CiscoIpPhoneIconMenu,
143 b"CiscoIPPhoneIconMenu",
144 PHONE_MENU_MAX_BYTES
145);
146impl_phone_xml_document!(
147 CiscoIpPhoneIconFileMenu,
148 b"CiscoIPPhoneIconFileMenu",
149 PHONE_MENU_MAX_BYTES
150);
151
152fn validate_root(document: &[u8], expected: &[u8]) -> Result<(), PhoneXmlError> {
153 let mut reader = Reader::from_reader(decoding_reader(document));
154 let mut buffer = Vec::new();
155 loop {
156 match reader.read_event_into(&mut buffer) {
157 Ok(Event::Start(element) | Event::Empty(element)) => {
158 return if element.name().as_ref().as_bytes() == expected {
159 Ok(())
160 } else {
161 Err(PhoneXmlError::InvalidField {
162 field: "phone XML document root",
163 expected: "the schema root for this document type",
164 })
165 };
166 }
167 Ok(Event::Eof) => {
168 return Err(PhoneXmlError::InvalidField {
169 field: "phone XML document root",
170 expected: "one schema root element",
171 });
172 }
173 Ok(_) => {}
174 Err(error) => return Err(PhoneXmlError::Malformed(error)),
175 }
176 buffer.clear();
177 }
178}