Skip to content

Commit 3ff914d

Browse files
rklaehndignifiedquiredivagant-martian
authored
refactor(iroh-cli)!: Use config and logging from iroh-node-utils (#2953)
## Description Move things like figuring out the configuration and data directory into iroh-node-utils as well, so it can be used from other binaries than iroh-cli. E.g. doctor, or a pure blobs bin, or for testing. ## Breaking Changes This affects iroh-cli, which is a bin crate. But nevertheless, here are the changes: - module iroh_cli::logging has moved to iroh-node-util to make it available for other people building nodes - iroh_config_root, iroh_data_root and iroh_cache_root in iroh-cli have been replaced with generic config_root, data_root and cache_root in iroh-node-util::config ## Notes & open questions <!-- Any notes, remarks or open questions you have to make about the PR. --> ## Change checklist - [ ] Self-review. - [ ] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [ ] Tests if relevant. - [ ] All breaking changes documented. --------- Co-authored-by: dignifiedquire <me@dignifiedquire.com> Co-authored-by: Diva M <divma@protonmail.com>
1 parent f174c8e commit 3ff914d

File tree

11 files changed

+174
-143
lines changed

11 files changed

+174
-143
lines changed

Cargo.lock

Lines changed: 7 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

iroh-cli/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ console = "0.15.5"
3333
crossterm = "0.27.0"
3434
derive_more = { version = "1.0.0", features = ["display"] }
3535
dialoguer = { version = "0.11.0", default-features = false }
36-
dirs-next = "2.0.0"
3736
futures-buffered = "0.2.4"
3837
futures-lite = "2.3"
3938
futures-util = { version = "0.3.30", features = ["futures-sink"] }
@@ -45,6 +44,7 @@ iroh-blobs = { version = "0.28.1", features = ["cli"] }
4544
iroh-docs = { version = "0.28.0", features = ["cli"] }
4645
iroh-gossip = { version = "0.28.1", features = ["cli"] }
4746
iroh-metrics = { version = "0.28.0" }
47+
iroh-node-util = { path = "../iroh-node-util", features = ["config", "logging"] }
4848
parking_lot = "0.12.1"
4949
pkarr = { version = "2.2.0", default-features = false }
5050
portable-atomic = "1"
@@ -56,7 +56,6 @@ ratatui = "0.26.2"
5656
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
5757
rustyline = "12.0.0"
5858
serde = { version = "1.0.197", features = ["derive"] }
59-
serde_with = "3.7.0"
6059
shell-words = "1.1.0"
6160
shellexpand = "3.1.0"
6261
strum = { version = "0.26.2", features = ["derive"] }
@@ -67,8 +66,6 @@ tokio = { version = "1.36.0", features = ["full"] }
6766
tokio-util = { version = "0.7.12", features = ["rt"] }
6867
toml = { version = "0.8.12", features = ["preserve_order"] }
6968
tracing = "0.1.40"
70-
tracing-appender = "0.2.3"
71-
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
7269

7370
[dev-dependencies]
7471
duct = "0.13.6"

iroh-cli/src/commands.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl Cli {
128128
)
129129
.await
130130
} else {
131-
crate::logging::init_terminal_logging()?;
131+
iroh_node_util::logging::init_terminal_logging()?;
132132
let iroh = if let Some(addr) = self.rpc_addr {
133133
Iroh::connect_addr(addr).await.context("rpc connect")?
134134
} else {
@@ -154,7 +154,7 @@ impl Cli {
154154
)
155155
.await
156156
} else {
157-
crate::logging::init_terminal_logging()?;
157+
iroh_node_util::logging::init_terminal_logging()?;
158158
let iroh = if let Some(addr) = self.rpc_addr {
159159
Iroh::connect_addr(addr).await.context("rpc connect")?
160160
} else {

iroh-cli/src/commands/doctor.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ use iroh::{
4444
util::{path::IrohPaths, progress::ProgressWriter},
4545
};
4646
use iroh_metrics::core::Core;
47+
use iroh_node_util::config::data_root;
4748
use portable_atomic::AtomicU64;
4849
use postcard::experimental::max_size::MaxSize;
4950
use rand::Rng;
@@ -53,7 +54,7 @@ use tokio::{io::AsyncWriteExt, sync};
5354
use tokio_util::task::AbortOnDropHandle;
5455
use tracing::warn;
5556

56-
use crate::config::{iroh_data_root, NodeConfig};
57+
use crate::config::NodeConfig;
5758

5859
/// Options for the secret key usage.
5960
#[derive(Debug, Clone, derive_more::Display)]
@@ -1016,7 +1017,7 @@ fn create_secret_key(secret_key: SecretKeyOption) -> anyhow::Result<SecretKey> {
10161017
SecretKey::try_from(&bytes[..])?
10171018
}
10181019
SecretKeyOption::Local => {
1019-
let path = IrohPaths::SecretKey.with_root(iroh_data_root()?);
1020+
let path = IrohPaths::SecretKey.with_root(data_root("iroh")?);
10201021
if path.exists() {
10211022
let bytes = std::fs::read(&path)?;
10221023
SecretKey::try_from_openssh(bytes)?
@@ -1108,8 +1109,9 @@ fn inspect_ticket(ticket: &str, zbase32: bool) -> anyhow::Result<()> {
11081109

11091110
/// Runs the doctor commands.
11101111
pub async fn run(command: Commands, config: &NodeConfig) -> anyhow::Result<()> {
1111-
let data_dir = iroh_data_root()?;
1112-
let _guard = crate::logging::init_terminal_and_file_logging(&config.file_logs, &data_dir)?;
1112+
let data_dir = data_root("iroh")?;
1113+
let _guard =
1114+
iroh_node_util::logging::init_terminal_and_file_logging(&config.file_logs, &data_dir)?;
11131115
let metrics_fut = super::start::start_metrics_server(config.metrics_addr);
11141116
let cmd_res = match command {
11151117
Commands::Report {

iroh-cli/src/commands/start.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ where
4646
F: FnOnce(iroh::client::Iroh) -> T + Send + 'static,
4747
T: Future<Output = Result<()>> + 'static,
4848
{
49-
let _guard = crate::logging::init_terminal_and_file_logging(&config.file_logs, iroh_data_root)?;
49+
let _guard =
50+
iroh_node_util::logging::init_terminal_and_file_logging(&config.file_logs, iroh_data_root)?;
5051
let metrics_fut = start_metrics_server(config.metrics_addr);
5152
let metrics_dumper_fut =
5253
start_metrics_dumper(config.metrics_dump_path.clone(), Duration::from_millis(100));

iroh-cli/src/config.rs

Lines changed: 13 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
//! Configuration for the iroh CLI.
22
33
use std::{
4-
env,
54
net::SocketAddr,
65
path::{Path, PathBuf},
7-
str::FromStr,
86
sync::Arc,
97
time::Duration,
108
};
119

12-
use anyhow::{anyhow, Result};
10+
use anyhow::Result;
1311
use iroh::{
1412
net::{RelayMap, RelayNode},
1513
node::GcPolicy,
1614
};
15+
use iroh_node_util::{config::config_root, logging::env_file_rust_log};
1716
use serde::Deserialize;
1817

19-
const ENV_CONFIG_DIR: &str = "IROH_CONFIG_DIR";
20-
const ENV_FILE_RUST_LOG: &str = "IROH_FILE_RUST_LOG";
18+
/// BIN_NAME is the name of the binary. This is used in various places, e.g. for the home directory
19+
/// and for environment variables.
20+
pub(crate) const BIN_NAME: &str = "iroh";
2121

2222
/// CONFIG_FILE_NAME is the name of the optional config file located in the iroh home directory
2323
pub(crate) const CONFIG_FILE_NAME: &str = "iroh.config.toml";
@@ -52,7 +52,7 @@ pub(crate) struct NodeConfig {
5252
/// Bind address on which to serve Prometheus metrics
5353
pub(crate) metrics_addr: Option<SocketAddr>,
5454
/// Configuration for the logfile.
55-
pub(crate) file_logs: super::logging::FileLogging,
55+
pub(crate) file_logs: iroh_node_util::logging::FileLogging,
5656
/// Path to dump metrics to in CSV format.
5757
pub(crate) metrics_dump_path: Option<PathBuf>,
5858
}
@@ -81,7 +81,7 @@ impl NodeConfig {
8181
/// default config file will be loaded. If that is not present the default config will
8282
/// be used.
8383
pub(crate) async fn load(file: Option<&Path>) -> Result<NodeConfig> {
84-
let default_config = iroh_config_path(CONFIG_FILE_NAME)?;
84+
let default_config = config_root(BIN_NAME)?.join(CONFIG_FILE_NAME);
8585

8686
let config_file = match file {
8787
Some(file) => Some(file),
@@ -101,7 +101,7 @@ impl NodeConfig {
101101
};
102102

103103
// override from env var
104-
if let Some(env_filter) = env_file_rust_log().transpose()? {
104+
if let Some(env_filter) = env_file_rust_log(BIN_NAME).transpose()? {
105105
config.file_logs.rust_log = env_filter;
106106
}
107107
Ok(config)
@@ -144,112 +144,17 @@ impl From<GcPolicyConfig> for GcPolicy {
144144
}
145145
}
146146

147-
/// Parse [`ENV_FILE_RUST_LOG`] as [`tracing_subscriber::EnvFilter`]. Returns `None` if not
148-
/// present.
149-
fn env_file_rust_log() -> Option<Result<crate::logging::EnvFilter>> {
150-
match env::var(ENV_FILE_RUST_LOG) {
151-
Ok(s) => Some(crate::logging::EnvFilter::from_str(&s).map_err(Into::into)),
152-
Err(e) => match e {
153-
env::VarError::NotPresent => None,
154-
e @ env::VarError::NotUnicode(_) => Some(Err(e.into())),
155-
},
156-
}
157-
}
158-
159-
/// Name of directory that wraps all iroh files in a given application directory
160-
const IROH_DIR: &str = "iroh";
161-
162-
/// Returns the path to the user's iroh config directory.
163-
///
164-
/// If the `IROH_CONFIG_DIR` environment variable is set it will be used unconditionally.
165-
/// Otherwise the returned value depends on the operating system according to the following
166-
/// table.
167-
///
168-
/// | Platform | Value | Example |
169-
/// | -------- | ------------------------------------- | -------------------------------- |
170-
/// | Linux | `$XDG_CONFIG_HOME` or `$HOME`/.config/iroh | /home/alice/.config/iroh |
171-
/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh |
172-
/// | Windows | `{FOLDERID_RoamingAppData}`/iroh | C:\Users\Alice\AppData\Roaming\iroh |
173-
pub(crate) fn iroh_config_root() -> Result<PathBuf> {
174-
if let Some(val) = env::var_os(ENV_CONFIG_DIR) {
175-
return Ok(PathBuf::from(val));
176-
}
177-
let cfg = dirs_next::config_dir()
178-
.ok_or_else(|| anyhow!("operating environment provides no directory for configuration"))?;
179-
Ok(cfg.join(IROH_DIR))
180-
}
181-
182-
/// Path that leads to a file in the iroh config directory.
183-
pub(crate) fn iroh_config_path(file_name: impl AsRef<Path>) -> Result<PathBuf> {
184-
let path = iroh_config_root()?.join(file_name);
185-
Ok(path)
186-
}
187-
188-
/// Returns the path to the user's iroh data directory.
189-
///
190-
/// If the `IROH_DATA_DIR` environment variable is set it will be used unconditionally.
191-
/// Otherwise the returned value depends on the operating system according to the following
192-
/// table.
193-
///
194-
/// | Platform | Value | Example |
195-
/// | -------- | --------------------------------------------- | ---------------------------------------- |
196-
/// | Linux | `$XDG_DATA_HOME`/iroh or `$HOME`/.local/share/iroh | /home/alice/.local/share/iroh |
197-
/// | macOS | `$HOME`/Library/Application Support/iroh | /Users/Alice/Library/Application Support/iroh |
198-
/// | Windows | `{FOLDERID_RoamingAppData}/iroh` | C:\Users\Alice\AppData\Roaming\iroh |
199-
pub(crate) fn iroh_data_root() -> Result<PathBuf> {
200-
let path = if let Some(val) = env::var_os("IROH_DATA_DIR") {
201-
PathBuf::from(val)
202-
} else {
203-
let path = dirs_next::data_dir().ok_or_else(|| {
204-
anyhow!("operating environment provides no directory for application data")
205-
})?;
206-
path.join(IROH_DIR)
207-
};
208-
let path = if !path.is_absolute() {
209-
std::env::current_dir()?.join(path)
210-
} else {
211-
path
212-
};
213-
Ok(path)
214-
}
215-
216-
/// Returns the path to the user's iroh cache directory.
217-
///
218-
/// If the `IROH_CACHE_DIR` environment variable is set it will be used unconditionally.
219-
/// Otherwise the returned value depends on the operating system according to the following
220-
/// table.
221-
///
222-
/// | Platform | Value | Example |
223-
/// | -------- | --------------------------------------------- | ---------------------------------------- |
224-
/// | Linux | `$XDG_CACHE_HOME`/iroh or `$HOME`/.cache/iroh | /home/.cache/iroh |
225-
/// | macOS | `$HOME`/Library/Caches/iroh | /Users/Alice/Library/Caches/iroh |
226-
/// | Windows | `{FOLDERID_LocalAppData}/iroh` | C:\Users\Alice\AppData\Roaming\iroh |
227-
#[allow(dead_code)]
228-
pub(crate) fn iroh_cache_root() -> Result<PathBuf> {
229-
if let Some(val) = env::var_os("IROH_CACHE_DIR") {
230-
return Ok(PathBuf::from(val));
231-
}
232-
let path = dirs_next::cache_dir().ok_or_else(|| {
233-
anyhow!("operating environment provides no directory for application data")
234-
})?;
235-
Ok(path.join(IROH_DIR))
236-
}
237-
238-
/// Path that leads to a file in the iroh cache directory.
239-
#[allow(dead_code)]
240-
pub(crate) fn iroh_cache_path(file_name: &Path) -> Result<PathBuf> {
241-
let path = iroh_cache_root()?.join(file_name);
242-
Ok(path)
243-
}
244-
245147
#[cfg(test)]
246148
mod tests {
247-
use std::net::{Ipv4Addr, Ipv6Addr};
149+
use std::{
150+
net::{Ipv4Addr, Ipv6Addr},
151+
str::FromStr,
152+
};
248153

154+
use iroh_node_util::logging::{EnvFilter, Rotation};
249155
use url::Url;
250156

251157
use super::*;
252-
use crate::logging::{EnvFilter, Rotation};
253158

254159
#[test]
255160
fn test_toml_invalid_field() {

iroh-cli/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ use std::time::Duration;
22

33
use anyhow::Result;
44
use clap::Parser;
5+
use config::BIN_NAME;
6+
use iroh_node_util::config::data_root;
57

68
mod commands;
79
mod config;
8-
mod logging;
910

1011
use crate::commands::Cli;
1112

@@ -24,7 +25,7 @@ fn main() -> Result<()> {
2425
}
2526

2627
async fn main_impl() -> Result<()> {
27-
let data_dir = config::iroh_data_root()?;
28+
let data_dir = data_root(BIN_NAME)?;
2829
let cli = Cli::parse();
2930
cli.run(&data_dir).await
3031
}

iroh-node-util/Cargo.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,18 @@ serde = "1"
2828
serde-error = "0.1.3"
2929
futures-lite = "2.5.0"
3030
tracing = "0.1.40"
31+
32+
derive_more = { version = "1.0.0", features = ["display"], optional = true }
33+
dirs-next = { version = "2.0.0", optional = true }
34+
rustyline = { version = "12.0.0", optional = true }
35+
serde_with = { version = "3.7.0", optional = true }
36+
tracing-appender = { version = "0.2.3", optional = true }
37+
tracing-subscriber = { version = "0.3", features = ["env-filter"], optional = true }
38+
39+
[features]
40+
logging = ["dep:derive_more", "dep:serde_with", "dep:rustyline", "dep:tracing-appender", "dep:tracing-subscriber"]
41+
config = ["dep:dirs-next"]
42+
43+
[package.metadata.docs.rs]
44+
all-features = true
45+
rustdoc-args = ["--cfg", "iroh_docsrs"]

0 commit comments

Comments
 (0)