Remove need to give JSON file path
This commit is contained in:
parent
9c20b2a8cc
commit
57c85bd97d
2 changed files with 53 additions and 107 deletions
|
|
@ -1,77 +1,31 @@
|
|||
use crate::error::CkError;
|
||||
use crate::config::Config;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::path::Path;
|
||||
|
||||
use fs_err as fs;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cache {
|
||||
root: PathBuf,
|
||||
files: HashMap<PathBuf, String>,
|
||||
values: HashMap<PathBuf, Value>,
|
||||
value: Value,
|
||||
pub variables: HashMap<String, Value>,
|
||||
last_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
/// Create a new cache, used to read files only once and otherwise store their contents.
|
||||
pub fn new(doc_dir: &str) -> Cache {
|
||||
pub fn new(config: &Config) -> Cache {
|
||||
let root = Path::new(&config.doc_dir);
|
||||
let filename = Path::new(&config.template).file_stem().unwrap();
|
||||
let file_path = root.join(&Path::with_extension(Path::new(filename), "json"));
|
||||
let content = fs::read_to_string(&file_path).expect("failed to read JSON file");
|
||||
|
||||
Cache {
|
||||
root: Path::new(doc_dir).to_owned(),
|
||||
files: HashMap::new(),
|
||||
values: HashMap::new(),
|
||||
value: serde_json::from_str::<Value>(&content).expect("failed to convert from JSON"),
|
||||
variables: HashMap::new(),
|
||||
last_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path(&mut self, path: &String) -> PathBuf {
|
||||
if path != "-" {
|
||||
let resolve = self.root.join(path);
|
||||
self.last_path = Some(resolve.clone());
|
||||
resolve
|
||||
} else {
|
||||
self.last_path
|
||||
.as_ref()
|
||||
// FIXME: Point to a line number
|
||||
.expect("No last path set. Make sure to specify a full path before using `-`")
|
||||
.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn read_file(&mut self, path: PathBuf) -> Result<String, io::Error> {
|
||||
if let Some(f) = self.files.get(&path) {
|
||||
return Ok(f.clone());
|
||||
}
|
||||
|
||||
let file = fs::read_to_string(&path)?;
|
||||
|
||||
self.files.insert(path, file.clone());
|
||||
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
/// Get the text from a file. If called multiple times, the file will only be read once
|
||||
pub fn get_file(&mut self, path: &String) -> Result<String, io::Error> {
|
||||
let path = self.resolve_path(path);
|
||||
self.read_file(path)
|
||||
}
|
||||
|
||||
/// Parse the JSON from a file. If called multiple times, the file will only be read once.
|
||||
pub fn get_value(&mut self, path: &String) -> Result<Value, CkError> {
|
||||
let path = self.resolve_path(path);
|
||||
|
||||
if let Some(v) = self.values.get(&path) {
|
||||
return Ok(v.clone());
|
||||
}
|
||||
|
||||
let content = self.read_file(path.clone())?;
|
||||
let val = serde_json::from_str::<Value>(&content)?;
|
||||
|
||||
self.values.insert(path, val.clone());
|
||||
|
||||
Ok(val)
|
||||
pub fn value(&self) -> &Value {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ fn main() -> Result<(), String> {
|
|||
let config = parse_config(env::args().collect());
|
||||
|
||||
let mut failed = Vec::new();
|
||||
let mut cache = Cache::new(&config.doc_dir);
|
||||
let mut cache = Cache::new(&config);
|
||||
let commands = get_commands(&config.template)
|
||||
.map_err(|_| format!("Jsondocck failed for {}", &config.template))?;
|
||||
|
||||
|
|
@ -55,12 +55,12 @@ pub enum CommandKind {
|
|||
}
|
||||
|
||||
impl CommandKind {
|
||||
fn validate(&self, args: &[String], command_num: usize, lineno: usize) -> bool {
|
||||
fn validate(&self, args: &[String], lineno: usize) -> bool {
|
||||
let count = match self {
|
||||
CommandKind::Has => (1..=3).contains(&args.len()),
|
||||
CommandKind::IsMany => args.len() >= 3,
|
||||
CommandKind::Count | CommandKind::Is => 3 == args.len(),
|
||||
CommandKind::Set => 4 == args.len(),
|
||||
CommandKind::Has => (1..=2).contains(&args.len()),
|
||||
CommandKind::IsMany => args.len() >= 2,
|
||||
CommandKind::Count | CommandKind::Is => 2 == args.len(),
|
||||
CommandKind::Set => 3 == args.len(),
|
||||
};
|
||||
|
||||
if !count {
|
||||
|
|
@ -68,15 +68,10 @@ impl CommandKind {
|
|||
return false;
|
||||
}
|
||||
|
||||
if args[0] == "-" && command_num == 0 {
|
||||
print_err(&format!("Tried to use the previous path in the first command"), lineno);
|
||||
return false;
|
||||
}
|
||||
|
||||
if let CommandKind::Count = self {
|
||||
if args[2].parse::<usize>().is_err() {
|
||||
if args[1].parse::<usize>().is_err() {
|
||||
print_err(
|
||||
&format!("Third argument to @count must be a valid usize (got `{}`)", args[2]),
|
||||
&format!("Second argument to @count must be a valid usize (got `{}`)", args[2]),
|
||||
lineno,
|
||||
);
|
||||
return false;
|
||||
|
|
@ -181,7 +176,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
|||
}
|
||||
};
|
||||
|
||||
if !cmd.validate(&args, commands.len(), lineno) {
|
||||
if !cmd.validate(&args, lineno) {
|
||||
errors = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -199,26 +194,24 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
let result = match command.kind {
|
||||
CommandKind::Has => {
|
||||
match command.args.len() {
|
||||
// @has <path> = file existence
|
||||
1 => cache.get_file(&command.args[0]).is_ok(),
|
||||
// @has <path> <jsonpath> = check path exists
|
||||
2 => {
|
||||
let val = cache.get_value(&command.args[0])?;
|
||||
let results = select(&val, &command.args[1]).unwrap();
|
||||
// @has <jsonpath> = check path exists
|
||||
1 => {
|
||||
let val = cache.value();
|
||||
let results = select(val, &command.args[0]).unwrap();
|
||||
!results.is_empty()
|
||||
}
|
||||
// @has <path> <jsonpath> <value> = check *any* item matched by path equals value
|
||||
3 => {
|
||||
let val = cache.get_value(&command.args[0])?;
|
||||
let results = select(&val, &command.args[1]).unwrap();
|
||||
let pat = string_to_value(&command.args[2], cache);
|
||||
// @has <jsonpath> <value> = check *any* item matched by path equals value
|
||||
2 => {
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[0]).unwrap();
|
||||
let pat = string_to_value(&command.args[1], cache);
|
||||
let has = results.contains(&pat.as_ref());
|
||||
// Give better error for when @has check fails
|
||||
if !command.negated && !has {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"{} matched to {:?} but didn't have {:?}",
|
||||
&command.args[1],
|
||||
&command.args[0],
|
||||
results,
|
||||
pat.as_ref()
|
||||
),
|
||||
|
|
@ -233,13 +226,13 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
}
|
||||
CommandKind::IsMany => {
|
||||
// @ismany <path> <jsonpath> <value>...
|
||||
let (path, query, values) = if let [path, query, values @ ..] = &command.args[..] {
|
||||
(path, query, values)
|
||||
let (query, values) = if let [query, values @ ..] = &command.args[..] {
|
||||
(query, values)
|
||||
} else {
|
||||
unreachable!("Checked in CommandKind::validate")
|
||||
};
|
||||
let val = cache.get_value(path)?;
|
||||
let got_values = select(&val, &query).unwrap();
|
||||
let val = cache.value();
|
||||
let got_values = select(val, &query).unwrap();
|
||||
assert!(!command.negated, "`@!ismany` is not supported");
|
||||
|
||||
// Serde json doesn't implement Ord or Hash for Value, so we must
|
||||
|
|
@ -270,18 +263,17 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
true
|
||||
}
|
||||
CommandKind::Count => {
|
||||
// @count <path> <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
|
||||
assert_eq!(command.args.len(), 3);
|
||||
let expected: usize = command.args[2].parse().unwrap();
|
||||
|
||||
let val = cache.get_value(&command.args[0])?;
|
||||
let results = select(&val, &command.args[1]).unwrap();
|
||||
// @count <jsonpath> <count> = Check that the jsonpath matches exactly [count] times
|
||||
assert_eq!(command.args.len(), 2);
|
||||
let expected: usize = command.args[1].parse().unwrap();
|
||||
let val = cache.value();
|
||||
let results = select(val, &command.args[0]).unwrap();
|
||||
let eq = results.len() == expected;
|
||||
if !command.negated && !eq {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"`{}` matched to `{:?}` with length {}, but expected length {}",
|
||||
&command.args[1],
|
||||
&command.args[0],
|
||||
results,
|
||||
results.len(),
|
||||
expected
|
||||
|
|
@ -293,17 +285,17 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
}
|
||||
}
|
||||
CommandKind::Is => {
|
||||
// @has <path> <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
|
||||
assert_eq!(command.args.len(), 3);
|
||||
let val = cache.get_value(&command.args[0])?;
|
||||
let results = select(&val, &command.args[1]).unwrap();
|
||||
let pat = string_to_value(&command.args[2], cache);
|
||||
// @has <jsonpath> <value> = check *exactly one* item matched by path, and it equals value
|
||||
assert_eq!(command.args.len(), 2);
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[0]).unwrap();
|
||||
let pat = string_to_value(&command.args[1], cache);
|
||||
let is = results.len() == 1 && results[0] == pat.as_ref();
|
||||
if !command.negated && !is {
|
||||
return Err(CkError::FailedCheck(
|
||||
format!(
|
||||
"{} matched to {:?}, but expected {:?}",
|
||||
&command.args[1],
|
||||
&command.args[0],
|
||||
results,
|
||||
pat.as_ref()
|
||||
),
|
||||
|
|
@ -314,16 +306,16 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
}
|
||||
}
|
||||
CommandKind::Set => {
|
||||
// @set <name> = <path> <jsonpath>
|
||||
assert_eq!(command.args.len(), 4);
|
||||
// @set <name> = <jsonpath>
|
||||
assert_eq!(command.args.len(), 3);
|
||||
assert_eq!(command.args[1], "=", "Expected an `=`");
|
||||
let val = cache.get_value(&command.args[2])?;
|
||||
let results = select(&val, &command.args[3]).unwrap();
|
||||
let val = cache.value().clone();
|
||||
let results = select(&val, &command.args[2]).unwrap();
|
||||
assert_eq!(
|
||||
results.len(),
|
||||
1,
|
||||
"Expected 1 match for `{}` (because of @set): matched to {:?}",
|
||||
command.args[3],
|
||||
command.args[2],
|
||||
results
|
||||
);
|
||||
match results.len() {
|
||||
|
|
@ -336,7 +328,7 @@ fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
|
|||
_ => {
|
||||
panic!(
|
||||
"Got multiple results in `@set` for `{}`: {:?}",
|
||||
&command.args[3], results
|
||||
&command.args[2], results,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue