mirror of
https://github.com/Hopiu/lychee.git
synced 2026-03-29 10:50:24 +00:00
Custom config handling to spot errors when passing invalid config and ignoring errors loading missing default conf
This commit is contained in:
parent
08d71e6196
commit
9eb3149a69
3 changed files with 36 additions and 38 deletions
|
|
@ -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<LycheeOptions> {
|
|||
|
||||
// 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) {
|
||||
|
|
|
|||
|
|
@ -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<String>,
|
||||
|
||||
/// 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<PathBuf>,
|
||||
|
||||
#[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<Option<Config>> {
|
||||
pub(crate) fn load_from_file(path: &Path) -> Result<Config> {
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue