1use crate::prelude::*;
2
3use std::os::unix::fs::{DirBuilderExt as _, PermissionsExt as _};
4
5pub fn make_all() -> Result<()> {
6 create_dir_all_with_permissions(&cache_dir(), 0o700)?;
7 create_dir_all_with_permissions(&runtime_dir(), 0o700)?;
8 create_dir_all_with_permissions(&data_dir(), 0o700)?;
9
10 Ok(())
11}
12
13fn create_dir_all_with_permissions(
14 path: &std::path::Path,
15 mode: u32,
16) -> Result<()> {
17 std::fs::DirBuilder::new()
20 .recursive(true)
21 .mode(mode)
22 .create(path)
23 .map_err(|source| Error::CreateDirectory {
24 source,
25 file: path.to_path_buf(),
26 })?;
27 std::fs::set_permissions(path, std::fs::Permissions::from_mode(mode))
30 .map_err(|source| Error::CreateDirectory {
31 source,
32 file: path.to_path_buf(),
33 })?;
34 Ok(())
35}
36
37pub fn config_file() -> std::path::PathBuf {
38 config_dir().join("config.json")
39}
40
41const INVALID_PATH: &percent_encoding::AsciiSet =
42 &percent_encoding::CONTROLS.add(b'/').add(b'%').add(b':');
43pub fn db_file(server: &str, email: &str) -> std::path::PathBuf {
44 let server =
45 percent_encoding::percent_encode(server.as_bytes(), INVALID_PATH)
46 .to_string();
47 cache_dir().join(format!("{server}:{email}.json"))
48}
49
50pub fn pid_file() -> std::path::PathBuf {
51 runtime_dir().join("pidfile")
52}
53
54pub fn agent_stdout_file() -> std::path::PathBuf {
55 data_dir().join("agent.out")
56}
57
58pub fn agent_stderr_file() -> std::path::PathBuf {
59 data_dir().join("agent.err")
60}
61
62pub fn device_id_file() -> std::path::PathBuf {
63 data_dir().join("device_id")
64}
65
66pub fn socket_file() -> std::path::PathBuf {
67 runtime_dir().join("socket")
68}
69
70pub fn ssh_agent_socket_file() -> std::path::PathBuf {
71 runtime_dir().join("ssh-agent-socket")
72}
73
74fn config_dir() -> std::path::PathBuf {
75 let project_dirs =
76 directories::ProjectDirs::from("", "", &profile()).unwrap();
77 project_dirs.config_dir().to_path_buf()
78}
79
80fn cache_dir() -> std::path::PathBuf {
81 let project_dirs =
82 directories::ProjectDirs::from("", "", &profile()).unwrap();
83 project_dirs.cache_dir().to_path_buf()
84}
85
86fn data_dir() -> std::path::PathBuf {
87 let project_dirs =
88 directories::ProjectDirs::from("", "", &profile()).unwrap();
89 project_dirs.data_dir().to_path_buf()
90}
91
92fn runtime_dir() -> std::path::PathBuf {
93 let project_dirs =
94 directories::ProjectDirs::from("", "", &profile()).unwrap();
95 project_dirs.runtime_dir().map_or_else(
96 || {
97 format!(
98 "{}/{}-{}",
99 std::env::temp_dir().to_string_lossy(),
100 &profile(),
101 rustix::process::getuid().as_raw()
102 )
103 .into()
104 },
105 std::path::Path::to_path_buf,
106 )
107}
108
109pub fn profile() -> String {
110 match std::env::var("RBW_PROFILE") {
111 Ok(profile) if !profile.is_empty() => format!("rbw-{profile}"),
112 _ => "rbw".to_string(),
113 }
114}