1pub mod environment;
20mod hidden_input;
21pub(crate) mod path_serde;
22pub(crate) mod paths;
23pub(crate) mod program;
24#[cfg(unix)]
25mod unix;
26#[cfg(windows)]
27mod windows;
28
29#[cfg(unix)]
30use unix as os;
31#[cfg(windows)]
32use windows as os;
33
34pub(crate) use os::{
35 CRLF_LINE_ENDINGS, Child, DEFAULT_EDITOR, ENV_NAMES_IGNORE_CASE, HOME_VARIABLE, HiddenInput,
36 PrivateTempDir, STATE_VARIABLES, TEMP_VARIABLES, USER_VARIABLES, VISIBLE_INPUT_FALLBACK,
37 create_private_dir, create_private_dir_all, editor, exit_signal, file_identity, home_directory,
38 host_name, ipc, is_owner_only, is_private_dir, is_private_file, kill_saved_group,
39 path_from_bytes, private_options, private_tempdir_in, process_start_time,
40 remove_abandoned_directory, resolve_system_aliases, restrict_file, running_process_start_time,
41 spawn_detached, spawn_gated, stop_requested, sync_dir, transient_file_access,
42};
43pub(crate) use os::{PRIVATE_DIRECTORY, PRIVATE_FILE};
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub(crate) struct FileIdentity {
49 volume: u64,
50 index: u64,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub(crate) enum ProcessEnd {
56 Exited(i32),
58 Signaled(Option<i32>),
60}
61
62pub(crate) fn process_end(status: &std::process::ExitStatus) -> ProcessEnd {
64 match status.code() {
65 Some(code) => ProcessEnd::Exited(code),
66 None => ProcessEnd::Signaled(exit_signal(status)),
67 }
68}
69
70pub(crate) fn exit_description(status: &std::process::ExitStatus) -> String {
73 status.to_string()
74}
75
76pub(crate) const FILE_LOCK_RETRY_INTERVAL: std::time::Duration =
79 std::time::Duration::from_millis(25);
80
81pub use artifactize_tools::files::path_text;
82pub(crate) use artifactize_tools::files::{
83 EntryName, FileKind, canonicalize, entry_kind, link_target, open_directory, open_entry,
84 open_no_follow, open_nonblocking, read_dir,
85};
86
87pub fn open_regular(path: &std::path::Path) -> std::io::Result<std::fs::File> {
90 let file = open_no_follow(std::fs::OpenOptions::new().read(true), path)?;
91 if file.metadata()?.is_file() {
92 Ok(file)
93 } else {
94 Err(std::io::Error::new(
95 std::io::ErrorKind::InvalidInput,
96 "not a regular file",
97 ))
98 }
99}
100
101pub(crate) fn state_directory() -> Option<std::path::PathBuf> {
103 resolve_state_directory(
104 environment::var("ARTIFACTIZE_STATE_HOME").map(Into::into),
105 STATE_VARIABLES
106 .iter()
107 .find_map(|name| environment::var(name).filter(|v| !v.is_empty()))
108 .map(Into::into),
109 home_directory(),
110 )
111}
112
113fn resolve_state_directory(
114 artifactize: Option<std::path::PathBuf>,
115 state: Option<std::path::PathBuf>,
116 home: Option<std::path::PathBuf>,
117) -> Option<std::path::PathBuf> {
118 artifactize
119 .filter(|p| !p.as_os_str().is_empty())
120 .or_else(|| {
121 state
122 .filter(|p| !p.as_os_str().is_empty())
123 .map(|p| p.join("artifactize"))
124 })
125 .or_else(|| {
126 home.filter(|p| !p.as_os_str().is_empty())
127 .map(|p| p.join(".local/state/artifactize"))
128 })
129}
130
131pub(crate) fn user_name() -> Option<String> {
133 USER_VARIABLES
134 .iter()
135 .find_map(|name| environment::var_text(name).filter(|value| !value.is_empty()))
136}
137
138pub(crate) fn fingerprint_line_ending(stdout: &[u8]) -> (&[u8], &'static str) {
140 if CRLF_LINE_ENDINGS {
141 (
142 stdout
143 .strip_suffix(b"\r\n")
144 .or_else(|| stdout.strip_suffix(b"\n"))
145 .unwrap_or(stdout),
146 "LF or CRLF",
147 )
148 } else {
149 (stdout.strip_suffix(b"\n").unwrap_or(stdout), "LF")
150 }
151}
152
153pub(crate) fn path_kind(path: &std::path::Path) -> std::io::Result<FileKind> {
155 match (path.parent(), path.file_name()) {
156 (Some(parent), Some(name)) => entry_kind(&open_directory(parent)?, name),
157 _ => open_directory(path).map(|_| FileKind::Directory),
158 }
159}
160
161pub(crate) fn serialize_paths<S: serde::Serializer>(
163 paths: &[std::path::PathBuf],
164 serializer: S,
165) -> Result<S::Ok, S::Error> {
166 use serde::Serialize;
167 paths
168 .iter()
169 .map(|path| path_text(path))
170 .collect::<Vec<_>>()
171 .serialize(serializer)
172}
173
174#[cfg(test)]
175mod state_directory_tests {
176 use super::*;
177 use std::path::PathBuf;
178
179 #[test]
180 fn state_home_precedence_and_empty_values() {
181 let artifactize = PathBuf::from("custom/state");
182 let state = PathBuf::from("state");
183 let home = PathBuf::from("reviewer");
184 assert_eq!(
185 resolve_state_directory(
186 Some(artifactize.clone()),
187 Some(state.clone()),
188 Some(home.clone())
189 ),
190 Some(artifactize)
191 );
192 assert_eq!(
193 resolve_state_directory(None, Some(state.clone()), Some(home.clone())),
194 Some(state.join("artifactize"))
195 );
196 assert_eq!(
197 resolve_state_directory(None, None, Some(home.clone())),
198 Some(home.join(".local/state/artifactize"))
199 );
200 let empty = Some(PathBuf::new());
201 assert_eq!(
202 resolve_state_directory(empty.clone(), empty.clone(), Some(home.clone())),
203 Some(home.join(".local/state/artifactize"))
204 );
205 assert_eq!(
206 resolve_state_directory(empty.clone(), empty.clone(), empty),
207 None
208 );
209 assert_eq!(resolve_state_directory(None, None, None), None);
210 }
211}
212
213mod console;
214pub(crate) use console::pause_after_help;
215
216mod interaction;
217pub use interaction::{PrivateTempFile, private_temp_file, run_editor, selected_editor};
218pub(crate) use interaction::{is_within, label, paths_equal, private_anonymous_file};
219
220pub(crate) fn entry_exists(path: &std::path::Path) -> std::io::Result<bool> {
222 let Some(parent) = path.parent() else {
223 return Ok(false);
224 };
225 let directory = match open_directory(parent) {
226 Ok(directory) => directory,
227 Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(false),
228 Err(error) => return Err(error),
229 };
230 let Some(name) = path.file_name() else {
231 return Ok(false);
232 };
233 match entry_kind(&directory, name) {
234 Ok(_) => Ok(true),
235 Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(false),
236 Err(error) => Err(error),
237 }
238}
239
240pub(crate) fn marker_metadata(path: &std::path::Path) -> std::io::Result<std::fs::Metadata> {
245 path.symlink_metadata()
246}