Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use Display instead of multiple println!s for inspection results #52

Merged
merged 1 commit into from
Sep 29, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
177 changes: 105 additions & 72 deletions src/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use biscuit_auth::{
use chrono::offset::Utc;
use serde::Serialize;
use serde_json::json;
use std::fs;
use std::path::PathBuf;
use std::{fmt::Display, fs};

use crate::cli::*;
use crate::errors::CliError::*;
Expand All @@ -29,34 +29,39 @@ struct TokenDescription {
blocks: Vec<TokenBlock>,
}

impl TokenDescription {
fn render(&self) {
impl Display for TokenDescription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.sealed {
println!("Sealed biscuit");
writeln!(f, "Sealed biscuit")?;
} else {
println!("Open biscuit");
writeln!(f, "Open biscuit")?;
}

for (i, block) in self.blocks.iter().enumerate() {
if i == 0 {
if let Some(root_key_id) = self.root_key_id {
println!("Authority block (root key identifier: {}):", &root_key_id);
writeln!(
f,
"Authority block (root key identifier: {}):",
&root_key_id
)?;
} else {
println!("Authority block:");
writeln!(f, "Authority block:")?;
}
} else if let Some(epk) = &block.external_key {
println!("Block n°{}, (third party, signed by {}):", i, epk);
writeln!(f, "Block n°{}, (third party, signed by {}):", i, epk)?;
} else {
println!("Block n°{}:", i);
writeln!(f, "Block n°{}:", i)?;
}

println!("== Datalog ==");
println!("{}", block.code);
writeln!(f, "== Datalog ==")?;
writeln!(f, "{}", block.code)?;

println!("== Revocation id ==");
println!("{}", block.revocation_id);
println!("\n==========\n");
writeln!(f, "== Revocation id ==")?;
writeln!(f, "{}", block.revocation_id)?;
writeln!(f, "\n==========\n")?;
}
Ok(())
}
}

Expand Down Expand Up @@ -92,28 +97,29 @@ struct QueryResult {
facts: RResult<Vec<String>, Token>,
}

impl QueryResult {
fn render(&self) {
println!();
impl Display for QueryResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f)?;
if self.query_all {
println!("🔎 Running query on all facts: {}", &self.query);
writeln!(f, "🔎 Running query on all facts: {}", &self.query)?;
} else {
println!("🔎 Running query: {}", &self.query);
writeln!(f, "🔎 Running query: {}", &self.query)?;
}
match &self.facts.clone().into_result() {
Ok(facts) => {
if facts.is_empty() {
println!("❌ No results");
writeln!(f, "❌ No results")?;
} else {
for fact in facts {
println!("{}", &fact);
writeln!(f, "{}", &fact)?;
}
}
}
Err(_) => {
println!("❌ Query failed");
writeln!(f, "❌ Query failed")?;
}
}
Ok(())
}
}

Expand All @@ -123,19 +129,19 @@ struct AuthResult {
result: RResult<(usize, String), Token>,
}

impl AuthResult {
fn render(&self) {
impl Display for AuthResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.result.clone().into_result() {
Ok((_, policy)) => {
println!("✅ Authorizer check succeeded 🛡️");
println!("Matched allow policy: {}", policy);
writeln!(f, "✅ Authorizer check succeeded 🛡️")?;
writeln!(f, "Matched allow policy: {}", policy)
}
Err(e) => {
println!("❌ Authorizer check failed 🛡️");
writeln!(f, "❌ Authorizer check failed 🛡️")?;
match e {
Token::FailedLogic(l) => display_logic_error(&self.policies, l),
Token::RunLimit(l) => display_run_limit(l),
_ => {}
Token::FailedLogic(l) => display_logic_error(f, &self.policies, l),
Token::RunLimit(l) => display_run_limit(f, l),
_ => Ok(()),
}
}
}
Expand All @@ -150,27 +156,29 @@ pub struct InspectionResults {
query: Option<QueryResult>,
}

impl InspectionResults {
pub fn render(&self) {
self.token.render();
impl Display for InspectionResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.token.fmt(f)?;

match self.signatures_check {
None => println!("🙈 Public key check skipped 🔑"),
Some(true) => println!("✅ Public key check succeeded 🔑"),
Some(false) => println!("❌ Public key check failed 🔑"),
None => writeln!(f, "🙈 Public key check skipped 🔑")?,
Some(true) => writeln!(f, "✅ Public key check succeeded 🔑")?,
Some(false) => writeln!(f, "❌ Public key check failed 🔑")?,
}

match &self.auth {
None => println!("🙈 Datalog check skipped 🛡️"),
Some(auth_result) => auth_result.render(),
None => writeln!(f, "🙈 Datalog check skipped 🛡️")?,
Some(auth_result) => auth_result.fmt(f)?,
}

match &self.query {
None => {}
Some(query_result) => query_result.render(),
None => Ok(()),
Some(query_result) => query_result.fmt(f),
}
}
}

impl InspectionResults {
pub fn ensure_success(&self) -> Result<()> {
if self.signatures_check == Some(false) {
Err(SignaturesCheckFailed)?;
Expand Down Expand Up @@ -199,14 +207,15 @@ struct SnapshotDescription {
elapsed_micros: u128,
}

impl SnapshotDescription {
pub fn render(&self) {
println!("{}", self.code);
impl Display for SnapshotDescription {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.code)?;

println!(
writeln!(
f,
"⏱️ Execution time: {}μs ({} iterations)",
self.elapsed_micros, self.iterations
);
)
}
}

Expand All @@ -217,21 +226,23 @@ pub struct SnapshotInspectionResults {
query: Option<QueryResult>,
}

impl SnapshotInspectionResults {
pub fn render(&self) {
self.snapshot.render();
impl Display for SnapshotInspectionResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.snapshot.fmt(f)?;
Copy link

@romgus romgus Sep 28, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why fmt(f) and not writeln!(f, ...)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This delegates to the snapshot's Display instance.


match &self.auth {
None => println!("🙈 Datalog check skipped 🛡️"),
Some(auth_result) => auth_result.render(),
None => writeln!(f, "🙈 Datalog check skipped 🛡️")?,
Some(auth_result) => auth_result.fmt(f)?,
}

match &self.query {
None => {}
Some(query_result) => query_result.render(),
None => Ok(()),
Some(query_result) => query_result.fmt(f),
}
}
}

impl SnapshotInspectionResults {
pub fn ensure_success(&self) -> Result<()> {
if let Some(ref auth) = self.auth {
if auth.result.clone().into_result().is_err() {
Expand Down Expand Up @@ -289,7 +300,7 @@ pub fn handle_inspect(inspect: &Inspect) -> Result<()> {
if inspect.json {
println!("{}", serde_json::to_string(&res)?);
} else {
res.render();
println!("{}", &res);
}
res.ensure_success()
}
Expand Down Expand Up @@ -497,7 +508,7 @@ pub fn handle_inspect_snapshot(inspect_snapshot: &InspectSnapshot) -> Result<()>
if inspect_snapshot.json {
println!("{}", serde_json::to_string(&res)?);
} else {
res.render();
println!("{}", &res);
}
res.ensure_success()
}
Expand Down Expand Up @@ -612,42 +623,59 @@ pub fn handle_inspect_snapshot_inner(
})
}

fn display_logic_error(policies: &[String], e: &Logic) {
fn display_logic_error(
f: &mut std::fmt::Formatter<'_>,
policies: &[String],
e: &Logic,
) -> std::fmt::Result {
match e {
Logic::Unauthorized { policy, checks } => {
display_matched_policy(policies, policy);
display_failed_checks(checks);
display_matched_policy(f, policies, policy)?;
display_failed_checks(f, checks)
}
Logic::NoMatchingPolicy { checks } => {
println!("No policy matched");
display_failed_checks(checks);
}
e => println!("An execution error happened during authorization: {:?}", &e),
writeln!(f, "No policy matched")?;
display_failed_checks(f, checks)
}
e => writeln!(
f,
"An execution error happened during authorization: {:?}",
&e
),
}
}

fn display_matched_policy(policies: &[String], policy: &MatchedPolicy) {
fn display_matched_policy(
f: &mut std::fmt::Formatter<'_>,
policies: &[String],
policy: &MatchedPolicy,
) -> std::fmt::Result {
match policy {
MatchedPolicy::Allow(i) => {
let policy = policies.get(*i);
println!(
writeln!(
f,
"An allow policy matched: {}",
policy.expect("Incorrect policy index")
);
)
}
MatchedPolicy::Deny(i) => {
let policy = policies.get(*i);
println!(
writeln!(
f,
"A deny policy matched: {}",
policy.expect("Incorrect policy index")
);
)
}
}
}

fn display_failed_checks(checks: &Vec<FailedCheck>) {
fn display_failed_checks(
f: &mut std::fmt::Formatter<'_>,
checks: &Vec<FailedCheck>,
) -> std::fmt::Result {
if !checks.is_empty() {
println!("The following checks failed:");
writeln!(f, "The following checks failed:")?;
}
for c in checks {
match c {
Expand All @@ -657,15 +685,20 @@ fn display_failed_checks(checks: &Vec<FailedCheck>) {
} else {
format!("Block {}", &bc.block_id)
};
println!(" {} check: {}", &block_name, &bc.rule);
writeln!(f, " {} check: {}", &block_name, &bc.rule)?;
}
FailedCheck::Authorizer(ac) => println!(" Authorizer check: {}", &ac.rule),
FailedCheck::Authorizer(ac) => writeln!(f, " Authorizer check: {}", &ac.rule)?,
}
}
Ok(())
}

fn display_run_limit(e: &RunLimit) {
println!("The authorizer execution was aborted: {}", &e.to_string());
fn display_run_limit(f: &mut std::fmt::Formatter<'_>, e: &RunLimit) -> std::fmt::Result {
writeln!(
f,
"The authorizer execution was aborted: {}",
&e.to_string()
)
}

fn is_sealed(b: &UnverifiedBiscuit) -> Result<bool> {
Expand Down
Loading