diff --git a/components/clarinet-cli/src/generate/contract.rs b/components/clarinet-cli/src/generate/contract.rs index 0d896afbd..055a4e88e 100644 --- a/components/clarinet-cli/src/generate/contract.rs +++ b/components/clarinet-cli/src/generate/contract.rs @@ -31,11 +31,6 @@ impl GetChangesForRmContract { f.append_path("tests")?; f.append_path(&name)?; if !f.exists() { - format!( - "{} tests/{} doesn't exist. Skipping removal", - red!("Warning"), - name - ); return Ok(()); } let change = FileDeletion { diff --git a/components/clarity-repl/src/analysis/mod.rs b/components/clarity-repl/src/analysis/mod.rs index 6627befb1..7757e86e7 100644 --- a/components/clarity-repl/src/analysis/mod.rs +++ b/components/clarity-repl/src/analysis/mod.rs @@ -154,9 +154,8 @@ where F: FnOnce(&mut AnalysisDatabase) -> std::result::Result, { conn.begin(); - let result = f(conn).map_err(|e| { + let result = f(conn).inspect_err(|_| { conn.roll_back().expect("Failed to roll back"); - e })?; conn.commit().expect("Failed to commit"); Ok(result) diff --git a/components/clarity-repl/src/repl/logger_hook.rs b/components/clarity-repl/src/repl/logger_hook.rs new file mode 100644 index 000000000..8f757d594 --- /dev/null +++ b/components/clarity-repl/src/repl/logger_hook.rs @@ -0,0 +1,59 @@ +use clarity::vm::errors::Error; +use clarity::vm::functions::NativeFunctions; +use clarity::vm::representations::Span; +use clarity::vm::types::QualifiedContractIdentifier; +use clarity::vm::{ + contexts::{Environment, LocalContext}, + types::Value, + EvalHook, ExecutionResult, SymbolicExpression, + SymbolicExpressionType::List, +}; + +pub struct ContractLog { + contract_id: QualifiedContractIdentifier, + span: Span, + result: Value, +} + +#[derive(Default)] +pub struct LoggerHook { + logs: Vec, +} + +impl LoggerHook { + pub fn new() -> Self { + LoggerHook::default() + } +} + +impl EvalHook for LoggerHook { + fn will_begin_eval(&mut self, _: &mut Environment, _: &LocalContext, _: &SymbolicExpression) {} + + fn did_finish_eval( + &mut self, + env: &mut Environment, + _context: &LocalContext, + expr: &SymbolicExpression, + res: &Result, + ) { + if let List(list) = &expr.expr { + if let Some((function_name, _args)) = list.split_first() { + if let Some(function_name) = function_name.match_atom() { + if let Some(NativeFunctions::Print) = + NativeFunctions::lookup_by_name(function_name) + { + if let Ok(value) = res { + self.logs.push(ContractLog { + contract_id: env.contract_context.contract_identifier.clone(), + span: expr.span.clone(), + result: value.clone(), + }); + } + } + } + } + } + } + + fn did_complete(&mut self, _: Result<&mut ExecutionResult, String>) {} +} diff --git a/components/clarity-repl/src/repl/mod.rs b/components/clarity-repl/src/repl/mod.rs index aec9074fc..f1c086f55 100644 --- a/components/clarity-repl/src/repl/mod.rs +++ b/components/clarity-repl/src/repl/mod.rs @@ -3,6 +3,7 @@ pub mod clarity_values; pub mod datastore; pub mod diagnostic; pub mod interpreter; +pub mod logger_hook; pub mod session; pub mod settings; pub mod tracer; diff --git a/components/clarity-repl/src/repl/session.rs b/components/clarity-repl/src/repl/session.rs index 30c910b71..a2263e866 100644 --- a/components/clarity-repl/src/repl/session.rs +++ b/components/clarity-repl/src/repl/session.rs @@ -1,5 +1,6 @@ use super::boot::{STACKS_BOOT_CODE_MAINNET, STACKS_BOOT_CODE_TESTNET}; use super::diagnostic::output_diagnostic; +use super::logger_hook::LoggerHook; use super::{ClarityCodeSource, ClarityContract, ClarityInterpreter, ContractDeployer}; use crate::analysis::coverage::TestCoverageReport; use crate::repl::clarity_values::value_to_string; @@ -573,7 +574,7 @@ impl Session { let result = self.interpreter.run(contract, ast, cost_track, Some(hooks)); - result.map(|result| { + result.inspect(|result| { if let EvaluationResult::Contract(contract_result) = &result.result { self.contracts .insert(contract_id.clone(), contract_result.contract.clone()); @@ -581,7 +582,6 @@ impl Session { if let Some(coverage) = coverage { self.coverage_reports.push(coverage); } - result }) } @@ -606,6 +606,9 @@ impl Session { let contract_id = QualifiedContractIdentifier::parse(&contract_id_str).unwrap(); let mut hooks: Vec<&mut dyn EvalHook> = vec![]; + let mut logger = LoggerHook::new(); + hooks.push(&mut logger); + let mut coverage = TestCoverageReport::new(test_name.clone()); if track_coverage { hooks.push(&mut coverage); @@ -1775,3 +1778,60 @@ mod tests { assert!(time_block_2 - time_block_1 == 600); } } +#[cfg(test)] +mod logger_hook_tests { + + use crate::{repl::DEFAULT_EPOCH, test_fixtures::clarity_contract::ClarityContractBuilder}; + + use super::*; + + #[test] + fn can_retrieve_print_values() { + let settings = SessionSettings::default(); + let mut session = Session::new(settings); + session.start().expect("session could not start"); + session.update_epoch(DEFAULT_EPOCH); + + // session.deploy_contract(contract, eval_hooks, cost_track, test_name, ast) + let snippet = [ + "(define-public (print-and-return (input (response uint uint)))", + " (begin", + " (match input x (print x) y (print y))", + " input", + " )", + ")", + ] + .join("\n"); + + let contract = ClarityContractBuilder::new().code_source(snippet).build(); + + let _ = session.deploy_contract(&contract, None, false, None, None); + let arg = SymbolicExpression::atom_value(Value::okay(Value::UInt(42)).unwrap()); + let res = session.call_contract_fn( + "contract", + "print-and-return", + &[arg], + &session.get_tx_sender(), + false, + false, + false, + "test".to_owned(), + ); + + println!("{:?}", res); + + let arg = SymbolicExpression::atom_value(Value::error(Value::UInt(404)).unwrap()); + let res = session.call_contract_fn( + "contract", + "print-and-return", + &[arg], + &session.get_tx_sender(), + false, + false, + false, + "test".to_owned(), + ); + + println!("{:?}", res); + } +}