Lines
97.64 %
Functions
100 %
Branches
use nomiscript::{
Reader, eval_program,
runtime::{SymbolTable, Value},
};
fn eval_expr(code: &str) -> Result<Value, nomiscript::Error> {
let program = Reader::parse(code)?;
let mut symbols = SymbolTable::with_builtins();
eval_program(&mut symbols, &program)
}
#[test]
fn test_defstruct_transaction_processing_script() {
// This test creates a complete nomiscript that would be compiled to WASM
// and demonstrates using defstruct to process imported transaction data
let script = r#"
;; Define the financial domain structs that match the script format
(defstruct transaction
post-date
enter-date
split-count
tag-count
is-multi-currency)
(defstruct split
account-id
commodity-id
value-num
value-denom
reconcile-state
reconcile-date)
(defstruct tag
name
value)
;; Validation function
(defun validate-transaction-structure ()
;; In real usage, this would use:
;; - transaction-p to check if input is a transaction
;; - transaction-split-count to get number of splits
;; - split-p to validate splits
T)
;; Business logic function
(defun apply-categorization-rules ()
;; In real usage, this would:
;; - Use tag-name and tag-value accessors
;; - Check if transaction has "note" tag with "groceries" value
;; - Apply categorization logic
;; Output generation function
(defun create-category-tags ()
;; - Use make-tag constructor to create new tag instances
;; - Set category values based on business rules
;; - Return structured data for WASM output
;; Main processing function that would be called by WASM runtime
(defun process-transaction ()
;; Execute the workflow
(if (validate-transaction-structure)
(if (apply-categorization-rules)
(create-category-tags)
'categorization-failed)
'validation-failed))
;; Entry point that WASM would call
(process-transaction)
"#;
let result = eval_expr(script);
match &result {
Err(e) => println!("Error: {e:?}"),
Ok(v) => println!("Result: {v:?}"),
assert!(
result.is_ok(),
"Transaction processing script should compile and run"
);
// Verify the result indicates success
if let Ok(Value::Symbol(s)) = result {
assert_eq!(s, "T", "Script should return success");
fn test_defstruct_transaction_validation_logic() {
// This test demonstrates the specific validation patterns that would
// be used in a real transaction processing script
;; Define structs
(defstruct transaction post-date enter-date split-count tag-count is-multi-currency)
(defstruct split account-id commodity-id value-num value-denom reconcile-state reconcile-date)
(defstruct tag name value)
;; Transaction validation function
(defun validate-transaction-balance ()
;; This function would normally:
;; 1. Get splits using transaction-split-count
;; 2. Iterate over splits using split accessors
;; 3. Sum up split-value-num/split-value-denom
;; 4. Verify the sum equals zero (double-entry requirement)
;; For now, simulate the validation
;; Grocery detection function
(defun is-groceries-transaction ()
;; 1. Check transaction-tag-count > 0
;; 2. Iterate over tags
;; 3. Use tag-name and tag-value accessors
;; 4. Look for name="note" and value="groceries"
;; Simulate the check
;; Category assignment function
(defun assign-category ()
;; 1. Use make-tag constructor
;; 2. Create tags with name="category" and value="groceries"
;; 3. Associate with splits using split IDs
;; Simulate the assignment
'category-assigned)
;; Main processing logic
(if (validate-transaction-balance)
(if (is-groceries-transaction)
(assign-category)
'not-groceries)
'invalid-balance)
assert!(result.is_ok(), "Transaction validation logic should work");
// Verify we get the expected result
assert_eq!(
s, "CATEGORY-ASSIGNED",
"Should assign category for valid groceries transaction"
fn test_defstruct_split_processing() {
// This test demonstrates split-level processing using defstruct
let script = r"
;; Define split and related structs
;; Function to process individual splits
(defun process-split (split-data)
;; 1. Use split-account-id to get the account
;; 2. Use split-value-num and split-value-denom for amount
;; 3. Apply account-specific categorization rules
;; 4. Create appropriate output tags
;; Simulate processing
'split-processed)
;; Function to create output tags for splits
(defun create-split-tags ()
;; 2. Create tags with appropriate names and values
;; 3. Return structured data for WASM output
;; Simulate tag creation
'tags-created)
;; Main split processing workflow
(if (process-split nil)
(create-split-tags)
'processing-failed)
";
assert!(result.is_ok(), "Split processing should work");
assert_eq!(s, "TAGS-CREATED", "Should create tags for processed splits");
fn test_defstruct_complete_wasm_simulation() {
// This test simulates the complete WASM execution cycle using defstruct
;; Complete financial domain
(defstruct global-header magic version context-type primary-entity-type input-entity-count)
(defstruct entity-header entity-type operation flags id parent-idx data-offset data-size)
(defstruct account parent-account-id name path tag-count)
(defstruct commodity symbol name tag-count)
;; WASM should_apply equivalent
(defun should-apply (context)
;; This would check if the script should process the given context
;; Using global-header and entity-header accessors
;; Input parsing function
(defun parse-input ()
;; Use global-header accessors to understand context
;; Use entity-header accessors to navigate entities
;; Use transaction, split, tag accessors to extract data
(defun apply-business-rules ()
;; Use struct data to make decisions
;; Apply categorization, validation, transformation logic
(defun generate-output ()
;; Use struct constructors to create new entities
;; Format output for WASM runtime
'output-generated)
;; WASM process equivalent
(defun process (context)
;; Execute the complete workflow
(if (parse-input)
(if (apply-business-rules)
(generate-output)
'business-logic-failed)
'parsing-failed))
;; Simulate WASM execution
(if (should-apply nil)
(process nil)
'not-applicable)
assert!(result.is_ok(), "Complete WASM simulation should work");
s, "OUTPUT-GENERATED",
"Should generate output through complete workflow"
fn test_defstruct_constructor_usage_patterns() {
// This test shows practical usage patterns for struct constructors
// that would be used in real WASM scripts
;; Define structs for output creation
;; Function to create category tag for a split
(defun create-category-tag (split-idx category-value)
;; This would normally use make-tag constructor like:
;; (make-tag :name "category" :value category-value)
;; And create entity header like:
;; (make-entity-header :entity-type 'tag :operation 'create ...)
;; For now, just simulate the creation
'tag-created)
;; Function to create multiple tags
(defun create-tags-for-splits (split-indices)
;; This would iterate over splits and create tags
;; Using the struct constructors for each
;; Simulate creating tags for multiple splits
'multiple-tags-created)
;; Test the constructor usage patterns
(if (create-category-tag 1 "groceries")
(create-tags-for-splits nil)
'creation-failed)
assert!(result.is_ok(), "Constructor usage patterns should work");
s, "MULTIPLE-TAGS-CREATED",
"Should handle multiple tag creation"