Skip to content

Commit 3d567ef

Browse files
authored
cli: Improve config file handling (#2123)
1 parent 74ef79b commit 3d567ef

File tree

6 files changed

+93
-56
lines changed

6 files changed

+93
-56
lines changed

svix-cli/Cargo.lock

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

svix-cli/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ clap_complete = "4.5.38"
3535
colored_json = "5.0.0"
3636
concolor-clap = "0.1.0"
3737
dialoguer = "0.11.0"
38+
dirs = "6.0.0"
3839
figment = { version = "0.10.19", features = ["toml", "env"] }
40+
fs-err = "3.2.0"
3941
futures-util = "0.3.31"
40-
home = "0.5.11"
4142
http = "1.2.0"
4243
# Reduce compile times for idna (we don't need the fastest possible impl of UTS 46)
4344
# As per https://github.com/hsivonen/idna_adapter?tab=readme-ov-file#opting-to-use-unicode-rs

svix-cli/src/cmds/listen.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ impl ListenArgs {
2020
let cfg_path = get_config_file_path()?;
2121
if let Err(e) = updated_cfg.save_to_disk(&cfg_path).context(format!(
2222
"failed to save relay token to config file at `{}`",
23-
cfg_path.as_os_str().to_str().unwrap_or_default()
23+
cfg_path.display()
2424
)) {
25-
eprintln!("{e}");
25+
eprintln!("{e:#}");
2626
}
2727
token
2828
}

svix-cli/src/cmds/login.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub async fn prompt(_cfg: &Config) -> Result<()> {
4343
cfg.auth_token = Some(auth_token);
4444
let fp = config::get_config_file_path()?;
4545
if let Err(e) = cfg.save_to_disk(&fp) {
46-
eprintln!("\n{e}\n");
46+
eprintln!("\n{e:#}\n");
4747
anyhow::bail!(
4848
"Failed to configure the Svix CLI, please try again or try setting your auth \
4949
token manually `SVIX_AUTH_TOKEN` environment variable."
@@ -52,7 +52,7 @@ pub async fn prompt(_cfg: &Config) -> Result<()> {
5252

5353
println!(
5454
"All Set! Your config has been written to `{}`",
55-
fp.as_os_str().to_str().unwrap_or_default()
55+
fp.display()
5656
);
5757
println!(
5858
"Type `{} --help` to print the Svix CLI documentation!",

svix-cli/src/config.rs

Lines changed: 23 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use std::{
2-
fs::{File, OpenOptions},
32
io::Write,
43
path::{Path, PathBuf},
54
};
65

7-
use anyhow::Result;
6+
use anyhow::{Context as _, Result};
87
use figment::{
98
providers::{Env, Format, Toml},
109
Figment,
1110
};
11+
use fs_err::{self as fs, File};
1212
use serde::{Deserialize, Serialize};
1313

1414
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
@@ -30,29 +30,29 @@ pub struct Config {
3030
pub relay_disable_security: Option<bool>,
3131
}
3232

33-
fn config_file_open_opts() -> OpenOptions {
34-
OpenOptions::new()
35-
.create(true)
36-
.truncate(true)
37-
.write(true)
38-
.to_owned()
39-
}
33+
fn create_config_file(path: &Path) -> Result<File> {
34+
let dir = path
35+
.parent()
36+
.context("config file path must not be empty")?;
37+
fs::create_dir_all(dir)?;
4038

41-
#[cfg(windows)]
42-
fn open_config_file(path: &Path) -> Result<File> {
43-
Ok(config_file_open_opts().open(path)?)
44-
}
39+
let mut opts = File::options();
40+
opts.create(true).truncate(true).write(true);
41+
42+
#[cfg(unix)]
43+
{
44+
use std::os::unix::fs::OpenOptionsExt;
45+
46+
const FILE_MODE: u32 = 0o600;
47+
opts.options_mut().mode(FILE_MODE);
48+
}
4549

46-
#[cfg(unix)]
47-
fn open_config_file(path: &Path) -> Result<File> {
48-
use std::os::unix::fs::OpenOptionsExt;
49-
const FILE_MODE: u32 = 0o600;
50-
Ok(config_file_open_opts().mode(FILE_MODE).open(path)?)
50+
Ok(opts.open(path)?)
5151
}
5252

5353
impl Config {
5454
pub fn load() -> Result<Config> {
55-
let cfg_file = get_folder()?.join(FILE_NAME);
55+
let cfg_file = get_config_file_path()?;
5656
let config: Config = Figment::new()
5757
.merge(Toml::file(cfg_file))
5858
.merge(Env::prefixed("SVIX_"))
@@ -61,8 +61,7 @@ impl Config {
6161
}
6262

6363
pub fn save_to_disk(&self, path: &Path) -> Result<()> {
64-
let mut fh = open_config_file(path)?;
65-
64+
let mut fh = create_config_file(path)?;
6665
let source = &toml::to_string_pretty(self)?;
6766
fh.write_all(source.as_bytes())?;
6867
Ok(())
@@ -81,21 +80,9 @@ impl Config {
8180
const FILE_NAME: &str = "config.toml";
8281

8382
fn get_folder() -> Result<PathBuf> {
84-
let config_path = if cfg!(windows) {
85-
std::env::var("APPDATA")
86-
} else {
87-
std::env::var("XDG_CONFIG_HOME")
88-
};
89-
90-
let pb = match config_path {
91-
Ok(path) => PathBuf::from(path),
92-
Err(_e) => {
93-
// N.b. per <https://github.com/rust-lang/cargo/blob/master/crates/home/README.md> the
94-
// stdlib should be fixed as of Rust 1.86.
95-
home::home_dir().ok_or_else(|| anyhow::anyhow!("unable to find config path"))?
96-
}
97-
};
98-
Ok(pb.join(".config").join("svix"))
83+
Ok(dirs::config_dir()
84+
.context("unable to find config path")?
85+
.join("svix"))
9986
}
10087

10188
pub fn get_config_file_path() -> Result<PathBuf> {

svix-cli/src/relay/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,14 +191,14 @@ impl Client {
191191
async move {
192192
read_from_ws_loop(ws_rx, remote_tx, local_url.clone(), http_client.clone())
193193
.await
194-
.inspect_err(|e| eprintln!("read loop terminated: {e}"))
194+
.inspect_err(|e| eprintln!("read loop terminated: {e:#}"))
195195
}
196196
});
197197

198198
set.spawn(async move {
199199
send_to_ws_loop(remote_rx, ws_tx)
200200
.await
201-
.inspect_err(|e| eprintln!("write loop terminated: {e}"))
201+
.inspect_err(|e| eprintln!("write loop terminated: {e:#}"))
202202
});
203203

204204
// If any task terminates, trash the rest so we can reconnect.
@@ -248,7 +248,7 @@ pub async fn listen(
248248
let show_welcome_message = attempt_count == 0 || orig_token != client.token;
249249

250250
if let Err(e) = client.connect(show_welcome_message).await {
251-
eprintln!("Failed to connect to Webhook Relay: {e}");
251+
eprintln!("Failed to connect to Webhook Relay: {e:#}");
252252
if e.downcast_ref::<TokenInUse>().is_some() {
253253
eprintln!("Generating a new token for this session.");
254254
client.token = {
@@ -418,11 +418,11 @@ async fn handle_incoming_message(
418418
println!("<- Forwarding message id={msg_id} to: {local_url}");
419419
match make_local_request(client, local_url, data).await {
420420
Err(err) => {
421-
eprintln!("Failed to make request to local server: \n{err}");
421+
eprintln!("Failed to make request to local server: \n{err:#}");
422422
}
423423
Ok(resp) => {
424424
if let Err(err) = process_response(msg_id, resp, tx).await {
425-
eprintln!("Failed to read response from local server: \n{err}");
425+
eprintln!("Failed to read response from local server: \n{err:#}");
426426
}
427427
}
428428
}

0 commit comments

Comments
 (0)