-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy patherror_handling.rs
More file actions
73 lines (68 loc) · 2.95 KB
/
Copy patherror_handling.rs
File metadata and controls
73 lines (68 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//! What you'll learn:
//! How a `Runnable` can fail with a `CognisError`, how that error
//! propagates through `.pipe()`, and how the caller matches on it
//! to show a friendly message instead of crashing.
//!
//! Why this matters:
//! User input is messy — someone types "forty-two" when your form
//! expected "42". Every Cognis primitive uses the same
//! `CognisError` enum, so error handling is a single match arm
//! regardless of whether you bubbled out of a parser, an LLM call,
//! or a tool. No stringly-typed errors, no surprise panics.
//!
//! Scenario:
//! A signup flow asks the user for their age. The chain validates
//! the input as a positive `u32`, then proceeds to a fake "create
//! account" stage. We send three inputs through: a valid number, a
//! word ("forty-two"), and a negative — and show what the caller
//! would surface to the UI for each.
//!
//! Run with:
//! cargo run -p cognis-examples --example resilience_error_handling
//!
//! Sample output (against ollama / llama3.1):
//! input 29 -> ok: created account (age=29)
//! input forty-two -> show user: couldn't parse age from "forty-two" — please enter a number
//! input -3 -> show user: age must be positive (got "-3")
use cognis::prelude::*;
use cognis_core::compose::lambda;
use cognis_core::runnable_ext::RunnableExt;
#[tokio::main]
async fn main() -> Result<()> {
// Stage 1: validate. We use `CognisError::Internal` here for
// brevity — production code would define its own validation
// variant or wrap a domain error. The point is that the same
// `CognisError` enum surfaces through `.pipe()` regardless of
// which stage failed, so a single match arm at the top covers all
// signup-flow failures.
let parse_age = lambda(|raw: String| async move {
match raw.trim().parse::<i32>() {
Ok(n) if n > 0 => Ok::<_, CognisError>(n as u32),
Ok(_) => Err(CognisError::Internal(format!(
"age must be positive (got {raw:?})"
))),
Err(_) => Err(CognisError::Internal(format!(
"couldn't parse age from {raw:?} — please enter a number"
))),
}
});
// Stage 2: pretend to create an account.
let create_account =
lambda(
|age: u32| async move { Ok::<_, CognisError>(format!("created account (age={age})")) },
);
let signup = parse_age.pipe(create_account);
for input in ["29", "forty-two", "-3"] {
match signup.invoke(input.to_string(), Default::default()).await {
Ok(msg) => println!("input {input:<10} -> ok: {msg}"),
Err(CognisError::Internal(reason)) => {
// The shape your UI middleware would consume.
println!("input {input:<10} -> show user: {reason}");
}
Err(other) => {
println!("input {input:<10} -> unexpected error: {other}");
}
}
}
Ok(())
}