diff --git a/lychee-bin/src/main.rs b/lychee-bin/src/main.rs index 5b81ed7..cdf67c6 100644 --- a/lychee-bin/src/main.rs +++ b/lychee-bin/src/main.rs @@ -60,6 +60,7 @@ use std::fs::{self, File}; use std::io::{self, BufRead, BufReader, ErrorKind, Write}; +use std::path::PathBuf; use std::sync::Arc; use anyhow::{Context, Error, Result}; @@ -67,8 +68,9 @@ use clap::Parser; use color::YELLOW; use commands::CommandParams; use formatters::response::ResponseFormatter; -use log::{info, warn}; +use log::{error, info, warn}; use openssl_sys as _; +use options::LYCHEE_CONFIG_FILE; // required for vendored-openssl feature use ring as _; // required for apple silicon @@ -103,6 +105,7 @@ enum ExitCode { #[allow(unused)] UnexpectedFailure = 1, LinkCheckFailure = 2, + ConfigFile = 3, } /// Ignore lines starting with this marker in `.lycheeignore` files @@ -147,8 +150,21 @@ fn load_config() -> Result { // Load a potentially existing config file and merge it into the config from // the CLI - if let Some(c) = Config::load_from_file(&opts.config_file)? { - opts.config.merge(c); + if let Some(config_file) = &opts.config_file { + match Config::load_from_file(config_file) { + Ok(c) => opts.config.merge(c), + Err(e) => { + error!("Error while loading config file {:?}: {}", config_file, e); + std::process::exit(ExitCode::ConfigFile as i32); + } + } + } else { + // If no config file was explicitly provided, we try to load the default + // config file from the current directory, but it's not an error if it + // doesn't exist. + if let Ok(c) = Config::load_from_file(&PathBuf::from(LYCHEE_CONFIG_FILE)) { + opts.config.merge(c); + } } if let Ok(lycheeignore) = File::open(LYCHEE_IGNORE_FILE) { diff --git a/lychee-bin/src/options.rs b/lychee-bin/src/options.rs index 33411be..c06ed19 100644 --- a/lychee-bin/src/options.rs +++ b/lychee-bin/src/options.rs @@ -9,10 +9,12 @@ use lychee_lib::{ }; use secrecy::{ExposeSecret, SecretString}; use serde::Deserialize; -use std::{collections::HashSet, fs, io::ErrorKind, path::PathBuf, str::FromStr, time::Duration}; +use std::path::Path; +use std::{collections::HashSet, fs, path::PathBuf, str::FromStr, time::Duration}; pub(crate) const LYCHEE_IGNORE_FILE: &str = ".lycheeignore"; pub(crate) const LYCHEE_CACHE_FILE: &str = ".lycheecache"; +pub(crate) const LYCHEE_CONFIG_FILE: &str = "lychee.toml"; const DEFAULT_METHOD: &str = "get"; const DEFAULT_MAX_CACHE_AGE: &str = "1d"; @@ -29,6 +31,14 @@ const HELP_MSG_CACHE: &str = formatcp!( "Use request cache stored on disk at `{}`", LYCHEE_CACHE_FILE, ); +// We use a custom help message here because we want to show the default +// value of the config file, but also be able to check if the user has +// provided a custom value. If they didn't, we won't throw an error if +// the file doesn't exist. +const HELP_MSG_CONFIG_FILE: &str = formatcp!( + "Configuration file to use\n\n[default: {}]", + LYCHEE_CONFIG_FILE, +); const TIMEOUT_STR: &str = concatcp!(DEFAULT_TIMEOUT_SECS); const RETRY_WAIT_TIME_STR: &str = concatcp!(DEFAULT_RETRY_WAIT_TIME_SECS); @@ -112,8 +122,9 @@ pub(crate) struct LycheeOptions { raw_inputs: Vec, /// Configuration file to use - #[arg(short, long = "config", default_value = "./lychee.toml")] - pub(crate) config_file: String, + #[arg(short, long = "config")] + #[arg(help = HELP_MSG_CONFIG_FILE)] + pub(crate) config_file: Option, #[clap(flatten)] pub(crate) config: Config, @@ -340,22 +351,10 @@ pub(crate) struct Config { impl Config { /// Load configuration from a file - pub(crate) fn load_from_file(path: &str) -> Result> { + pub(crate) fn load_from_file(path: &Path) -> Result { // Read configuration file - let result = fs::read_to_string(path); - - // Ignore a file-not-found error - let contents = match result { - Ok(c) => c, - Err(e) => { - return match e.kind() { - ErrorKind::NotFound => Ok(None), - _ => Err(Error::from(e)), - } - } - }; - - Ok(Some(toml::from_str(&contents)?)) + let contents = fs::read_to_string(path)?; + toml::from_str(&contents).context("Failed to parse configuration file") } /// Merge the configuration from TOML into the CLI configuration diff --git a/lychee-bin/tests/cli.rs b/lychee-bin/tests/cli.rs index f64889b..c53ddc8 100644 --- a/lychee-bin/tests/cli.rs +++ b/lychee-bin/tests/cli.rs @@ -574,23 +574,6 @@ mod cli { Ok(()) } - #[tokio::test] - async fn test_example_config() -> Result<()> { - let mock_server = mock_server!(StatusCode::OK); - let mut cmd = main_command(); - cmd.arg("--config") - .arg("lychee.example.toml") - .arg("-") - .write_stdin(mock_server.uri()) - .env_clear() - .assert() - .success() - .stdout(contains("1 Total")) - .stdout(contains("1 OK")); - - Ok(()) - } - #[tokio::test] async fn test_empty_config() -> Result<()> { let mock_server = mock_server!(StatusCode::OK);