2026-01-21 10:24:17 +00:00
|
|
|
#!/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] {
|
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
- Unified Component Architecture: components/mod.nu, main_provisioning/
{components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
- Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
ADDING_COMMANDS.md documents new-command workflow.
- Platform service manager: service-manager.nu (+573), startup.nu (+611),
service-check.nu (+255); autostart/bootstrap/health/target refactored.
- Nushell 0.112.2 migration: removed all try/catch and bash redirections;
external commands prefixed with ^; type signatures enforced. Driven by
scripts/refactor-try-catch{,-simplified}.nu.
- TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
tty-filter.sh, tty-commands.conf.
- New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
- Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
refactored (-454), removed legacy loaders/file_loader.nu (-330).
- Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
- Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
test_services.
- README + CHANGELOG updated.
2026-04-17 04:27:33 +01:00
|
|
|
mut accumulated = (ok [])
|
2026-01-21 10:24:17 +00:00
|
|
|
|
|
|
|
|
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
|
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
- Unified Component Architecture: components/mod.nu, main_provisioning/
{components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
- Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
ADDING_COMMANDS.md documents new-command workflow.
- Platform service manager: service-manager.nu (+573), startup.nu (+611),
service-check.nu (+255); autostart/bootstrap/health/target refactored.
- Nushell 0.112.2 migration: removed all try/catch and bash redirections;
external commands prefixed with ^; type signatures enforced. Driven by
scripts/refactor-try-catch{,-simplified}.nu.
- TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
tty-filter.sh, tty-commands.conf.
- New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
- Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
refactored (-454), removed legacy loaders/file_loader.nu (-330).
- Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
- Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
test_services.
- README + CHANGELOG updated.
2026-04-17 04:27:33 +01:00
|
|
|
export def match-result [result: record, on_ok: closure, on_err: closure] {
|
2026-01-21 10:24:17 +00:00
|
|
|
if (is-ok $result) {
|
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
- Unified Component Architecture: components/mod.nu, main_provisioning/
{components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
- Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
ADDING_COMMANDS.md documents new-command workflow.
- Platform service manager: service-manager.nu (+573), startup.nu (+611),
service-check.nu (+255); autostart/bootstrap/health/target refactored.
- Nushell 0.112.2 migration: removed all try/catch and bash redirections;
external commands prefixed with ^; type signatures enforced. Driven by
scripts/refactor-try-catch{,-simplified}.nu.
- TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
tty-filter.sh, tty-commands.conf.
- New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
- Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
refactored (-454), removed legacy loaders/file_loader.nu (-330).
- Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
- Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
test_services.
- README + CHANGELOG updated.
2026-04-17 04:27:33 +01:00
|
|
|
do $on_ok $result.ok
|
2026-01-21 10:24:17 +00:00
|
|
|
} else {
|
feat(core): three-layer DAG, unified component arch, commands-registry cache, Nushell 0.112.2 migration
- DAG architecture: `dag show/validate/export` (nulib/main_provisioning/dag.nu),
config loader (lib_provisioning/config/loader/dag.nu), taskserv dag-executor.
Backed by schemas/lib/dag/*.ncl; orchestrator emits NATS events via
WorkspaceComposition::into_workflow. See ADR-020, ADR-021.
- Unified Component Architecture: components/mod.nu, main_provisioning/
{components,workflow,extensions,ontoref-queries}.nu. Full workflow engine with
topological sort and NATS subject emission. Blocks A-H complete (libre-daoshi).
- Commands-registry: nulib/commands-registry.ncl (Nickel source, 314 lines) +
JSON cache at ~/.cache/provisioning/commands-registry.json rebuilt on source
change. cli/provisioning fast-path alias expansion avoids cold Nu startup.
ADDING_COMMANDS.md documents new-command workflow.
- Platform service manager: service-manager.nu (+573), startup.nu (+611),
service-check.nu (+255); autostart/bootstrap/health/target refactored.
- Nushell 0.112.2 migration: removed all try/catch and bash redirections;
external commands prefixed with ^; type signatures enforced. Driven by
scripts/refactor-try-catch{,-simplified}.nu.
- TTY stack: removed shlib/*-tty.sh; replaced by cli/tty-dispatch.sh,
tty-filter.sh, tty-commands.conf.
- New domain modules: images/ (golden image lifecycle), workspace/{state,sync}.nu,
main_provisioning/{bootstrap,cluster-deploy,fip,state}.nu, commands/{state,
build,integrations/auth,utilities/alias}.nu, platform.nu expanded (+874).
- Config loader overhaul: loader/core.nu slimmed (-759), cache/core.nu
refactored (-454), removed legacy loaders/file_loader.nu (-330).
- Thirteen new provisioning-<domain>.nu top-level modules for bash dispatcher.
- Tests: test_workspace_state.nu (+351); updates to test_oci_registry,
test_services.
- README + CHANGELOG updated.
2026-04-17 04:27:33 +01:00
|
|
|
do $on_err $result.err
|
2026-01-21 10:24:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 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)"
|
|
|
|
|
}
|
|
|
|
|
}
|