1use crate::error::ZipErrorT;
2use crate::ffi;
3use crate::file::{Encoding, File, LocateFlag, OpenFlag as FileOpenFlag};
4use crate::source::Source;
5use crate::Error;
6use crate::Result;
7use std::ffi::CStr;
8use std::marker::PhantomData;
9use std::ptr::null_mut;
10
11#[derive(Clone, Copy, PartialEq, Eq, Hash)]
12pub enum OpenFlag {
13 CheckConsistency,
14 Create,
15 Exclusive,
16 Truncate,
17 ReadOnly,
18}
19
20#[derive(Debug)]
21pub struct Archive {
22 handle: *mut ffi::zip_t,
23}
24
25impl Archive {
26 pub fn open<S, F>(mut source: Source<S>, flags: F) -> Result<Archive>
27 where
28 F: AsRef<[OpenFlag]>,
29 {
30 let mut flags_value = 0;
31 for flag in flags.as_ref() {
32 match flag {
33 OpenFlag::CheckConsistency => flags_value |= ffi::ZIP_CHECKCONS,
34 OpenFlag::Create => flags_value |= ffi::ZIP_CREATE,
35 OpenFlag::Exclusive => flags_value |= ffi::ZIP_EXCL,
36 OpenFlag::Truncate => flags_value |= ffi::ZIP_TRUNCATE,
37 OpenFlag::ReadOnly => flags_value |= ffi::ZIP_RDONLY,
38 }
39 }
40
41 unsafe {
42 let mut error = ZipErrorT::default();
43 let handle =
44 ffi::zip_open_from_source(source.handle_mut(), flags_value as _, &mut *error);
45
46 if handle.is_null() {
47 Err(error.into())
48 } else {
49 source.taken();
50 Ok(Archive { handle })
51 }
52 }
53 }
54
55 fn error(&mut self) -> ZipErrorT<&mut ffi::zip_error_t> {
56 unsafe {
57 let error = ffi::zip_get_error(self.handle);
58 (&mut *error).into()
59 }
60 }
61
62 fn close_mut(&mut self) -> Result<()> {
65 if self.handle.is_null() {
66 Ok(())
67 } else {
68 let result = unsafe { ffi::zip_close(self.handle) };
69 if result == 0 {
70 self.handle = null_mut();
71 Ok(())
72 } else {
73 Err(self.error().into())
74 }
75 }
76 }
77
78 pub fn close(mut self) -> std::result::Result<(), (Self, Error)> {
81 match self.close_mut() {
82 Ok(()) => Ok(()),
83 Err(e) => Err((self, e)),
84 }
85 }
86
87 fn discard_mut(&mut self) {
89 if !self.handle.is_null() {
90 unsafe {
91 ffi::zip_discard(self.handle);
92 }
93 }
94 }
95
96 pub fn discard(mut self) {
98 self.discard_mut()
99 }
100
101 pub fn add<N, S>(
104 &mut self,
105 name: N,
106 mut source: Source<S>,
107 encoding: Encoding,
108 overwrite: bool,
109 ) -> Result<u64>
110 where
111 N: AsRef<CStr>,
112 {
113 let mut flags = match encoding {
114 Encoding::Guess => ffi::ZIP_FL_ENC_GUESS,
115 Encoding::Utf8 => ffi::ZIP_FL_ENC_UTF_8,
116 Encoding::Cp437 => ffi::ZIP_FL_ENC_CP437,
117 };
118 if overwrite {
119 flags |= ffi::ZIP_FL_OVERWRITE;
120 }
121
122 let response = unsafe {
123 ffi::zip_file_add(
124 self.handle,
125 name.as_ref().as_ptr(),
126 source.handle_mut(),
127 flags as _,
128 )
129 };
130 if response == -1 {
131 Err(self.error().into())
132 } else {
133 source.taken();
134 Ok(response as _)
135 }
136 }
137
138 pub fn replace<S>(&mut self, index: u64, mut source: Source<S>) -> Result<()> {
140 let response =
141 unsafe { ffi::zip_file_replace(self.handle, index as _, source.handle_mut(), 0) };
142 if response == -1 {
143 Err(self.error().into())
144 } else {
145 source.taken();
146 Ok(())
147 }
148 }
149
150 pub fn open_file<N, O, L>(
153 &mut self,
154 name: N,
155 open_flags: O,
156 locate_flags: L,
157 ) -> Result<File<'_>>
158 where
159 N: AsRef<CStr>,
160 O: AsRef<[FileOpenFlag]>,
161 L: AsRef<[LocateFlag]>,
162 {
163 let mut flags_value = 0;
164 for flag in open_flags.as_ref() {
165 match flag {
166 FileOpenFlag::Compressed => flags_value |= ffi::ZIP_FL_COMPRESSED,
167 FileOpenFlag::Unchanged => flags_value |= ffi::ZIP_FL_UNCHANGED,
168 }
169 }
170 for flag in locate_flags.as_ref() {
171 match flag {
172 LocateFlag::NoCase => flags_value |= ffi::ZIP_FL_NOCASE,
173 LocateFlag::NoDir => flags_value |= ffi::ZIP_FL_NODIR,
174 LocateFlag::EncodingRaw => flags_value |= ffi::ZIP_FL_ENC_RAW,
175 LocateFlag::EncodingGuess => flags_value |= ffi::ZIP_FL_ENC_GUESS,
176 LocateFlag::EncodingStrict => flags_value |= ffi::ZIP_FL_ENC_STRICT,
177 }
178 }
179 let handle =
180 unsafe { ffi::zip_fopen(self.handle, name.as_ref().as_ptr(), flags_value as _) };
181 if handle.is_null() {
182 Err(self.error().into())
183 } else {
184 Ok(File {
185 handle,
186 phantom: PhantomData,
187 })
188 }
189 }
190}
191
192impl Drop for Archive {
196 fn drop(&mut self) {
197 if let Err(_) = self.close_mut() {
198 self.discard_mut()
199 }
200 }
201}