gix_archive/
lib.rs

1//! The implementation of creating an archive from a worktree stream, similar to `git archive`.
2//!
3//! ## Deviation
4//!
5//! This implementation is early and just does the basics. Git does more to support more context when filtering and to keep
6//! more information about entries in the various archive formats.
7//! `tar` is implemented in a very basic fashion only.
8//!
9//! ## Feature Flags
10//! All features are related to which container formats are available.
11#![cfg_attr(
12    all(doc, feature = "document-features"),
13    doc = ::document_features::document_features!()
14)]
15#![cfg_attr(all(doc, feature = "document-features"), feature(doc_cfg, doc_auto_cfg))]
16#![deny(rust_2018_idioms, missing_docs)]
17#![forbid(unsafe_code)]
18
19use bstr::BString;
20
21/// The error returned by [`write_stream()`].
22#[derive(Debug, thiserror::Error)]
23#[allow(missing_docs)]
24pub enum Error {
25    #[error(transparent)]
26    Io(#[from] std::io::Error),
27    #[error(transparent)]
28    NextStreamEntry(#[from] gix_worktree_stream::entry::Error),
29    #[error("The internal format cannot be used as an archive, it's merely a debugging tool")]
30    InternalFormatMustNotPersist,
31    #[error("Support for the format '{wanted:?}' was not compiled in")]
32    SupportNotCompiledIn { wanted: Format },
33    #[error("Cannot create a zip archive if output stream does not support seek")]
34    ZipWithoutSeek,
35    #[error("Cannot use modification as it is not within the supported bounds")]
36    InvalidModificationTime(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
37}
38
39/// The supported container formats for use in [`write_stream()`].
40#[derive(Default, PartialEq, Eq, Copy, Clone, Debug)]
41pub enum Format {
42    /// An internal format that is suitable only for intra-process communication.
43    ///
44    /// All transformations in the options are ignored. Calling [`write_stream`] is disallowed
45    /// as it's more efficient to call [gix_worktree_stream::Stream::into_read()] right away.
46    /// It is provided here as a basis available without extra dependencies, and as a debugging tool.
47    #[default]
48    InternalTransientNonPersistable,
49    /// A standard `tar` archive.
50    ///
51    /// Use it as well if a custom container format is desired. The idea is to decode it on a separate thread
52    /// to rewrite the data to the desired format.
53    Tar,
54    /// A convenience format that will `gzip` deflate the `tar` stream.
55    TarGz {
56        /// If `None`, use the default compression level. Otherwise use the given one which
57        /// ranges from 0-9 for the deflate algorithm.
58        compression_level: Option<u8>,
59    },
60    /// A standard `zip` archive. Note that this format silently converts illformed UTF-8 to UTF-8, which will
61    /// equal a change of path.
62    ///
63    /// Requires the `zip` feature toggle to have an effect.
64    ///
65    /// ### Shortcoming
66    ///
67    /// Even though symlinks are stored as such, for some reason at least on MacOS those aren't restored. That works,
68    /// however, when letting `git` create the archive.
69    Zip {
70        /// If `None`, use the default compression level. Otherwise use the given one which
71        /// ranges from 0-9 for the deflate algorithm.
72        compression_level: Option<u8>,
73    },
74}
75
76/// Options for configuring [`write_stream()`].
77#[derive(Clone, Debug)]
78pub struct Options {
79    /// The archive's format.
80    pub format: Format,
81    /// Given a `path`, originating in the git tree, to place into the archive, put `<prefix>/path` in front of it.
82    ///
83    /// Note that that `/` should be used as separator, and that a prefix directory has to end with `/`.
84    pub tree_prefix: Option<BString>,
85    /// The modification time for all entries in the archive as seen since UNIX epoch.
86    ///
87    /// Defaults to the current time. The caller may set this to the commit time if available.
88    pub modification_time: gix_date::SecondsSinceUnixEpoch,
89}
90
91impl Default for Options {
92    fn default() -> Self {
93        Options {
94            format: Default::default(),
95            tree_prefix: None,
96            modification_time: std::time::SystemTime::now()
97                .duration_since(std::time::UNIX_EPOCH)
98                .map(|t| t.as_secs() as i64)
99                .unwrap_or_default(),
100        }
101    }
102}
103
104mod write;
105pub use write::{write_stream, write_stream_seek};