4 Commits

Author SHA1 Message Date
b26a594cc8 Implement the load_from_file function
The implementation is dead simple, and pretty dumb. I'm not going to
figure out all the different IO errors I might see. Instead, the
function will report that it couldn't read the file and call it good.
2025-07-17 15:06:01 -05:00
246987fa68 Signature & tests for fn load_from_file()
This function almost writes itself. I need a thin layer to handle the
file IO errors and report them appropriately, and then all the magic is
a pass-through of the existing read_conf_str.

I've made basic unit tests for the most obvious scenarios. The test for
missing-file behavior is incomplete because I need to create a new error
variant.
2025-07-17 14:21:53 -05:00
551297f46b Remove some debug prints 2025-07-17 14:13:04 -05:00
912a7283fd Externalize the test table
I'm beginning work on the file reading functions, so I need some files
to read in my tests. I'll also need the WholeFile struct to compare
against.

The input string has been moved out into a file and put back into the
test fixture with `include_str!()`. The WholeFile construction has been
moved to a util function so I can reuse it in another test.
2025-07-17 14:04:17 -05:00
3 changed files with 97 additions and 53 deletions

View File

@@ -8,6 +8,7 @@ pub enum Error {
BadFormat, BadFormat,
NoSuchProperty, NoSuchProperty,
NoSuchTable, NoSuchTable,
CouldntReadFile,
TomlWrap(toml::de::Error), TomlWrap(toml::de::Error),
} }
@@ -71,6 +72,17 @@ struct WholeFile {
project_overrides: Vec<PartialConfig>, project_overrides: Vec<PartialConfig>,
} }
fn load_from_file(path: &str) -> Result<WholeFile> {
let res = std::fs::read_to_string(path);
match res {
Ok(s) => read_conf_str(s.as_str()),
Err(e) => {
eprintln!("->> file io err: {:?}", e);
Err(Error::CouldntReadFile)
}
}
}
fn read_conf_str(text: &str) -> Result<WholeFile> { fn read_conf_str(text: &str) -> Result<WholeFile> {
let mut whole = WholeFile::default(); let mut whole = WholeFile::default();
@@ -98,7 +110,6 @@ fn read_conf_str(text: &str) -> Result<WholeFile> {
.project_path(path.clone()); .project_path(path.clone());
whole.project_overrides.push(part_cfg); whole.project_overrides.push(part_cfg);
} }
println!(" ->> lconf - keys {:?}", cfg_table.keys().collect::<Vec<&String>>());
Ok(whole) Ok(whole)
} }
@@ -145,6 +156,43 @@ mod tests {
use super::*; use super::*;
// Util for generating a reference struct
fn gen_expected_struct() -> WholeFile {
WholeFile {
all: PartialConfig {
project_path: None,
gitea_url: Some(String::from("http://localhost:3000")),
owner: None,
repo: None,
token: Some(String::from("fake-token"))
},
project_overrides: vec![
PartialConfig {
project_path: Some(String::from("/home/robert/projects/gt-tool")),
gitea_url: None,
owner: Some(String::from("robert")),
repo: Some(String::from("gt-tool")),
token: None,
},
PartialConfig {
project_path: Some(String::from("/home/robert/projects/rcalc")),
gitea_url: None,
owner: Some(String::from("jamis")),
repo: Some(String::from("rcalc")),
token: None,
},
PartialConfig {
project_path: Some(String::from("/home/robert/projects/rcalc-builders")),
gitea_url: None,
owner: Some(String::from("jamis")),
repo: Some(String::from("rcalc")),
token: None,
},
],
}
}
#[test] #[test]
fn read_single_prop() -> Result<()> { fn read_single_prop() -> Result<()> {
let fx_input_str = "owner = \"dingus\""; let fx_input_str = "owner = \"dingus\"";
@@ -185,59 +233,9 @@ mod tests {
#[test] #[test]
fn read_config_string_ok() -> Result<()> { fn read_config_string_ok() -> Result<()> {
let fx_sample_config_string = " let fx_sample_config_string = include_str!("../test_data/sample_config.toml");
[all] let fx_expected_struct = gen_expected_struct();
gitea_url = \"http://localhost:3000\"
token = \"fake-token\"
[\"/home/robert/projects/gt-tool\"]
owner = \"robert\"
repo = \"gt-tool\"
[\"/home/robert/projects/rcalc\"]
owner = \"jamis\"
repo = \"rcalc\"
[\"/home/robert/projects/rcalc-builders\"]
owner = \"jamis\"
repo = \"rcalc\"
";
let fx_expected_struct = WholeFile {
all: PartialConfig {
project_path: None,
gitea_url: Some(String::from("http://localhost:3000")),
owner: None,
repo: None,
token: Some(String::from("fake-token"))
},
project_overrides: vec![
PartialConfig {
project_path: Some(String::from("/home/robert/projects/gt-tool")),
gitea_url: None,
owner: Some(String::from("robert")),
repo: Some(String::from("gt-tool")),
token: None,
},
PartialConfig {
project_path: Some(String::from("/home/robert/projects/rcalc")),
gitea_url: None,
owner: Some(String::from("jamis")),
repo: Some(String::from("rcalc")),
token: None,
},
PartialConfig {
project_path: Some(String::from("/home/robert/projects/rcalc-builders")),
gitea_url: None,
owner: Some(String::from("jamis")),
repo: Some(String::from("rcalc")),
token: None,
},
],
};
let conf = read_conf_str(fx_sample_config_string)?; let conf = read_conf_str(fx_sample_config_string)?;
println!(" ->> Test conf: {:?}", conf);
println!(" ->> Ref conf: {:?}", fx_expected_struct);
assert_eq!(conf, fx_expected_struct); assert_eq!(conf, fx_expected_struct);
Ok(()) Ok(())
@@ -256,4 +254,30 @@ repo = \"rcalc\"
let conf = read_conf_str(fx_sample_cfg); let conf = read_conf_str(fx_sample_cfg);
assert!(conf.is_err()); assert!(conf.is_err());
} }
#[test]
// File exists and has valid configuration.
fn load_from_file_ok() -> Result<()> {
let conf = load_from_file("test_data/sample_config.toml")?;
assert_eq!(conf, gen_expected_struct());
Ok(())
}
#[test]
// File does not exist.
fn load_from_file_missing() -> Result<()> {
let res = load_from_file("test_data/doesnt_exist.toml");
let err = res.unwrap_err();
assert_eq!(err, Error::CouldntReadFile);
Ok(())
}
#[test]
// File exists but has garbage inside.
// TODO: This bumps against the same semantic issue as the todo note on
// the 'read_config_string_empty' test
fn load_from_file_bad() {
let res = load_from_file("test_data/missing_all_table.toml");
assert!(res.is_err());
}
} }

View File

@@ -0,0 +1,5 @@
# There must be an "[all]" table or the loader will reject the config file.
["/some/other/path"]
gitea_url = "fake-url"

View File

@@ -0,0 +1,15 @@
[all]
gitea_url = "http://localhost:3000"
token = "fake-token"
["/home/robert/projects/gt-tool"]
owner = "robert"
repo = "gt-tool"
["/home/robert/projects/rcalc"]
owner = "jamis"
repo = "rcalc"
["/home/robert/projects/rcalc-builders"]
owner = "jamis"
repo = "rcalc"