chore: apply cargo fmt to core crate
config.rs had two single-line struct variants and a let-chain that rustfmt (edition 2024) wants expanded; db.rs test module picked up the same treatment. Whitespace-only - no behaviour change. Why: 'cargo fmt --check' is part of every task's definition of done, and main was handing over slightly dirty.
This commit is contained in:
@@ -18,9 +18,17 @@ pub enum ConfigError {
|
|||||||
#[error("config file not found: {0}")]
|
#[error("config file not found: {0}")]
|
||||||
NotFound(PathBuf),
|
NotFound(PathBuf),
|
||||||
#[error("failed to read config file {path}: {source}")]
|
#[error("failed to read config file {path}: {source}")]
|
||||||
Io { path: PathBuf, #[source] source: std::io::Error },
|
Io {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: std::io::Error,
|
||||||
|
},
|
||||||
#[error("failed to parse config file {path}: {source}")]
|
#[error("failed to parse config file {path}: {source}")]
|
||||||
Parse { path: PathBuf, #[source] source: toml::de::Error },
|
Parse {
|
||||||
|
path: PathBuf,
|
||||||
|
#[source]
|
||||||
|
source: toml::de::Error,
|
||||||
|
},
|
||||||
#[error("invalid config: {field} — {message}")]
|
#[error("invalid config: {field} — {message}")]
|
||||||
Invalid { field: String, message: String },
|
Invalid { field: String, message: String },
|
||||||
}
|
}
|
||||||
@@ -145,12 +153,13 @@ pub const DEFAULT_WEB_PORT: u16 = 8080;
|
|||||||
impl Config {
|
impl Config {
|
||||||
/// Load from `path`, deriving the default state dir from `$HOME`.
|
/// Load from `path`, deriving the default state dir from `$HOME`.
|
||||||
pub fn load(path: &Path) -> Result<Config, ConfigError> {
|
pub fn load(path: &Path) -> Result<Config, ConfigError> {
|
||||||
let home = std::env::var_os("HOME").map(PathBuf::from).ok_or_else(|| {
|
let home =
|
||||||
ConfigError::Invalid {
|
std::env::var_os("HOME")
|
||||||
field: "state.dir".to_string(),
|
.map(PathBuf::from)
|
||||||
message: "$HOME is unset; set [state].dir explicitly".to_string(),
|
.ok_or_else(|| ConfigError::Invalid {
|
||||||
}
|
field: "state.dir".to_string(),
|
||||||
})?;
|
message: "$HOME is unset; set [state].dir explicitly".to_string(),
|
||||||
|
})?;
|
||||||
Self::load_with(path, &home)
|
Self::load_with(path, &home)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,17 +169,25 @@ impl Config {
|
|||||||
if e.kind() == std::io::ErrorKind::NotFound {
|
if e.kind() == std::io::ErrorKind::NotFound {
|
||||||
ConfigError::NotFound(path.to_path_buf())
|
ConfigError::NotFound(path.to_path_buf())
|
||||||
} else {
|
} else {
|
||||||
ConfigError::Io { path: path.to_path_buf(), source: e }
|
ConfigError::Io {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
source: e,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
let raw: RawConfig =
|
let raw: RawConfig = toml::from_str(&text).map_err(|e| ConfigError::Parse {
|
||||||
toml::from_str(&text).map_err(|e| ConfigError::Parse { path: path.to_path_buf(), source: e })?;
|
path: path.to_path_buf(),
|
||||||
|
source: e,
|
||||||
|
})?;
|
||||||
Self::from_raw(raw, home)
|
Self::from_raw(raw, home)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_raw(raw: RawConfig, home: &Path) -> Result<Config, ConfigError> {
|
fn from_raw(raw: RawConfig, home: &Path) -> Result<Config, ConfigError> {
|
||||||
let state = if raw.state.dir.is_empty() {
|
let state = if raw.state.dir.is_empty() {
|
||||||
home.join(".config").join("rpn").to_string_lossy().into_owned()
|
home.join(".config")
|
||||||
|
.join("rpn")
|
||||||
|
.to_string_lossy()
|
||||||
|
.into_owned()
|
||||||
} else {
|
} else {
|
||||||
raw.state.dir.clone()
|
raw.state.dir.clone()
|
||||||
};
|
};
|
||||||
@@ -186,7 +203,10 @@ impl Config {
|
|||||||
Ok(Config {
|
Ok(Config {
|
||||||
database: DatabaseConfig {
|
database: DatabaseConfig {
|
||||||
host: raw.database.host,
|
host: raw.database.host,
|
||||||
port: raw.database.port.as_port("database.port", DEFAULT_DATABASE_PORT)?,
|
port: raw
|
||||||
|
.database
|
||||||
|
.port
|
||||||
|
.as_port("database.port", DEFAULT_DATABASE_PORT)?,
|
||||||
user: raw.database.user,
|
user: raw.database.user,
|
||||||
password: raw.database.password,
|
password: raw.database.password,
|
||||||
name: raw.database.name,
|
name: raw.database.name,
|
||||||
@@ -202,7 +222,13 @@ impl Config {
|
|||||||
|
|
||||||
/// Human-readable rendering with the password masked.
|
/// Human-readable rendering with the password masked.
|
||||||
pub fn show(&self) -> String {
|
pub fn show(&self) -> String {
|
||||||
let mask = |s: &str| if s.is_empty() { String::new() } else { "***".to_string() };
|
let mask = |s: &str| {
|
||||||
|
if s.is_empty() {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
"***".to_string()
|
||||||
|
}
|
||||||
|
};
|
||||||
format!(
|
format!(
|
||||||
"[database]\n\
|
"[database]\n\
|
||||||
host = \"{}\"\n\
|
host = \"{}\"\n\
|
||||||
@@ -320,7 +346,10 @@ dir = "/var/backup/rpn"
|
|||||||
let d = tempfile::tempdir().unwrap();
|
let d = tempfile::tempdir().unwrap();
|
||||||
let p = d.path().join("nope.toml");
|
let p = d.path().join("nope.toml");
|
||||||
let err = Config::load_with(&p, Path::new("/home/u")).unwrap_err();
|
let err = Config::load_with(&p, Path::new("/home/u")).unwrap_err();
|
||||||
assert!(matches!(err, ConfigError::NotFound(ref q) if *q == p), "{err:?}");
|
assert!(
|
||||||
|
matches!(err, ConfigError::NotFound(ref q) if *q == p),
|
||||||
|
"{err:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -424,7 +453,10 @@ dir = \"/opt/rpn\"
|
|||||||
let (_d, p) = dir_with("config.toml", FULL_TOML);
|
let (_d, p) = dir_with("config.toml", FULL_TOML);
|
||||||
let cfg = Config::load_with(&p, Path::new("/home/u")).unwrap();
|
let cfg = Config::load_with(&p, Path::new("/home/u")).unwrap();
|
||||||
let s = cfg.show();
|
let s = cfg.show();
|
||||||
assert!(!s.contains("hunter2"), "password leaked into show output:\n{s}");
|
assert!(
|
||||||
|
!s.contains("hunter2"),
|
||||||
|
"password leaked into show output:\n{s}"
|
||||||
|
);
|
||||||
assert!(s.contains("***"), "{s}");
|
assert!(s.contains("***"), "{s}");
|
||||||
assert!(s.contains("alice"), "user should remain visible:\n{s}");
|
assert!(s.contains("alice"), "user should remain visible:\n{s}");
|
||||||
}
|
}
|
||||||
@@ -441,7 +473,8 @@ dir = \"/opt/rpn\"
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_database_requires_host_user_name() {
|
fn validate_database_requires_host_user_name() {
|
||||||
let body = "[database]\nhost = \"\"\nport = \"\"\nuser = \"u\"\npassword = \"\"\nname = \"\"\n";
|
let body =
|
||||||
|
"[database]\nhost = \"\"\nport = \"\"\nuser = \"u\"\npassword = \"\"\nname = \"\"\n";
|
||||||
let (_d, p) = dir_with("config.toml", body);
|
let (_d, p) = dir_with("config.toml", body);
|
||||||
let cfg = Config::load_with(&p, Path::new("/home/u")).unwrap();
|
let cfg = Config::load_with(&p, Path::new("/home/u")).unwrap();
|
||||||
let err = cfg.validate_database().unwrap_err();
|
let err = cfg.validate_database().unwrap_err();
|
||||||
|
|||||||
@@ -22,9 +22,16 @@ mod tests {
|
|||||||
password: "pw".into(),
|
password: "pw".into(),
|
||||||
name: "rpn".into(),
|
name: "rpn".into(),
|
||||||
},
|
},
|
||||||
web: WebConfig { port: 8080, fqdn: String::new() },
|
web: WebConfig {
|
||||||
state: StateConfig { dir: "/tmp/rpn".into() },
|
port: 8080,
|
||||||
backup: BackupConfig { dir: "/tmp/rpn/backups".into() },
|
fqdn: String::new(),
|
||||||
|
},
|
||||||
|
state: StateConfig {
|
||||||
|
dir: "/tmp/rpn".into(),
|
||||||
|
},
|
||||||
|
backup: BackupConfig {
|
||||||
|
dir: "/tmp/rpn/backups".into(),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +44,10 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn connection_url_with_database() {
|
fn connection_url_with_database() {
|
||||||
let cfg = test_config();
|
let cfg = test_config();
|
||||||
assert_eq!(connection_url(&cfg, true), "mysql://u:***@db.example:3307/rpn");
|
assert_eq!(
|
||||||
|
connection_url(&cfg, true),
|
||||||
|
"mysql://u:***@db.example:3307/rpn"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -82,8 +92,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn embedded_migration_is_schema_v1() {
|
fn embedded_migration_is_schema_v1() {
|
||||||
for table in ["files", "customers", "batches", "cards", "transactions", "invoices", "invoice_items"] {
|
for table in [
|
||||||
assert!(MIGRATION_V1.contains(&format!("CREATE TABLE {table}")), "{table}");
|
"files",
|
||||||
|
"customers",
|
||||||
|
"batches",
|
||||||
|
"cards",
|
||||||
|
"transactions",
|
||||||
|
"invoices",
|
||||||
|
"invoice_items",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
MIGRATION_V1.contains(&format!("CREATE TABLE {table}")),
|
||||||
|
"{table}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// the migrations bookkeeping table is created by the app, not the file
|
// the migrations bookkeeping table is created by the app, not the file
|
||||||
assert!(!MIGRATION_V1.contains("CREATE TABLE schema_migrations"));
|
assert!(!MIGRATION_V1.contains("CREATE TABLE schema_migrations"));
|
||||||
@@ -92,11 +113,17 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn backup_command_uses_dump_client() {
|
fn backup_command_uses_dump_client() {
|
||||||
let (program, args, use_pwd) = backup_command(&test_config());
|
let (program, args, use_pwd) = backup_command(&test_config());
|
||||||
assert!(program == "mariadb-dump" || program == "mysqldump", "{program}");
|
assert!(
|
||||||
|
program == "mariadb-dump" || program == "mysqldump",
|
||||||
|
"{program}"
|
||||||
|
);
|
||||||
assert!(args.iter().any(|a| a == "--host=db.example"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--host=db.example"), "{args:?}");
|
||||||
assert!(args.iter().any(|a| a == "--port=3307"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--port=3307"), "{args:?}");
|
||||||
assert!(args.iter().any(|a| a == "--user=u"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--user=u"), "{args:?}");
|
||||||
assert!(!args.iter().any(|a| a.contains("pw")), "password on argv: {args:?}");
|
assert!(
|
||||||
|
!args.iter().any(|a| a.contains("pw")),
|
||||||
|
"password on argv: {args:?}"
|
||||||
|
);
|
||||||
assert!(use_pwd, "MYSQL_PWD must be set for the dump client");
|
assert!(use_pwd, "MYSQL_PWD must be set for the dump client");
|
||||||
assert!(args.iter().any(|a| a == "--single-transaction"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--single-transaction"), "{args:?}");
|
||||||
assert!(args.iter().any(|a| a == "--databases"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--databases"), "{args:?}");
|
||||||
@@ -113,7 +140,10 @@ mod tests {
|
|||||||
assert!(program == "mariadb" || program == "mysql", "{program}");
|
assert!(program == "mariadb" || program == "mysql", "{program}");
|
||||||
assert!(args.iter().any(|a| a == "--host=db.example"), "{args:?}");
|
assert!(args.iter().any(|a| a == "--host=db.example"), "{args:?}");
|
||||||
assert!(args.iter().any(|a| a == "rpn"), "{args:?}");
|
assert!(args.iter().any(|a| a == "rpn"), "{args:?}");
|
||||||
assert!(!args.iter().any(|a| a.contains(".sql")), "file must not be on argv: {args:?}");
|
assert!(
|
||||||
|
!args.iter().any(|a| a.contains(".sql")),
|
||||||
|
"file must not be on argv: {args:?}"
|
||||||
|
);
|
||||||
assert!(use_pwd);
|
assert!(use_pwd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user