Skip to content

Commit

Permalink
Merge pull request #6 from CloudCannon/feat/macros
Browse files Browse the repository at this point in the history
Add macros
  • Loading branch information
bglw authored Nov 28, 2024
2 parents b03714f + 6f1b563 commit 340f4ad
Show file tree
Hide file tree
Showing 8 changed files with 326 additions and 39 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

## Unreleased

* Added macro feature to Toolproof

## v0.5.0 (November 28, 2024)

* Add `before_all` commands to the Toolproof config
Expand Down
109 changes: 90 additions & 19 deletions toolproof/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use std::{collections::HashMap, time::Instant};
use console::{style, Term};
use futures::future::join_all;
use normalize_path::NormalizePath;
use parser::{ToolproofFileType, ToolproofPlatform};
use parser::{parse_macro, ToolproofFileType, ToolproofPlatform};
use schematic::color::owo::OwoColorize;
use segments::ToolproofSegments;
use similar_string::compare_similarity;
Expand Down Expand Up @@ -54,6 +54,16 @@ pub struct ToolproofTestFile {
pub file_directory: String,
}

#[derive(Debug, Clone)]
pub struct ToolproofMacroFile {
pub macro_segments: ToolproofSegments,
pub macro_orig: String,
pub steps: Vec<ToolproofTestStep>,
pub original_source: String,
pub file_path: String,
pub file_directory: String,
}

