jsondocck: command -> directive
This commit is contained in:
parent
2c9fb22f04
commit
14db1b5b1c
4 changed files with 41 additions and 39 deletions
|
|
@ -4,7 +4,7 @@ use getopts::Options;
|
|||
pub struct Config {
|
||||
/// The directory documentation output was generated in
|
||||
pub doc_dir: String,
|
||||
/// The file documentation was generated for, with docck commands to check
|
||||
/// The file documentation was generated for, with docck directives to check
|
||||
pub template: String,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use std::borrow::Cow;
|
||||
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::cache::Cache;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Command {
|
||||
pub kind: CommandKind,
|
||||
pub struct Directive {
|
||||
pub kind: DirectiveKind,
|
||||
pub path: String,
|
||||
pub lineno: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum CommandKind {
|
||||
pub enum DirectiveKind {
|
||||
/// `//@ has <path>`
|
||||
///
|
||||
/// Checks the path exists.
|
||||
|
|
@ -55,16 +57,16 @@ pub enum CommandKind {
|
|||
Set { variable: String },
|
||||
}
|
||||
|
||||
impl CommandKind {
|
||||
impl DirectiveKind {
|
||||
/// Returns both the kind and the path.
|
||||
///
|
||||
/// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
|
||||
/// Returns `None` if the directive isn't from jsondocck (e.g. from compiletest).
|
||||
pub fn parse<'a>(
|
||||
command_name: &str,
|
||||
directive_name: &str,
|
||||
negated: bool,
|
||||
args: &'a [String],
|
||||
) -> Option<(Self, &'a str)> {
|
||||
let kind = match (command_name, negated) {
|
||||
let kind = match (directive_name, negated) {
|
||||
("count", false) => {
|
||||
assert_eq!(args.len(), 2);
|
||||
let expected = args[1].parse().expect("invalid number for `count`");
|
||||
|
|
@ -104,11 +106,11 @@ impl CommandKind {
|
|||
_ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
|
||||
},
|
||||
|
||||
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
|
||||
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => {
|
||||
return None;
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
|
||||
panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" })
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -116,22 +118,22 @@ impl CommandKind {
|
|||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Performs the actual work of ensuring a command passes.
|
||||
impl Directive {
|
||||
/// Performs the actual work of ensuring a directive passes.
|
||||
pub fn check(&self, cache: &mut Cache) -> Result<(), String> {
|
||||
let matches = cache.select(&self.path);
|
||||
match &self.kind {
|
||||
CommandKind::HasPath => {
|
||||
DirectiveKind::HasPath => {
|
||||
if matches.is_empty() {
|
||||
return Err("matched to no values".to_owned());
|
||||
}
|
||||
}
|
||||
CommandKind::HasNotPath => {
|
||||
DirectiveKind::HasNotPath => {
|
||||
if !matches.is_empty() {
|
||||
return Err(format!("matched to {matches:?}, but wanted no matches"));
|
||||
}
|
||||
}
|
||||
CommandKind::HasValue { value } => {
|
||||
DirectiveKind::HasValue { value } => {
|
||||
let want_value = string_to_value(value, cache);
|
||||
if !matches.contains(&want_value.as_ref()) {
|
||||
return Err(format!(
|
||||
|
|
@ -139,7 +141,7 @@ impl Command {
|
|||
));
|
||||
}
|
||||
}
|
||||
CommandKind::HasNotValue { value } => {
|
||||
DirectiveKind::HasNotValue { value } => {
|
||||
let wantnt_value = string_to_value(value, cache);
|
||||
if matches.contains(&wantnt_value.as_ref()) {
|
||||
return Err(format!(
|
||||
|
|
@ -152,14 +154,14 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
CommandKind::Is { value } => {
|
||||
DirectiveKind::Is { value } => {
|
||||
let want_value = string_to_value(value, cache);
|
||||
let matched = get_one(&matches)?;
|
||||
if matched != want_value.as_ref() {
|
||||
return Err(format!("matched to {matched:?} but want {want_value:?}"));
|
||||
}
|
||||
}
|
||||
CommandKind::IsNot { value } => {
|
||||
DirectiveKind::IsNot { value } => {
|
||||
let wantnt_value = string_to_value(value, cache);
|
||||
let matched = get_one(&matches)?;
|
||||
if matched == wantnt_value.as_ref() {
|
||||
|
|
@ -167,7 +169,7 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
CommandKind::IsMany { values } => {
|
||||
DirectiveKind::IsMany { values } => {
|
||||
// Serde json doesn't implement Ord or Hash for Value, so we must
|
||||
// use a Vec here. While in theory that makes setwize equality
|
||||
// O(n^2), in practice n will never be large enough to matter.
|
||||
|
|
@ -187,7 +189,7 @@ impl Command {
|
|||
}
|
||||
}
|
||||
}
|
||||
CommandKind::CountIs { expected } => {
|
||||
DirectiveKind::CountIs { expected } => {
|
||||
if *expected != matches.len() {
|
||||
return Err(format!(
|
||||
"matched to `{matches:?}` with length {}, but expected length {expected}",
|
||||
|
|
@ -195,7 +197,7 @@ impl Command {
|
|||
));
|
||||
}
|
||||
}
|
||||
CommandKind::Set { variable } => {
|
||||
DirectiveKind::Set { variable } => {
|
||||
let value = get_one(&matches)?;
|
||||
let r = cache.variables.insert(variable.to_owned(), value.clone());
|
||||
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
|
||||
|
|
@ -227,4 +229,4 @@ fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
|
|||
} else {
|
||||
Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use crate::Command;
|
||||
use crate::Directive;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CkError {
|
||||
pub message: String,
|
||||
pub command: Command,
|
||||
pub directive: Directive,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ mod error;
|
|||
|
||||
use cache::Cache;
|
||||
use config::parse_config;
|
||||
use directive::{Command, CommandKind};
|
||||
use directive::{Directive, DirectiveKind};
|
||||
use error::CkError;
|
||||
|
||||
fn main() -> ExitCode {
|
||||
|
|
@ -19,14 +19,14 @@ fn main() -> ExitCode {
|
|||
|
||||
let mut failed = Vec::new();
|
||||
let mut cache = Cache::new(&config);
|
||||
let Ok(commands) = get_commands(&config.template) else {
|
||||
let Ok(directives) = get_directives(&config.template) else {
|
||||
eprintln!("Jsondocck failed for {}", &config.template);
|
||||
return ExitCode::FAILURE;
|
||||
};
|
||||
|
||||
for command in commands {
|
||||
if let Err(message) = command.check( &mut cache) {
|
||||
failed.push(CkError { command, message });
|
||||
for directive in directives {
|
||||
if let Err(message) = directive.check(&mut cache) {
|
||||
failed.push(CkError { directive, message });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ fn main() -> ExitCode {
|
|||
ExitCode::SUCCESS
|
||||
} else {
|
||||
for i in failed {
|
||||
eprintln!("{}:{}, command failed", config.template, i.command.lineno);
|
||||
eprintln!("{}:{}, directive failed", config.template, i.directive.lineno);
|
||||
eprintln!("{}", i.message)
|
||||
}
|
||||
ExitCode::FAILURE
|
||||
|
|
@ -47,7 +47,7 @@ static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
|
|||
^\s*
|
||||
//@\s+
|
||||
(?P<negated>!?)
|
||||
(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
|
||||
(?P<directive>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
|
||||
(?P<args>.*)$
|
||||
"#,
|
||||
)
|
||||
|
|
@ -70,12 +70,12 @@ static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
|
|||
});
|
||||
|
||||
fn print_err(msg: &str, lineno: usize) {
|
||||
eprintln!("Invalid command: {} on line {}", msg, lineno)
|
||||
eprintln!("Invalid directive: {} on line {}", msg, lineno)
|
||||
}
|
||||
|
||||
/// Get a list of commands from a file.
|
||||
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
||||
let mut commands = Vec::new();
|
||||
/// Get a list of directives from a file.
|
||||
fn get_directives(template: &str) -> Result<Vec<Directive>, ()> {
|
||||
let mut directives = Vec::new();
|
||||
let mut errors = false;
|
||||
let file = fs::read_to_string(template).unwrap();
|
||||
|
||||
|
|
@ -83,7 +83,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
|||
let lineno = lineno + 1;
|
||||
|
||||
if DEPRECATED_LINE_PATTERN.is_match(line) {
|
||||
print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno);
|
||||
print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno);
|
||||
errors = true;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -101,10 +101,10 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
|
|||
continue;
|
||||
};
|
||||
|
||||
if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
|
||||
commands.push(Command { kind, lineno, path: path.to_owned() })
|
||||
if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) {
|
||||
directives.push(Directive { kind, lineno, path: path.to_owned() })
|
||||
}
|
||||
}
|
||||
|
||||
if !errors { Ok(commands) } else { Err(()) }
|
||||
if !errors { Ok(directives) } else { Err(()) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue