1#![doc = "Unstable APIs\n\
2\
3All APIs accessible by importing this module are unstable; They may be changed in patch \
4releases. You MUST use an exact version specifier in `Cargo.toml`, to indicate the version of this \
5API you're using:\n\
6\
7```toml\n
8[dependencies]\n
9zip = \"="]
10#![doc=env!("CARGO_PKG_VERSION")]
11#![doc = "\"\n\
12```"]
13
14use std::borrow::Cow;
15use std::io;
16use std::io::Read;
17use std::path::{Component, MAIN_SEPARATOR, Path};
18
19pub mod stream {
21 pub use crate::read::stream::{ZipStreamReader, ZipStreamVisitor};
22}
23
24pub mod format {
26 pub use crate::format::*;
27}
28pub mod write {
30 use crate::result::{ZipError, ZipResult};
31 use crate::write::{FileOptionExtension, FileOptions};
32 pub trait FileOptionsExt<'k> {
34 fn with_deprecated_encryption(self, password: &'k [u8]) -> ZipResult<Self>
41 where
42 Self: Sized;
43 }
44 impl<'k, 'n, T: FileOptionExtension> FileOptionsExt<'k> for FileOptions<'k, 'n, T> {
45 fn with_deprecated_encryption(
46 self,
47 password: &'k [u8],
48 ) -> ZipResult<FileOptions<'k, 'n, T>> {
49 if password.is_empty() {
50 Err(ZipError::InvalidPassword)
55 } else {
56 Ok(self.with_deprecated_encryption(password))
57 }
58 }
59 }
60}
61
62pub trait LittleEndianReadExt: Read {
64 fn read_u16_le(&mut self) -> io::Result<u16> {
66 let mut out = [0u8; 2];
67 self.read_exact(&mut out)?;
68 Ok(u16::from_le_bytes(out))
69 }
70
71 fn read_u32_le(&mut self) -> io::Result<u32> {
73 let mut out = [0u8; 4];
74 self.read_exact(&mut out)?;
75 Ok(u32::from_le_bytes(out))
76 }
77
78 fn read_u64_le(&mut self) -> io::Result<u64> {
80 let mut out = [0u8; 8];
81 self.read_exact(&mut out)?;
82 Ok(u64::from_le_bytes(out))
83 }
84}
85
86impl<R: Read> LittleEndianReadExt for R {}
87
88pub fn path_to_string<T: AsRef<Path>>(path: T) -> Result<Box<str>, std::io::Error> {
90 let mut maybe_original = None;
91 if let Some(original) = path.as_ref().to_str() {
92 if original.is_empty() || original == "." || original == ".." {
93 return Ok(String::new().into_boxed_str());
94 }
95 if original.starts_with(MAIN_SEPARATOR) {
96 if original.len() == 1 {
97 return Ok(MAIN_SEPARATOR.to_string().into_boxed_str());
98 } else if (MAIN_SEPARATOR == '/' || !original[1..].contains(MAIN_SEPARATOR))
99 && !original.ends_with('.')
100 && !original.contains([MAIN_SEPARATOR, MAIN_SEPARATOR])
101 && !original.contains([MAIN_SEPARATOR, '.', MAIN_SEPARATOR])
102 && !original.contains([MAIN_SEPARATOR, '.', '.', MAIN_SEPARATOR])
103 {
104 maybe_original = Some(&original[1..]);
105 }
106 } else if !original.contains(MAIN_SEPARATOR) {
107 return Ok(original.into());
108 }
109 }
110 let mut recreate = maybe_original.is_none();
111 let mut normalized_components = Vec::new();
112
113 for component in path.as_ref().components() {
114 match component {
115 Component::Normal(os_str) => {
116 if let Some(valid_str) = os_str.to_str() {
117 normalized_components.push(Cow::Borrowed(valid_str));
118 } else {
119 recreate = true;
120 normalized_components.push(os_str.to_string_lossy());
121 }
122 }
123 Component::ParentDir => {
124 recreate = true;
125 normalized_components.pop();
126 }
127 _ => {
128 recreate = true;
129 }
130 }
131 }
132 if recreate {
133 Ok(normalized_components.join("/").into())
134 } else {
135 Ok(maybe_original
136 .ok_or_else(|| std::io::Error::other("Original path is empty"))?
137 .into())
138 }
139}