nuts_archive/entry/
mut.rs

1// MIT License
2//
3// Copyright (c) 2023,2024 Robin Doer
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to
7// deal in the Software without restriction, including without limitation the
8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9// sell copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21// IN THE SOFTWARE.
22
23#[cfg(test)]
24mod tests;
25
26use log::debug;
27use nuts_backend::Backend;
28use std::cmp;
29
30use crate::entry::mode::Mode;
31use crate::entry::{populate_mode_api, populate_tstamp_api, Inner};
32use crate::error::ArchiveResult;
33use crate::flush_header;
34use crate::header::Header;
35use crate::id::Id;
36use crate::pager::Pager;
37use crate::tree::Tree;
38
39macro_rules! impl_new {
40    ($type:ident, $mode:ident) => {
41        pub(crate) fn new(
42            pager: &'a mut Pager<B>,
43            header_id: &'a Id<B>,
44            header: &'a mut Header,
45            tree: &'a mut Tree<B>,
46            name: String,
47        ) -> $type<'a, B> {
48            $type(InnerBuilder::new(
49                pager,
50                header_id,
51                header,
52                tree,
53                name,
54                Mode::$mode(),
55            ))
56        }
57    };
58}
59
60/// Builder for an new file entry.
61///
62/// A `FileBuilder` instance is returned by
63/// [`Archive::append_file()`](crate::Archive::append_file). Calling
64/// [`FileBuilder::build()`] will create the entry at the end of the archive.
65pub struct FileBuilder<'a, B: Backend>(InnerBuilder<'a, B>);
66
67impl<'a, B: Backend> FileBuilder<'a, B> {
68    impl_new!(FileBuilder, file);
69
70    populate_mode_api!(mut);
71    populate_tstamp_api!(mut);
72
73    /// Finally, creates the new file entry at the end of the archive.
74    ///
75    /// It returns an [`EntryMut`] instance, where you are able to add content
76    /// to the entry.
77    pub fn build(self) -> ArchiveResult<EntryMut<'a, B>, B> {
78        self.0.build()
79    }
80
81    fn inner(&self) -> &Inner {
82        &self.0.entry
83    }
84
85    fn inner_mut(&mut self) -> &mut Inner {
86        &mut self.0.entry
87    }
88}
89
90/// Builder for an new directory entry.
91///
92/// A `DirectoryBuilder` instance is returned by
93/// [`Archive::append_directory()`](crate::Archive::append_directory). Calling
94/// [`DirectoryBuilder::build()`] will create the entry at the end of the
95/// archive.
96pub struct DirectoryBuilder<'a, B: Backend>(InnerBuilder<'a, B>);
97
98impl<'a, B: Backend> DirectoryBuilder<'a, B> {
99    impl_new!(DirectoryBuilder, directory);
100
101    populate_mode_api!(mut);
102    populate_tstamp_api!(mut);
103
104    /// Finally, creates the new directory entry at the end of the archive.
105    pub fn build(self) -> ArchiveResult<(), B> {
106        self.0.build().map(|_| ())
107    }
108
109    fn inner(&self) -> &Inner {
110        &self.0.entry
111    }
112
113    fn inner_mut(&mut self) -> &mut Inner {
114        &mut self.0.entry
115    }
116}
117
118/// Builder for an new symlink entry.
119///
120/// A `SymlinkBuilder` instance is returned by
121/// [`Archive::append_symlink()`](crate::Archive::append_symlink). Calling
122/// [`SymlinkBuilder::build()`] will create the entry at the end of the
123/// archive.
124pub struct SymlinkBuilder<'a, B: Backend> {
125    builder: InnerBuilder<'a, B>,
126    target: String,
127}
128
129impl<'a, B: Backend> SymlinkBuilder<'a, B> {
130    pub(crate) fn new(
131        pager: &'a mut Pager<B>,
132        header_id: &'a Id<B>,
133        header: &'a mut Header,
134        tree: &'a mut Tree<B>,
135        name: String,
136        target: String,
137    ) -> SymlinkBuilder<'a, B> {
138        let builder = InnerBuilder::new(pager, header_id, header, tree, name, Mode::symlink());
139
140        SymlinkBuilder { builder, target }
141    }
142
143    populate_mode_api!(mut);
144    populate_tstamp_api!(mut);
145
146    /// Finally, creates the new symlink entry at the end of the archive.
147    pub fn build(self) -> ArchiveResult<(), B> {
148        let mut entry = self.builder.build()?;
149
150        entry.write_all(self.target.as_bytes())?;
151
152        Ok(())
153    }
154
155    fn inner(&self) -> &Inner {
156        &self.builder.entry
157    }
158
159    fn inner_mut(&mut self) -> &mut Inner {
160        &mut self.builder.entry
161    }
162}
163
164struct InnerBuilder<'a, B: Backend> {
165    pager: &'a mut Pager<B>,
166    header_id: &'a Id<B>,
167    header: &'a mut Header,
168    tree: &'a mut Tree<B>,
169    entry: Inner,
170}
171
172impl<'a, B: Backend> InnerBuilder<'a, B> {
173    fn new(
174        pager: &'a mut Pager<B>,
175        header_id: &'a Id<B>,
176        header: &'a mut Header,
177        tree: &'a mut Tree<B>,
178        name: String,
179        mode: Mode,
180    ) -> InnerBuilder<'a, B> {
181        InnerBuilder {
182            pager,
183            header_id,
184            header,
185            tree,
186            entry: Inner::new(name, mode),
187        }
188    }
189
190    fn build(self) -> ArchiveResult<EntryMut<'a, B>, B> {
191        let id = self.tree.aquire(self.pager)?.clone();
192
193        self.entry.flush(self.pager, &id)?;
194
195        self.header.inc_files();
196        flush_header(self.pager, self.header_id, self.header, self.tree)?;
197
198        Ok(EntryMut::new(
199            self.pager,
200            self.header_id,
201            self.header,
202            self.tree,
203            self.entry,
204            id,
205        ))
206    }
207}
208
209/// A mutable entry of the archive.
210///
211/// An `EntryMut` instance is returned by [`FileBuilder::build()`] and gives
212/// you the possibility to add content to the entry.
213pub struct EntryMut<'a, B: Backend> {
214    pager: &'a mut Pager<B>,
215    header_id: &'a Id<B>,
216    header: &'a mut Header,
217    tree: &'a mut Tree<B>,
218    entry: Inner,
219    first: Id<B>,
220    last: Id<B>,
221    cache: Vec<u8>,
222}
223
224impl<'a, B: Backend> EntryMut<'a, B> {
225    fn new(
226        pager: &'a mut Pager<B>,
227        header_id: &'a Id<B>,
228        header: &'a mut Header,
229        tree: &'a mut Tree<B>,
230        entry: Inner,
231        id: Id<B>,
232    ) -> EntryMut<'a, B> {
233        EntryMut {
234            pager,
235            header_id,
236            header,
237            tree,
238            entry,
239            first: id.clone(),
240            last: id,
241            cache: vec![],
242        }
243    }
244
245    /// Appends some content from `buf` at the end of the entry.
246    ///
247    /// Note that the entire buffer is not necessarily written. The method
248    /// returns the number of bytes that were actually written.
249    pub fn write(&mut self, buf: &[u8]) -> ArchiveResult<usize, B> {
250        let block_size = self.pager.block_size() as u64;
251        let pos = (self.entry.size % block_size) as usize;
252
253        let available = if pos == 0 {
254            self.last = self.tree.aquire(self.pager)?.clone();
255
256            debug!("block aquired: {}", self.last);
257
258            self.cache.clear();
259            self.cache.resize(block_size as usize, 0);
260
261            block_size as usize
262        } else {
263            assert_eq!(self.cache.len(), block_size as usize);
264
265            block_size as usize - pos
266        };
267
268        let nbytes = cmp::min(buf.len(), available as usize);
269
270        debug!(
271            "bsize={}, pos={}, available={}, nbytes={}",
272            block_size, pos, available, nbytes
273        );
274
275        self.cache[pos..pos + nbytes].copy_from_slice(&buf[..nbytes]);
276        self.pager.write(&self.last, &self.cache)?;
277
278        self.entry.size += nbytes as u64;
279        self.entry.flush(self.pager, &self.first)?;
280        flush_header(self.pager, self.header_id, self.header, self.tree)?;
281
282        Ok(nbytes)
283    }
284
285    pub fn write_all(&mut self, mut buf: &[u8]) -> ArchiveResult<(), B> {
286        while !buf.is_empty() {
287            let n = self.write(buf)?;
288
289            buf = &buf[n..]
290        }
291
292        Ok(())
293    }
294}