diff --git a/examples/regorus.rs b/examples/regorus.rs index 91bb397..2af111f 100644 --- a/examples/regorus.rs +++ b/examples/regorus.rs @@ -2,34 +2,20 @@ // Licensed under the MIT License. use anyhow::{bail, Context, Result}; -use clap::Parser; +use clap::{Parser, Subcommand}; -#[derive(clap::Parser)] -#[command(author, version, about, long_about = None)] -struct Cli { - /// Policy or data files. Rego, json or yaml. - #[arg(required(true), long, short, value_name = "policy.rego")] - data: Vec, - - /// Input file. json or yaml. - #[arg(long, short, value_name = "input.rego")] +fn rego_eval( + files: &[String], input: Option, - - // Query. Rego expression. - #[arg(long, short)] query: Option, -} - -fn main() -> Result<()> { - let cli = Cli::parse(); - let enable_tracing = false; - + enable_tracing: bool, +) -> Result<()> { // User specified data. let mut data = regorus::Value::new_object(); // Read all policy files. let mut policies = vec![]; - for file in cli.data.iter() { + for file in files.iter() { let contents = std::fs::read_to_string(file).with_context(|| format!("Failed to read {file}"))?; @@ -54,7 +40,7 @@ fn main() -> Result<()> { let mut sources = vec![]; for (idx, rego) in policies.iter().enumerate() { sources.push(regorus::Source { - file: &cli.data[idx], + file: &files[idx], contents: rego.as_str(), lines: rego.split('\n').collect(), }); @@ -67,6 +53,22 @@ fn main() -> Result<()> { modules.push(parser.parse()?); } + // Parse input file. + let input = if let Some(file) = input { + let input_contents = std::fs::read_to_string(file.clone()) + .with_context(|| format!("Failed to read {file}"))?; + + Some(if file.ends_with(".json") { + serde_json::from_str(&input_contents)? + } else if file.ends_with(".yaml") { + serde_yaml::from_str(&input_contents)? + } else { + bail!("invalid input file {file}"); + }) + } else { + None + }; + // Analyze the modules and determine how statements must be schedules. let analyzer = regorus::Analyzer::new(); let schedule = analyzer.analyze(&modules)?; @@ -76,13 +78,13 @@ fn main() -> Result<()> { let mut interpreter = regorus::Interpreter::new(modules_ref)?; // Prepare for evalution. - interpreter.prepare_for_eval(Some(&schedule), &Some(data))?; + interpreter.prepare_for_eval(Some(&schedule), &Some(data.clone()))?; // Evaluate all the modules. - interpreter.eval(&None, &None, false, Some(&schedule))?; + interpreter.eval(&Some(data), &input, false, Some(&schedule))?; // Fetch query string. If none specified, use "data". - let query = match &cli.query { + let query = match &query { Some(query) => query, _ => "data", }; @@ -109,3 +111,118 @@ fn main() -> Result<()> { Ok(()) } + +fn rego_lex(file: String, verbose: bool) -> Result<()> { + let contents = + std::fs::read_to_string(file.clone()).with_context(|| format!("Failed to read {file}"))?; + + // Create source. + let source = regorus::Source { + file: file.as_str(), + contents: contents.as_str(), + lines: contents.split('\n').collect(), + }; + + // Create lexer. + let mut lexer = regorus::Lexer::new(&source); + + // Read tokens until EOF. + loop { + let token = lexer.next_token()?; + if token.0 == regorus::TokenKind::Eof { + break; + } + + if verbose { + // Print each token's line and mark with with ^. + println!("{}", token.1.message("", "")); + } + + // Print the token. + println!("{token:?}"); + } + Ok(()) +} + +fn rego_parse(file: String) -> Result<()> { + let contents = + std::fs::read_to_string(file.clone()).with_context(|| format!("Failed to read {file}"))?; + + // Create source. + let source = regorus::Source { + file: file.as_str(), + contents: contents.as_str(), + lines: contents.split('\n').collect(), + }; + + // Create a parser and parse the source. + let mut parser = regorus::Parser::new(&source)?; + let ast = parser.parse()?; + println!("{ast:#?}"); + + Ok(()) +} + +#[derive(Subcommand)] +enum RegorusCommand { + /// Evaluate a Rego Query. + Eval { + /// Policy or data files. Rego, json or yaml. + #[arg( + required(true), + long, + short, + value_name = "policy.rego|data.json|data.yaml" + )] + data: Vec, + + /// Input file. json or yaml. + #[arg(long, short, value_name = "input.rego")] + input: Option, + + /// Query. Rego query block. + query: Option, + + /// Enable tracing. + #[arg(long, short)] + trace: bool, + }, + + /// Tokenize a Rego policy. + Lex { + /// Rego policy file. + file: String, + + /// Verbose output. + #[arg(long, short)] + verbose: bool, + }, + + /// Parse q Rego policy. + Parse { + /// Rego policy file. + file: String, + }, +} + +#[derive(clap::Parser)] +#[command(author, version, about, long_about = None)] +struct Cli { + #[command(subcommand)] + command: RegorusCommand, +} + +fn main() -> Result<()> { + // Parse and dispatch command. + let cli = Cli::parse(); + match cli.command { + RegorusCommand::Eval { + data, + input, + query, + trace, + } => rego_eval(&data, input, query, trace), + RegorusCommand::Lex { file, verbose } => rego_lex(file, verbose), + RegorusCommand::Parse { file } => rego_parse(file), + } +} diff --git a/scripts/rego-eval b/scripts/rego-eval deleted file mode 100755 index ebf4326..0000000 --- a/scripts/rego-eval +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -set -e -rego=$(realpath -e $1) - -if [ ! -z "$2" ]; then - input=$(realpath -e $2) - cargo test interpreter::one_file -- --include-ignored --nocapture "$rego" "$input" -else - cargo test interpreter::one_file -- --include-ignored --nocapture "$rego" -fi diff --git a/scripts/rego-lex b/scripts/rego-lex deleted file mode 100755 index 1c7175a..0000000 --- a/scripts/rego-lex +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -set -e - -usage="usage: rego-lex [-v]" - -if [ -z "$1" ]; then - echo "$usage" - exit 1 -fi - -rego=$(realpath -e $1) - -case "$2" in - "-v") - verbose="verbose" - ;; - *) - if [ ! -z "$2" ]; then - echo "$usage" - exit 1 - fi -esac - -eval "cargo test lexer::one_file -- --include-ignored --nocapture $rego $verbose" diff --git a/scripts/rego-parse b/scripts/rego-parse deleted file mode 100755 index 533b6c0..0000000 --- a/scripts/rego-parse +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -set -e - -rego=$(realpath -e $1) -cargo test parser::one_file -- --include-ignored --nocapture "$rego" diff --git a/tests/interpreter/mod.rs b/tests/interpreter/mod.rs index 17e882c..2ef206c 100644 --- a/tests/interpreter/mod.rs +++ b/tests/interpreter/mod.rs @@ -380,53 +380,6 @@ pub fn eval_file( Ok(results) } -#[test] -#[ignore = "intended for use by scripts/rego-eval"] -fn one_file() -> Result<()> { - env_logger::init(); - - let mut file = String::default(); - let mut input = None; - for a in env::args() { - if a.ends_with(".rego") { - file = a; - } else if a.ends_with(".json") { - let input_json = std::fs::read_to_string(&a)?; - let value = Value::from_json_str(input_json.as_str())?; - input = Some(value); - } - } - - if file.is_empty() { - bail!("missing "); - } - - let contents = std::fs::read_to_string(&file)?; - - let source = Source { - file: file.as_str(), - contents: contents.as_str(), - lines: contents.split('\n').collect(), - }; - let mut parser = Parser::new(&source)?; - let modules = vec![parser.parse()?]; - - let analyzer = Analyzer::new(); - let schedule = analyzer.analyze(&modules)?; - - let mut modules_ref = vec![]; - for m in &modules { - modules_ref.push(m); - } - - let mut interpreter = interpreter::Interpreter::new(modules_ref)?; - interpreter.prepare_for_eval(Some(&schedule), &None)?; - let results = interpreter.eval_modules(&input, true)?; - println!("eval results:\n{}", serde_json::to_string_pretty(&results)?); - - Ok(()) -} - #[derive(PartialEq, Debug)] pub enum ValueOrVec { Single(Value), @@ -526,7 +479,6 @@ fn yaml_test_impl(file: &str, is_opa_test: bool) -> Result<()> { // Convert value to json compatible representation. let results = Value::from_json_str(serde_json::to_string(&results)?.as_str())?; - dbg!((&results, &expected_results[0])); match_values(&results, &expected_results[0])?; } else { check_output(&results, &expected_results)?; diff --git a/tests/lexer/mod.rs b/tests/lexer/mod.rs index 4c53424..a58f331 100644 --- a/tests/lexer/mod.rs +++ b/tests/lexer/mod.rs @@ -1,14 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![cfg(test)] - use anyhow::{bail, Result}; use regorus::*; use serde::{Deserialize, Serialize}; use std::env; use test_generator::test_resources; -//use walkdir::WalkDir; fn get_tokens<'source>(source: &'source Source<'source>) -> Result>> { let mut tokens = vec![]; @@ -58,46 +55,6 @@ fn check_loc(tok: &Token) -> Result<()> { } } -#[test] -#[ignore = "intended for use by scripts/lex-file"] -fn one_file() -> Result<()> { - let mut file = String::default(); - let mut verbose = false; - for a in env::args() { - if a.ends_with(".rego") { - file = a.clone(); - } - if matches!(a.as_str(), "verbose") { - verbose = true; - } - } - - if file.is_empty() { - bail!("missing ") - } - - let contents = std::fs::read_to_string(&file)?; - - let source = Source { - file: file.as_str(), - contents: contents.as_str(), - lines: contents.split('\n').collect(), - }; - - for tok in &get_tokens(&source)? { - if tok.0 == TokenKind::Eof { - break; - } - check_loc(tok)?; - if verbose { - println!("{}", tok.1.source.message(tok.1.line, tok.1.col, "", "")); - } - println!("{tok:?}"); - } - - Ok(()) -} - #[derive(Serialize, Deserialize, PartialEq, Debug)] struct Case { pub rego: String, @@ -219,36 +176,6 @@ fn one_yaml() -> Result<()> { yaml_test(file.as_str()) } -/* -fn run_yaml_tests_in(folder: &str) -> Result<()> { - let mut total = 0; - - for entry in WalkDir::new(folder) - .follow_links(true) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry - .path() - .to_str() - .ok_or_else(|| anyhow!("failed to convert path to utf8 {:?}", entry.path()))?; - if !path.ends_with(".yaml") { - continue; - } - - total += 1; - yaml_test(path)?; - } - - println!("{} lexer yaml tests passed.", total); - Ok(()) -} - -#[test] -fn lexer_yaml_tests() -> Result<()> { - run_yaml_tests_in("tests/lexer") -}*/ - #[test_resources("tests/lexer/**/*.yaml")] fn run(path: &str) { yaml_test(path).unwrap() diff --git a/tests/parser/mod.rs b/tests/parser/mod.rs index fde1c0f..20611b4 100644 --- a/tests/parser/mod.rs +++ b/tests/parser/mod.rs @@ -1,14 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -#![cfg(test)] - use anyhow::{anyhow, bail, Result}; use regorus::*; use serde::{Deserialize, Serialize}; use std::env; use test_generator::test_resources; -//use walkdir::WalkDir; macro_rules! my_assert_eq { ($left:expr, $right:expr, $($arg:tt)+) => { @@ -23,34 +20,6 @@ macro_rules! my_assert_eq { } } -#[test] -#[ignore = "intended for use by scripts/rego-parse"] -fn one_file() -> Result<()> { - let mut file = String::default(); - for a in env::args() { - if a.ends_with(".rego") { - file = a; - break; - } - } - - if file.is_empty() { - bail!("missing "); - } - - let contents = std::fs::read_to_string(&file)?; - - let source = Source { - file: file.as_str(), - contents: contents.as_str(), - lines: contents.split('\n').collect(), - }; - let mut parser = Parser::new(&source)?; - let ast = parser.parse()?; - println!("{ast:#?}"); - Ok(()) -} - fn skip_value(v: &Value) -> bool { matches!(v, Value::String(s) if s == "--skip--") } @@ -747,49 +716,12 @@ fn one_yaml() -> Result<()> { } if file.is_empty() { - bail!("missing "); + bail!("missing yaml test file"); } yaml_test(file.as_str()) } -/* -fn run_yaml_tests_in(folder: &str) -> Result<()> { - let mut total = 0; - - for entry in WalkDir::new(folder) - .follow_links(true) - .into_iter() - .filter_map(|e| e.ok()) - { - let path = entry - .path() - .to_str() - .ok_or_else(|| anyhow!("failed to convert path to utf8 {:?}", entry.path()))?; - if !path.ends_with(".yaml") { - continue; - } - - total += 1; - match yaml_test(path) { - Ok(_) => (), - Err(e) => { - bail!("test failed."); - } - } - } - - println!("{} parser yaml tests passed.", total); - Ok(()) -} - - -#[test] -fn parser_yaml_tests() -> Result<()> { - run_yaml_tests_in("tests/parser") -} -*/ - #[test_resources("tests/parser/**/*.yaml")] fn run(path: &str) { yaml_test(path).unwrap()