Skip to content

Commit 5cfb907

Browse files
authored
refactor: use OS-specific config & data dirs, default iroh-store dir (#218)
* refactor: use OS-specific config & data dirs, default iroh-store dir switch to using https://dirs.dev standard application directories instead of $HOME/.iroh iroh-store now uses a default directory for datastore data instead of explicitly requiring a store path * refactor: util config/data paths return Results
1 parent 425611f commit 5cfb907

File tree

9 files changed

+92
-34
lines changed

9 files changed

+92
-34
lines changed

iroh-ctl/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use iroh_ctl::{
88
store::{run_command as run_store_command, Store},
99
};
1010
use iroh_rpc_client::Client;
11-
use iroh_util::{iroh_home_path, make_config};
11+
use iroh_util::{iroh_config_path, make_config};
1212

1313
use iroh_ctl::{
1414
config::{Config, CONFIG_FILE_NAME, ENV_PREFIX},
@@ -53,7 +53,8 @@ enum Commands {
5353
async fn main() -> anyhow::Result<()> {
5454
let cli = Cli::parse();
5555

56-
let sources = vec![iroh_home_path(CONFIG_FILE_NAME), cli.cfg.clone()];
56+
let cfg_path = iroh_config_path(CONFIG_FILE_NAME)?;
57+
let sources = vec![Some(cfg_path), cli.cfg.clone()];
5758
let config = make_config(
5859
// default
5960
Config::default(),

iroh-gateway/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,16 @@ use iroh_gateway::{
99
core::Core,
1010
metrics,
1111
};
12-
use iroh_util::{iroh_home_path, make_config};
12+
use iroh_util::{iroh_config_path, make_config};
1313
use tokio::sync::RwLock;
1414
use tracing::{debug, error};
1515

1616
#[tokio::main(flavor = "multi_thread")]
1717
async fn main() -> Result<()> {
1818
let args = Args::parse();
1919

20-
let sources = vec![iroh_home_path(CONFIG_FILE_NAME), args.cfg.clone()];
20+
let cfg_path = iroh_config_path(CONFIG_FILE_NAME)?;
21+
let sources = vec![Some(cfg_path), args.cfg.clone()];
2122
let mut config = make_config(
2223
// default
2324
Config::default(),

iroh-one/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use iroh_one::{
1111
config::{Config, CONFIG_FILE_NAME, ENV_PREFIX},
1212
};
1313
use iroh_rpc_types::Addr;
14-
use iroh_util::{iroh_home_path, make_config};
14+
use iroh_util::{iroh_config_path, make_config};
1515
#[cfg(feature = "uds-gateway")]
1616
use tempdir::TempDir;
1717
use tokio::sync::RwLock;
@@ -21,7 +21,8 @@ use tracing::{debug, error};
2121
async fn main() -> Result<()> {
2222
let args = Args::parse();
2323

24-
let sources = vec![iroh_home_path(CONFIG_FILE_NAME), args.cfg.clone()];
24+
let cfg_path = iroh_config_path(CONFIG_FILE_NAME)?;
25+
let sources = vec![Some(cfg_path), args.cfg.clone()];
2526
let mut config = make_config(
2627
// default
2728
Config::default(),

iroh-p2p/src/keys.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
55
use anyhow::{anyhow, Result};
66
use async_trait::async_trait;
77
use futures::{Stream, StreamExt, TryStreamExt};
8-
use iroh_util::iroh_home_root;
8+
use iroh_util::iroh_config_root;
99
use ssh_key::LineEnding;
1010
use tokio::fs;
1111
use tracing::warn;
@@ -113,9 +113,9 @@ impl Keychain<MemoryStorage> {
113113
}
114114

115115
impl Keychain<DiskStorage> {
116-
/// Creates a new on disk keychain, with the root defaulting to `.iroh`.
116+
/// Creates a new on disk keychain, with the root defaulting to the iroh config directory
117117
pub async fn new() -> Result<Self> {
118-
let root = iroh_home_root().ok_or_else(|| anyhow!("missing home path"))?;
118+
let root = iroh_config_root()?;
119119
Self::with_root(root).await
120120
}
121121

iroh-p2p/src/main.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use anyhow::anyhow;
22
use clap::Parser;
33
use iroh_p2p::config::{Config, CONFIG_FILE_NAME, ENV_PREFIX};
44
use iroh_p2p::{cli::Args, metrics, DiskStorage, Keychain, Node};
5-
use iroh_util::{iroh_home_path, make_config};
5+
use iroh_util::{iroh_config_path, make_config};
66
use tokio::task;
77
use tracing::{debug, error};
88

@@ -15,7 +15,8 @@ async fn main() -> anyhow::Result<()> {
1515
let args = Args::parse();
1616

1717
// TODO: configurable network
18-
let sources = vec![iroh_home_path(CONFIG_FILE_NAME), args.cfg.clone()];
18+
let cfg_path = iroh_config_path(CONFIG_FILE_NAME)?;
19+
let sources = vec![Some(cfg_path), args.cfg.clone()];
1920
let network_config = make_config(
2021
// default
2122
Config::default_grpc(),

iroh-store/src/config.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use iroh_rpc_types::{
66
store::{StoreClientAddr, StoreServerAddr},
77
Addr,
88
};
9-
use iroh_util::insert_into_config_map;
9+
use iroh_util::{insert_into_config_map, iroh_data_path};
1010
use serde::{Deserialize, Serialize};
1111
use std::path::PathBuf;
1212

@@ -17,6 +17,15 @@ pub const CONFIG_FILE_NAME: &str = "store.config.toml";
1717
/// For example, `IROH_STORE_PATH=/path/to/config` would set the value of the `Config.path` field
1818
pub const ENV_PREFIX: &str = "IROH_STORE";
1919

20+
/// the path to data directory. If arg_path is `None`, the default iroh_data_path()/store is used
21+
/// iroh_data_path() returns an operating system-specific directory
22+
pub fn config_data_path(arg_path: Option<PathBuf>) -> Result<PathBuf> {
23+
match arg_path {
24+
Some(p) => Ok(p),
25+
None => iroh_data_path("store"),
26+
}
27+
}
28+
2029
/// The configuration for the store.
2130
#[derive(PartialEq, Debug, Deserialize, Serialize, Clone)]
2231
pub struct Config {
@@ -129,4 +138,17 @@ mod tests {
129138

130139
assert_eq!(expect, got);
131140
}
141+
142+
#[test]
143+
fn test_config_data_path() {
144+
let path = PathBuf::new().join("arg_path");
145+
let path_given = config_data_path(Some(path.clone())).expect("config data path error");
146+
assert_eq!(path_given.display().to_string(), path.display().to_string());
147+
148+
let no_path_given = config_data_path(None)
149+
.expect("config data path error")
150+
.display()
151+
.to_string();
152+
assert!(no_path_given.ends_with("store"));
153+
}
132154
}

iroh-store/src/main.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ use anyhow::anyhow;
22
use clap::Parser;
33
use iroh_store::{
44
cli::Args,
5-
config::{CONFIG_FILE_NAME, ENV_PREFIX},
5+
config::{config_data_path, CONFIG_FILE_NAME, ENV_PREFIX},
66
metrics, rpc, Config, Store,
77
};
8-
use iroh_util::{block_until_sigint, iroh_home_path, make_config};
9-
use std::path::PathBuf;
8+
use iroh_util::{block_until_sigint, iroh_config_path, make_config};
109
use tracing::{debug, error, info};
1110

1211
#[tokio::main(flavor = "multi_thread")]
@@ -16,10 +15,12 @@ async fn main() -> anyhow::Result<()> {
1615
let version = env!("CARGO_PKG_VERSION");
1716
println!("Starting iroh-store, version {version}");
1817

19-
let sources = vec![iroh_home_path(CONFIG_FILE_NAME), args.cfg.clone()];
18+
let config_path = iroh_config_path(CONFIG_FILE_NAME)?;
19+
let sources = vec![Some(config_path), args.cfg.clone()];
20+
let config_data_path = config_data_path(args.path.clone())?;
2021
let config = make_config(
2122
// default
22-
Config::new_grpc(args.path.clone().unwrap_or_else(|| PathBuf::from(""))),
23+
Config::new_grpc(config_data_path),
2324
// potential config files
2425
sources,
2526
// env var prefix for this config

iroh-util/Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ futures = "0.3.21"
1414
anyhow = "1.0.57"
1515
toml = "0.5.9"
1616
serde = { version = "1.0", features = ["derive"] }
17-
dirs = "4.0.0"
1817
config = "0.13.1"
1918
tracing = "0.1.34"
2019
temp-env = "0.2.0"
21-
rlimit = "0.8.3"
20+
rlimit = "0.8.3"
21+
dirs-next = "2.0.0"

iroh-util/src/lib.rs

+46-15
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
use std::{
22
cell::RefCell,
33
collections::HashMap,
4-
path::{Path, PathBuf},
4+
path::PathBuf,
55
sync::{
66
atomic::{AtomicUsize, Ordering},
77
Arc,
88
},
99
};
1010

11-
use anyhow::Result;
11+
use anyhow::{anyhow, Result};
1212
use cid::{
1313
multihash::{Code, MultihashDigest},
1414
Cid,
1515
};
1616
use config::{Config, ConfigError, Environment, File, Map, Source, Value, ValueKind};
17-
use dirs::home_dir;
1817
use tracing::debug;
1918

20-
const IROH_DIR: &str = ".iroh";
19+
/// name of directory that wraps all iroh files in a given application directory
20+
const IROH_DIR: &str = "iroh";
2121
const DEFAULT_NOFILE_LIMIT: u64 = 65536;
2222
const MIN_NOFILE_LIMIT: u64 = 2048;
2323

@@ -44,16 +44,47 @@ pub async fn block_until_sigint() {
4444
ctrlc_oneshot.await.unwrap();
4545
}
4646

47-
/// Path to the iroh home directory.
48-
pub fn iroh_home_root() -> Option<PathBuf> {
49-
let home = home_dir()?;
50-
Some(Path::new(&home).join(IROH_DIR))
47+
/// Returns the path to the user's iroh config directory.
48+
///
49+
/// The returned value depends on the operating system and is either a `Some`, containing a value from the following table, or a `None`.
50+
///
51+
/// | Platform | Value | Example |
52+
/// | -------- | ------------------------------------- | -------------------------------- |
53+
/// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config/iroh | /home/alice/.config/iroh |
54+
/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh |
55+
/// | Windows | `{FOLDERID_RoamingAppData}`/iroh | C:\Users\Alice\AppData\Roaming\iroh |
56+
pub fn iroh_config_root() -> Result<PathBuf> {
57+
let cfg = dirs_next::config_dir()
58+
.ok_or_else(|| anyhow!("operating environment provides no directory for configuration"))?;
59+
Ok(cfg.join(&IROH_DIR))
60+
}
61+
62+
// Path that leads to a file in the iroh config directory.
63+
pub fn iroh_config_path(file_name: &str) -> Result<PathBuf> {
64+
let path = iroh_config_root()?.join(file_name);
65+
Ok(path)
66+
}
67+
68+
/// Returns the path to the user's iroh data directory.
69+
///
70+
/// The returned value depends on the operating system and is either a `Some`, containing a value from the following table, or a `None`.
71+
///
72+
/// | Platform | Value | Example |
73+
/// | -------- | --------------------------------------------- | ---------------------------------------- |
74+
/// | Linux | `$XDG_DATA_HOME`/iroh or `$HOME`/.local/share/iroh | /home/alice/.local/share/iroh |
75+
/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh |
76+
/// | Windows | `{FOLDERID_RoamingAppData}/iroh` | C:\Users\Alice\AppData\Roaming\iroh |
77+
pub fn iroh_data_root() -> Result<PathBuf> {
78+
let path = dirs_next::data_dir().ok_or_else(|| {
79+
anyhow!("operating environment provides no directory for application data")
80+
})?;
81+
Ok(path.join(&IROH_DIR))
5182
}
5283

53-
/// Path that leads to a file in the iroh home directory.
54-
pub fn iroh_home_path(file_name: &str) -> Option<PathBuf> {
55-
let path = iroh_home_root()?.join(file_name);
56-
Some(path)
84+
/// Path that leads to a file in the iroh data directory.
85+
pub fn iroh_data_path(file_name: &str) -> Result<PathBuf> {
86+
let path = iroh_data_root()?.join(file_name);
87+
Ok(path)
5788
}
5889

5990
/// insert a value into a `config::Map`
@@ -174,9 +205,9 @@ pub fn increase_fd_limit() -> std::io::Result<u64> {
174205
mod tests {
175206
use super::*;
176207
#[test]
177-
fn test_iroh_home_path() {
178-
let got = iroh_home_path("foo.bar").unwrap();
208+
fn test_iroh_config_path() {
209+
let got = iroh_config_path("foo.bar").unwrap();
179210
let got = got.to_str().unwrap().to_string();
180-
assert!(got.ends_with("/.iroh/foo.bar"));
211+
assert!(got.ends_with("/iroh/foo.bar"));
181212
}
182213
}

0 commit comments

Comments
 (0)