#[derive(Debug, Clone, PartialEq)]
pub enum ToolproofTestSuccess {
Skipped,
Expand All @@ -77,6 +87,14 @@ pub enum ToolproofTestStep {
state: ToolproofTestStepState,
platforms: Option<Vec<ToolproofPlatform>>,
},
Macro {
step_macro: ToolproofSegments,
args: HashMap<String, serde_json::Value>,
orig: String,
hydrated_steps: Option<Vec<ToolproofTestStep>>,
state: ToolproofTestStepState,
platforms: Option<Vec<ToolproofPlatform>>,
},
Instruction {
step: ToolproofSegments,
args: HashMap<String, serde_json::Value>,
Expand Down Expand Up @@ -110,8 +128,11 @@ impl Display for ToolproofTestStep {
Instruction { orig, .. } | Assertion { orig, .. } => {
write!(f, "{}", orig)
}
Macro { orig, .. } => {
write!(f, "run steps from macro: {}", orig)
}
Ref { orig, .. } => {
write!(f, "run steps from: {}", orig)
write!(f, "run steps from file: {}", orig)
}
Snapshot { orig, .. } => {
write!(f, "snapshot: {}", orig)
Expand Down Expand Up @@ -146,6 +167,7 @@ impl ToolproofTestStep {

match self {
Ref { state, .. }
| Macro { state, .. }
| Instruction { state, .. }
| Assertion { state, .. }
| Snapshot { state, .. } => state.clone(),
Expand Down Expand Up @@ -208,6 +230,33 @@ async fn main_inner() -> Result<(), ()> {

let start = Instant::now();

let mut errors = vec![];

let macro_glob = Glob::new("**/*.toolproof.macro.yml").expect("Valid glob");
let macro_walker = macro_glob
.walk(ctx.params.root.clone().unwrap_or(".".into()))
.flatten();

let loaded_macros = macro_walker
.map(|entry| {
let file = entry.path().to_path_buf();
async { (file.clone(), read_to_string(file).await) }
})
.collect::<Vec<_>>();

let macros = join_all(loaded_macros).await;

let all_macros: HashMap<_, _> = macros
.into_iter()
.filter_map(|(p, i)| match parse_macro(&i.unwrap(), p.clone()) {
Ok(f) => Some((f.macro_segments.clone(), f)),
Err(e) => {
errors.push(e);
return None;
}
})
.collect();

let glob = Glob::new("**/*.toolproof.yml").expect("Valid glob");
let walker = glob
.walk(ctx.params.root.clone().unwrap_or(".".into()))
Expand All @@ -224,7 +273,6 @@ async fn main_inner() -> Result<(), ()> {

let mut names_thus_far: Vec<(String, String)> = vec![];

let mut errors = vec![];
let all_tests: BTreeMap<_, _> = files
.into_iter()
.filter_map(|(p, i)| {
Expand Down Expand Up @@ -259,6 +307,11 @@ async fn main_inner() -> Result<(), ()> {
return Err(());
}

let macro_comparisons: Vec<_> = all_macros
.keys()
.map(|k| k.get_comparison_string())
.collect();

let all_instructions = register_instructions();
let instruction_comparisons: Vec<_> = all_instructions
.keys()
Expand All @@ -280,6 +333,8 @@ async fn main_inner() -> Result<(), ()> {
let universe = Arc::new(Universe {
browser: OnceCell::new(),
tests: all_tests,
macros: all_macros,
macro_comparisons,
instructions: all_instructions,
instruction_comparisons,
retrievers: all_retrievers,
Expand Down Expand Up @@ -436,20 +491,38 @@ async fn main_inner() -> Result<(), ()> {
log_err_preamble();
println!("{}", "--- ERROR ---".on_yellow().bold());
match &e.step {
ToolproofTestStep::Ref {
other_file,
orig,
hydrated_steps,
state,
platforms,
} => println!("{}", &e.red()),
ToolproofTestStep::Instruction {
step,
args,
orig,
state,
platforms,
ToolproofTestStep::Ref { .. } => println!("{}", &e.red()),
ToolproofTestStep::Macro {
step_macro, orig, ..
} => {
let closest = log_closest(
"Macro",
&orig,
&step_macro,
&universe.macro_comparisons,
);

let matches = closest
.into_iter()
.map(|m| {
let (actual_segments, _) = universe
.macros
.get_key_value(&m)
.expect("should exist in the global set");
format!(
"• {}",
style(actual_segments.get_as_string()).cyan()
)
})
.collect::<Vec<_>>();

if matches.is_empty() {
eprintln!("{}", "No similar macro found".red());
} else {
eprintln!("Closest macro:\n{}", matches.join("\n"));
}
}
ToolproofTestStep::Instruction { step, orig, .. } => {
let closest = log_closest(
"Instruction",
&orig,
Expand Down Expand Up @@ -480,10 +553,8 @@ async fn main_inner() -> Result<(), ()> {
ToolproofTestStep::Assertion {
retrieval,
assertion,
args,
orig,
state,
platforms,
..
} => {
if !universe.retrievers.contains_key(&retrieval) {
let closest = log_closest(
Expand Down
68 changes: 67 additions & 1 deletion toolproof/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
errors::ToolproofInputError,
platforms::normalize_line_endings,
segments::{ToolproofSegment, ToolproofSegments},
ToolproofTestFile, ToolproofTestStep, ToolproofTestStepState,
ToolproofMacroFile, ToolproofTestFile, ToolproofTestStep, ToolproofTestStepState,
};

struct ToolproofTestInput {
Expand All @@ -17,6 +17,13 @@ struct ToolproofTestInput {
file_directory: String,
}

struct ToolproofMacroInput {
parsed: RawToolproofMacroFile,
original_source: String,
file_path: String,
file_directory: String,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum ToolproofFileType {
Expand All @@ -40,13 +47,25 @@ struct RawToolproofTestFile {
steps: Vec<RawToolproofTestStep>,
}

#[derive(serde::Serialize, serde::Deserialize)]
struct RawToolproofMacroFile {
r#macro: String,
steps: Vec<RawToolproofTestStep>,
}

#[derive(serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
enum RawToolproofTestStep {
Ref {
r#ref: String,
platforms: Option<Vec<ToolproofPlatform>>,
},
Macro {
r#macro: String,
platforms: Option<Vec<ToolproofPlatform>>,
#[serde(flatten)]
other: Map<String, Value>,
},
BareStep(String),
StepWithParams {
step: String,
Expand Down Expand Up @@ -83,6 +102,26 @@ impl TryFrom<ToolproofTestInput> for ToolproofTestFile {
}
}

impl TryFrom<ToolproofMacroInput> for ToolproofMacroFile {
type Error = ToolproofInputError;

fn try_from(value: ToolproofMacroInput) -> Result<Self, Self::Error> {
let mut steps = Vec::with_capacity(value.parsed.steps.len());
for step in value.parsed.steps {
steps.push(step.try_into()?);
}

Ok(ToolproofMacroFile {
macro_segments: parse_segments(&value.parsed.r#macro)?,
macro_orig: value.parsed.r#macro,
steps,
original_source: value.original_source,
file_path: value.file_path,
file_directory: value.file_directory,
})
}
}

impl TryFrom<RawToolproofTestStep> for ToolproofTestStep {
type Error = ToolproofInputError;

Expand All @@ -100,6 +139,18 @@ impl TryFrom<RawToolproofTestStep> for ToolproofTestStep {
state: ToolproofTestStepState::Dormant,
platforms,
}),
RawToolproofTestStep::Macro {
r#macro,
platforms,
other,
} => Ok(ToolproofTestStep::Macro {
step_macro: parse_segments(&r#macro)?,
args: HashMap::from_iter(other.into_iter()),
orig: r#macro,
hydrated_steps: None,
state: ToolproofTestStepState::Dormant,
platforms,
}),
RawToolproofTestStep::BareStep(step) => parse_step(step, None, HashMap::new()),
RawToolproofTestStep::StepWithParams {
step,
Expand Down Expand Up @@ -147,6 +198,21 @@ fn parse_step(
}
}

pub fn parse_macro(s: &str, p: PathBuf) -> Result<ToolproofMacroFile, ToolproofInputError> {
let raw_macro = serde_yaml::from_str::<RawToolproofMacroFile>(s)?;

ToolproofMacroInput {
parsed: raw_macro,
original_source: normalize_line_endings(s),
file_path: p.to_slash_lossy().into_owned(),
file_directory: p
.parent()
.map(|p| p.to_slash_lossy().into_owned())
.unwrap_or_else(|| ".".to_string()),
}
.try_into()
}

pub fn parse_file(s: &str, p: PathBuf) -> Result<ToolproofTestFile, ToolproofInputError> {
let raw_test = serde_yaml::from_str::<RawToolproofTestFile>(s)?;

Expand Down
Loading

0 comments on commit 340f4ad

Please sign in to comment.