async_deflate_zip/writer/entry_options.rs
1use std::path::Path;
2use std::time::SystemTime;
3
4#[cfg(unix)]
5use std::os::unix::fs::{MetadataExt, PermissionsExt};
6
7use tokio::fs;
8
9/// Per-entry options for appending to a ZIP archive.
10///
11/// Passed to [`ZipWriter::append_file`](crate::writer::ZipWriter::append_file),
12/// [`ZipWriter::append_directory`](crate::writer::ZipWriter::append_directory),
13/// and [`ZipWriter::append_symlink`](crate::writer::ZipWriter::append_symlink)
14/// to control metadata such as
15/// modification time, Unix permissions, UID/GID, and file comment.
16///
17/// Use the convenience constructors for common cases:
18/// - [`file`](EntryOptions::file) — 0644 + current time
19/// - [`directory`](EntryOptions::directory) — 0755 + current time
20/// - [`symlink`](EntryOptions::symlink) — 0777 + current time
21/// - [`from_path`](EntryOptions::from_path) — read metadata from a real file
22///
23/// For full control, construct the struct directly:
24///
25/// ```rust,no_run
26/// use async_deflate_zip::EntryOptions;
27/// use std::time::SystemTime;
28///
29/// let opts = EntryOptions {
30/// mtime: SystemTime::UNIX_EPOCH,
31/// permissions: Some(0o755),
32/// uid_gid: Some((1000, 1000)),
33/// comment: Some("my file".to_string()),
34/// };
35/// ```
36pub struct EntryOptions {
37 /// Last modification time. Stored in MS-DOS format in the fixed CD fields
38 /// and as a Unix timestamp in the extended timestamp extra field (0x5455).
39 pub mtime: SystemTime,
40 /// Unix permission bits (e.g. `0o644`, `0o755`). The file type bit
41 /// (`S_IFREG`/`S_IFDIR`/`S_IFLNK`) is added automatically by the writer.
42 /// When `None`, no Unix permissions are written.
43 pub permissions: Option<u32>,
44 /// Unix user and group IDs. Stored in the 0x7875 (Ux) extra field.
45 pub uid_gid: Option<(u32, u32)>,
46 /// Per-entry comment stored in the Central Directory.
47 /// Maximum length is 65535 bytes. Comments longer than this will
48 /// cause `finalize` to return [`ZipError::FieldTooLong`](crate::error::ZipError::FieldTooLong).
49 pub comment: Option<String>,
50}
51
52impl Default for EntryOptions {
53 fn default() -> Self {
54 Self {
55 mtime: SystemTime::now(),
56 permissions: None,
57 uid_gid: None,
58 comment: None,
59 }
60 }
61}
62
63impl EntryOptions {
64 /// Read metadata (mtime, permissions, uid/gid) from a real filesystem path.
65 ///
66 /// The file's content is **not** read — only `symlink_metadata` is queried
67 /// so this works on symlinks, directories, and regular files alike.
68 ///
69 /// On Unix, permission bits are extracted from the file's mode and
70 /// UID/GID from the file's owner. On non-Unix platforms, permissions
71 /// default to `None` and UID/GID to `None`.
72 pub async fn from_path<T: AsRef<Path>>(path: T) -> std::io::Result<Self> {
73 let meta = fs::symlink_metadata(&path).await?;
74 Ok(Self {
75 mtime: meta.modified().unwrap_or_else(|_| SystemTime::now()),
76 #[cfg(unix)]
77 permissions: Some(meta.permissions().mode() & 0o7777),
78 #[cfg(not(unix))]
79 permissions: None,
80 uid_gid: {
81 #[cfg(unix)]
82 {
83 Some((meta.uid(), meta.gid()))
84 }
85 #[cfg(not(unix))]
86 {
87 None
88 }
89 },
90 comment: None,
91 })
92 }
93
94 /// Convenience options for a regular file: permissions `0o644`,
95 /// modification time set to now.
96 pub fn file() -> Self {
97 Self {
98 mtime: SystemTime::now(),
99 permissions: Some(0o644),
100 ..Default::default()
101 }
102 }
103
104 /// Convenience options for a directory: permissions `0o755`,
105 /// modification time set to now.
106 pub fn directory() -> Self {
107 Self {
108 mtime: SystemTime::now(),
109 permissions: Some(0o755),
110 ..Default::default()
111 }
112 }
113
114 /// Convenience options for a symbolic link: permissions `0o777`,
115 /// modification time set to now.
116 pub fn symlink() -> Self {
117 Self {
118 mtime: SystemTime::now(),
119 permissions: Some(0o777),
120 ..Default::default()
121 }
122 }
123}