#!/usr/bin/env nu # Result Type Pattern - Hybrid error handling without try-catch # Combines preconditions (fail-fast), Result pattern, and functional composition # Version: 1.0 # # Usage: # use lib_provisioning/result.nu * # # def my-operation []: record { # if (precondition-fails) { return (err "message") } # ok {result: "value"} # } # Construct success result with value # Type: any -> {ok: any, err: null} export def ok [value: any] { {ok: $value, err: null} } # Construct error result with message # Type: string -> {ok: null, err: string} export def err [message: string] { {ok: null, err: $message} } # Check if result is successful # Type: record -> bool export def is-ok [result: record] { $result.err == null } # Check if result is error # Type: record -> bool export def is-err [result: record] { $result.err != null } # Monadic bind: chain operations on Results # Type: record, closure -> record # Stops propagation on error export def and-then [result: record, fn: closure] { if (is-ok $result) { do $fn $result.ok } else { $result # Propagate error } } # Map over Result value without stopping on error # Type: record, closure -> record export def map [result: record, fn: closure] { if (is-ok $result) { ok (do $fn $result.ok) } else { $result } } # Map over Result error # Type: record, closure -> record export def map-err [result: record, fn: closure] { if (is-err $result) { err (do $fn $result.err) } else { $result } } # Unwrap Result or return default # Type: record, any -> any export def unwrap-or [result: record, default: any] { if (is-ok $result) { $result.ok } else { $default } } # Unwrap Result or throw error # Type: record -> any (throws if error) export def unwrap! [result: record] { if (is-ok $result) { $result.ok } else { error make {msg: $result.err} } } # Combine two Results (stops on first error) # Type: record, record -> record export def combine [result1: record, result2: record] { if (is-err $result1) { return $result1 } if (is-err $result2) { return $result2 } ok {first: $result1.ok, second: $result2.ok} } # Combine list of Results (stops on first error) # Type: list -> record export def combine-all [results: list] { let mut accumulated = (ok []) for result in $results { if (is-err $accumulated) { break } $accumulated = (and-then $accumulated {|acc| if (is-ok $result) { ok ($acc | append $result.ok) } else { err $result.err } }) } $accumulated } # Try operation with automatic error wrapping # Type: closure -> record # Catches Nushell errors and wraps them (no try-catch) export def try-wrap [fn: closure] { let result = (do { do $fn } | complete) if $result.exit_code == 0 { ok ($result.stdout) } else { err $result.stderr } } # Match on Result (like Rust's match) # Type: record, closure, closure -> any export def match-result [result: record, on-ok: closure, on-err: closure] { if (is-ok $result) { do $on-ok $result.ok } else { do $on-err $result.err } } # Execute bash command and wrap result # Type: string -> record # Returns: {ok: output, err: null} on success; {ok: null, err: message} on error (no try-catch) export def bash-wrap [cmd: string] { let result = (do { bash -c $cmd } | complete) if $result.exit_code == 0 { ok ($result.stdout | str trim) } else { err $"Command failed: ($result.stderr)" } } # Execute bash command, check exit code # Type: string -> record # Returns: {ok: {exit_code: int, stdout: string}, err: null} or {ok: null, err: message} (no try-catch) export def bash-check [cmd: string] { let result = (do { bash -c $cmd | complete } | complete) if $result.exit_code == 0 { let bash_result = ($result.stdout) if ($bash_result.exit_code == 0) { ok $bash_result } else { err ($bash_result.stderr) } } else { err $"Command failed: ($result.stderr)" } } # Try bash command with fallback value # Type: string, any -> any # Returns value on success, fallback on error (no try-catch) export def bash-or [cmd: string, fallback: any] { let result = (do { bash -c $cmd } | complete) if $result.exit_code == 0 { ($result.stdout | str trim) } else { $fallback } } # Read JSON file safely # Type: string -> record # Returns: {ok: parsed_json, err: null} or {ok: null, err: message} (no try-catch) export def json-read [file_path: string] { let read_result = (do { open $file_path | from json } | complete) if $read_result.exit_code == 0 { ok ($read_result.stdout) } else { err $"Failed to read JSON from ($file_path): ($read_result.stderr)" } } # Write JSON to file safely # Type: string, any -> record # Returns: {ok: true, err: null} or {ok: false, err: message} (no try-catch) export def json-write [file_path: string, data: any] { let json_str = ($data | to json) let write_result = (do { bash -c $"cat > ($file_path) << 'EOF'\n($json_str)\nEOF" } | complete) if $write_result.exit_code == 0 { ok true } else { err $"Failed to write JSON to ($file_path): ($write_result.stderr)" } }