mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
eval, lex, parse commands (#30)
Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
committed by
GitHub
parent
63ecc44a48
commit
7a3d5e7e02
@@ -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<String>,
|
||||
|
||||
/// Input file. json or yaml.
|
||||
#[arg(long, short, value_name = "input.rego")]
|
||||
fn rego_eval(
|
||||
files: &[String],
|
||||
input: Option<String>,
|
||||
|
||||
// Query. Rego expression.
|
||||
#[arg(long, short)]
|
||||
query: Option<String>,
|
||||
}
|
||||
|
||||
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<String>,
|
||||
|
||||
/// Input file. json or yaml.
|
||||
#[arg(long, short, value_name = "input.rego")]
|
||||
input: Option<String>,
|
||||
|
||||
/// Query. Rego query block.
|
||||
query: Option<String>,
|
||||
|
||||
/// 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),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -1,27 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Copyright (c) Microsoft Corporation.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
set -e
|
||||
|
||||
usage="usage: rego-lex <policy.rego> [-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"
|
||||
@@ -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"
|
||||
@@ -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 <policy.rego>");
|
||||
}
|
||||
|
||||
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)?;
|
||||
|
||||
@@ -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<Vec<Token<'source>>> {
|
||||
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 <policy.rego>")
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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 <policy.rego>");
|
||||
}
|
||||
|
||||
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 <policy.rego>");
|
||||
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()
|
||||
|
||||
Reference in New Issue
Block a user