dt_core/utils.rs
1use std::path::{Path, PathBuf};
2
3use crate::error::{Error as AppError, Result};
4
5/// Gets config path from environment variables, or infer one.
6///
7/// 1. If the environment variable indexed by `env_for_file`'s value is
8/// present, that environment variable's value is returned as the config file
9/// path.
10///
11/// # Example
12///
13/// ```
14/// # use dt_core::utils::default_config_path;
15/// # use std::path::PathBuf;
16/// # use std::str::FromStr;
17/// std::env::set_var("DT_CLI_CONFIG_PATH", "/tmp/dt/configuration.toml");
18/// assert_eq!(
19/// default_config_path::<&str>("DT_CLI_CONFIG_PATH", "", &[]),
20/// Ok(PathBuf::from_str("/tmp/dt/configuration.toml").unwrap()),
21/// );
22/// ```
23///
24/// 2. Otherwise, if the environment variable indexed by `env_for_dir`'s value
25/// is present, that environment variable's value is considered the parent
26/// directory of the returned config path, filenames within `search_list` will
27/// be checked in order and the first existing file's path will be returned.
28/// If none of the `search_list` exists, a fallback filename `config.toml`
29/// will be used.
30///
31/// # Example
32///
33/// ```
34/// # use dt_core::utils::default_config_path;
35/// # use std::path::PathBuf;
36/// # use std::str::FromStr;
37/// std::env::set_var("DT_CONFIG_DIR", "/tmp/d/t");
38/// assert_eq!(
39/// default_config_path::<&str>(
40/// "some_non_existing_var",
41/// "DT_CONFIG_DIR",
42/// &[],
43/// ),
44/// Ok(PathBuf::from_str("/tmp/d/t/config.toml").unwrap()),
45/// );
46/// ```
47///
48/// 3. When neither of `env_for_file`'s and `env_for_dir`'s corresponding
49/// environment variable exists, the parent directory of returned path is
50/// inferred as `$XDG_CONFIG_HOME/dt`, or `$HOME/.config/dt` if
51/// XDG_CONFIG_HOME is not set in the runtime environment.
52///
53/// # Example
54///
55/// ```
56/// # use dt_core::utils::default_config_path;
57/// # use std::path::PathBuf;
58/// # use std::str::FromStr;
59/// std::env::set_var("XDG_CONFIG_HOME", "/tmp/confighome");
60/// assert_eq!(
61/// default_config_path::<&str>(
62/// "some_non_existing_var",
63/// "some_other_non_existing_var",
64/// &[],
65/// ),
66/// Ok(PathBuf::from_str("/tmp/confighome/dt/config.toml").unwrap()),
67/// );
68///
69/// std::env::remove_var("XDG_CONFIG_HOME");
70/// std::env::set_var("HOME", "/tmp/home");
71/// assert_eq!(
72/// default_config_path::<&str>(
73/// "some_non_existing_var",
74/// "some_other_non_existing_var",
75/// &[],
76/// ),
77/// Ok(PathBuf::from_str("/tmp/home/.config/dt/config.toml").unwrap()),
78/// );
79/// ```
80pub fn default_config_path<P: AsRef<Path>>(
81 env_for_file: &str,
82 env_for_dir: &str,
83 search_list: &[P],
84) -> Result<PathBuf> {
85 if let Ok(file_path) = std::env::var(env_for_file) {
86 log::debug!(
87 "Using config file '{}' (from environment variable `{}`)",
88 file_path,
89 env_for_file,
90 );
91 Ok(file_path.into())
92 } else {
93 let dir_path = match std::env::var(env_for_dir) {
94 Ok(dir_path) => {
95 log::debug!(
96 "Using config directory '{}' (from environment variable `{}`)",
97 dir_path,
98 env_for_dir,
99 );
100 dir_path.into()
101 }
102 _ => {
103 if let Some(dir_path) = dirs::config_dir() {
104 log::debug!("Using config directory '{}' (inferred)", dir_path.display(),);
105 dir_path.join("dt")
106 } else {
107 return Err(AppError::ConfigError(
108 "Could not infer directory to config file".to_owned(),
109 ));
110 }
111 }
112 };
113 let mut file_path = dir_path.join("config.toml");
114 for p in search_list {
115 let candidate = dir_path.join(p);
116 if candidate.exists() {
117 file_path = candidate;
118 break;
119 }
120 }
121 log::debug!("Using config file '{}' (inferred)", file_path.display());
122 Ok(file_path)
123 }
124}
125
126/// Gets the host-specific suffix, according to given [`hostname_sep`] and
127/// current machine's hostname.
128///
129/// [`hostname_sep`]: crate::config::GlobalConfig::hostname_sep
130pub fn host_specific_suffix(hostname_sep: &str) -> String {
131 hostname_sep.to_owned()
132 + gethostname::gethostname()
133 .to_str()
134 .expect("Failed getting hostname")
135}
136
137#[cfg(test)]
138pub(crate) mod testing {
139 use std::{
140 ffi::OsString, fs::Permissions, os::unix::prelude::PermissionsExt, path::PathBuf,
141 str::FromStr,
142 };
143
144 use color_eyre::Report;
145
146 const TESTROOT: &str = "/tmp/dt-testing";
147
148 pub fn get_testroot(top_level: &str) -> PathBuf {
149 PathBuf::from_str(TESTROOT).unwrap().join(top_level)
150 }
151
152 pub fn prepare_directory(abspath: PathBuf, mode: u32) -> std::result::Result<PathBuf, Report> {
153 std::fs::create_dir_all(&abspath)?;
154 std::fs::set_permissions(&abspath, Permissions::from_mode(mode))?;
155 Ok(abspath)
156 }
157
158 pub fn prepare_file(abspath: PathBuf, mode: u32) -> std::result::Result<PathBuf, Report> {
159 if let Some(parent) = abspath.parent() {
160 std::fs::create_dir_all(parent)?;
161 }
162 std::fs::write(
163 &abspath,
164 "Created by: `dt_core::syncing::tests::prepare_file`\n",
165 )?;
166 std::fs::set_permissions(&abspath, Permissions::from_mode(mode))?;
167 Ok(abspath)
168 }
169
170 pub fn gethostname() -> OsString {
171 "r2d2".into()
172 }
173
174 pub fn get_current_uid() -> users::uid_t {
175 418
176 }
177
178 pub fn get_current_username() -> Option<OsString> {
179 Some("luke".into())
180 }
181
182 pub fn linux_os_release() -> crate::error::Result<sys_info::LinuxOSReleaseInfo> {
183 let info = sys_info::LinuxOSReleaseInfo {
184 id: Some("dt".into()),
185 id_like: Some("DotfileTemplater".into()),
186 name: Some("dt".into()),
187 pretty_name: Some("DT".into()),
188 version: Some("latest".into()),
189 version_id: Some("0.99.99".into()),
190 version_codename: Some("dummy-version_codename".into()),
191 ansi_color: Some("dummy-ansi_color".into()),
192 logo: Some("Buzz Lightyear".into()),
193 cpe_name: Some("dummy-cpe_name".into()),
194 build_id: Some("#somethingsomething".into()),
195 variant: Some("dummy-variant".into()),
196 variant_id: Some("dummy-variant_id".into()),
197 home_url: Some("https://github.com/blurgyy/dt/".into()),
198 documentation_url: Some("https://dt.cli.rs/".into()),
199 support_url: Some("https://github.com/blurgyy/dt/issues".into()),
200 bug_report_url: Some("https://github.com/blurgyy/dt/issues".into()),
201 privacy_policy_url: Some(
202 "https://github.com/blurgyy/dt/blob/main/CODE_OF_CONDUCT.md".into(),
203 ),
204 };
205 Ok(info)
206 }
207}
208
209// Author: Blurgy <gy@blurgy.xyz>
210// Date: Oct 03 2021, 02:54 [CST]