umya_spreadsheet/reader/
driver.rs1use std::{
2 io,
3 path::{
4 Component,
5 Path,
6 PathBuf,
7 },
8 string::FromUtf8Error,
9};
10
11use quick_xml::events::attributes::Attribute;
12
13#[macro_export]
14macro_rules! xml_read_loop {
15 ($reader:ident $(,$pat:pat => $result:expr)+ $(,)?) => {
16 let mut buf = Vec::new();
17 loop {
18 let ev = match $reader.read_event_into(&mut buf) {
19 Ok(v) => v,
20 Err(e) => panic!("Error at position {}: {e:?}", $reader.buffer_position()),
21 };
22
23 match ev {
24 $($pat => $result,)+
25 _ => (),
26 }
27
28 buf.clear();
29 }
30 };
31}
32
33pub(crate) use crate::xml_read_loop;
34
35#[macro_export]
36macro_rules! set_string_from_xml {
37 ($self:ident, $e:ident, $attr:ident, $xml_attr:expr) => {{
38 if let Some(v) = get_attribute($e, $xml_attr.as_bytes()) {
39 $self.$attr.set_value_string(v);
40 }
41 }};
42}
43
44pub(crate) use crate::set_string_from_xml;
45
46pub(crate) fn normalize_path(path: &str) -> PathBuf {
47 let path = Path::new(path);
48 let mut components = path.components().peekable();
49 let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().copied() {
50 components.next();
51 PathBuf::from(c.as_os_str())
52 } else {
53 PathBuf::new()
54 };
55
56 for component in components {
57 match component {
58 Component::Prefix(..) => unreachable!(),
59 Component::RootDir => {
60 ret.push(component.as_os_str());
61 }
62 Component::CurDir => {}
63 Component::ParentDir => {
64 ret.pop();
65 }
66 Component::Normal(c) => {
67 ret.push(c);
68 }
69 }
70 }
71 ret
72}
73
74#[inline]
75pub(crate) fn join_paths(base_path: &str, target: &str) -> String {
76 match target.split_once('/') {
77 Some(("", target)) => normalize_path_to_str(target),
78 _ => normalize_path_to_str(&format!("{base_path}/{target}")),
79 }
80}
81
82#[inline]
83pub(crate) fn normalize_path_to_str(path: &str) -> String {
84 let ret = normalize_path(path);
85 ret.to_str().unwrap_or("").replace('\\', "/")
86}
87
88pub(crate) fn zip_by_name<'a, R: io::Read + io::Seek>(
96 arv: &'a mut zip::ZipArchive<R>,
97 name: &str,
98) -> Result<zip::read::ZipFile<'a, R>, zip::result::ZipError> {
99 if arv.by_name(name).is_ok() {
101 return arv.by_name(name);
104 }
105
106 let backslash_name = name.replace('/', "\\");
108 if backslash_name != name && arv.by_name(&backslash_name).is_ok() {
109 return arv.by_name(&backslash_name);
110 }
111
112 let normalized = name.replace('\\', "/").to_ascii_lowercase();
114 for i in 0..arv.len() {
115 let entry_name = match arv.by_index(i) {
116 Ok(f) => f.name().to_owned(),
117 Err(_) => continue,
118 };
119 let entry_normalized = entry_name.replace('\\', "/").to_ascii_lowercase();
120 if entry_normalized == normalized {
121 return arv.by_name(&entry_name);
122 }
123 }
124
125 Err(zip::result::ZipError::FileNotFound)
126}
127
128#[inline]
134pub(crate) fn get_attribute(e: &quick_xml::events::BytesStart<'_>, key: &[u8]) -> Option<String> {
135 let result = e
137 .attributes()
138 .with_checks(false)
139 .find_map(|attr| match attr {
140 Ok(ref attr) if attr.key.into_inner() == key => {
141 Some(get_attribute_value(attr).unwrap())
142 }
143 _ => None,
144 });
145 if result.is_some() {
146 return result;
147 }
148
149 let local_name = {
153 let pos = key.iter().position(|&b| b == b':')?;
154 &key[pos + 1..]
155 };
156
157 e.attributes()
158 .with_checks(false)
159 .find_map(|attr| match attr {
160 Ok(ref attr) => {
161 let attr_key = attr.key.into_inner();
162 let attr_local = match attr_key.iter().position(|&b| b == b':') {
163 Some(pos) => &attr_key[pos + 1..],
164 None => attr_key,
165 };
166 if attr_local == local_name {
167 Some(get_attribute_value(attr).unwrap())
168 } else {
169 None
170 }
171 }
172 _ => None,
173 })
174}
175
176#[inline]
177pub(crate) fn get_attribute_value(attr: &Attribute) -> Result<String, FromUtf8Error> {
178 String::from_utf8(attr.value.to_vec())
179}