Repository navigation
Integrations
Chris Michael edited this page Oct 6, 2026
·
1 revision
Three behaviors build on other Pudu packages, installed with this one:
| Module | Package | What it adds |
|---|---|---|
Behaviors.Validation |
pudu-lang-validator |
register(&kind, validator): answers Invalid with every violation before the handler runs; registerStream for streams |
Behaviors.Logging |
pudu-lang-log |
Logging.of(logger): one recorder that is an open behavior, open stream behavior, and open notification handler |
Behaviors.Resilient |
pudu-lang-resilience |
register(&kind, pipeline): retries, timeouts, circuit breaking, rate limiting, hedging, and fallback around the rest of the pipeline |
module Ex10Integrations
import Std.Io as Io
import PuduLangLog.Configuration as Configuration
import PuduLangLog as Log
import PuduLangLog.Sinks.Memory as Memory
import PuduLangMediator.Behaviors.Logging as Logging
import PuduLangMediator.Behaviors.Resilient as Resilient
import PuduLangMediator.Behaviors.Validation as Validation
import PuduLangMediator.Context as Context
import PuduLangMediator.Mediator as Mediator
import PuduLangMediator as Messaging
import PuduLangMediator.Registration as Registration
import PuduLangMediator.Request as Request
import PuduLangMediator.Utils.Shared as Shared
import PuduLangResilience.Pipeline as Pipeline
import PuduLangResilience.Retry as Retry
import PuduLangResilience.Timeout as Timeout
import PuduLangValidator.Rule as Rule
import PuduLangValidator.Rules.Number as NumberRule
import PuduLangValidator.Rules.Text as TextRule
import PuduLangValidator.Validator as Validator
type Transfer = { account: Str, amount: Int }
fn rules() -> Validator.Validator[Transfer] {
let account = Rule.build(TextRule.notEmpty(Rule.ruleFor("account", |transfer: Transfer| transfer.account)))
let amount = Rule.build(NumberRule.greaterThan(Rule.ruleFor("amount", |transfer: Transfer| transfer.amount), 0))
Validator.add(Validator.add(Validator.create(), account), amount)
}
export fn main() -> Int {
let transfer: Request.Kind[Transfer, Str, Str] = Request.kind("payments.transfer")
let memory = Memory.create()
let logger = Configuration.create().minimumLevel(Log.Information).writeTo(Memory.sink(&memory)).createLogger()
let guarded = match Pipeline.build([
Retry.strategy(Retry.Options{..Retry.defaults(), maxRetryAttempts: 2, delay: 10}),
Timeout.strategy(Timeout.after(1000))
]) {
case Ok(pipeline) => pipeline
case Err(invalid) => panic(Pipeline.explain(&invalid))
}
let calls = Shared.shared(0)
let mediator = match Mediator.build([
Registration.openBehavior(Logging.of(logger)),
Validation.register(&transfer, rules()),
Resilient.register(&transfer, guarded),
Registration.handler(&transfer, fn(request: Transfer, _context: Context.Context) -> Messaging.Outcome[Str, Str] {
let made = Shared.change(&calls, fn(count: Int) -> (Int, Int) { (count + 1, count + 1) })
if made == 1 { Messaging.raise("gateway busy") } else { Ok("sent " + show(request.amount) + " to " + request.account) }
})
]) {
case Ok(built) => built
case Err(invalid) => panic(Mediator.explain(&invalid))
}
let sent = Mediator.send(&mediator, &transfer, Transfer{account: "ES91", amount: 25})
let invalid = Mediator.send(&mediator, &transfer, Transfer{account: "", amount: 0})
let _sent = Io.writeLine(Messaging.summarize(&sent) + " after " + show(Shared.current(&calls)) + " calls")
let _invalid = Io.writeLine(Messaging.summarize(&invalid))
for event in Memory.events(&memory) {
let _said = Io.writeLine("log " + show(event.level) + ": " + event.template.text)
}
if sent == Ok("sent 25 to ES91") && Shared.current(&calls) == 2 { 0 } else { 1 }
}
Check it and run it:
pudu check src/Ex10Integrations.pudu
pudu run src/Ex10Integrations.puduOutput:
Ok("sent 25 to ES91") after 2 calls
The message is invalid: account: Text is required.; amount: Number must be greater than 0.
log Information: Handled {MessageName} in {Elapsed} ms
log Warning: {MessageName} failed in {Elapsed} ms: {Failure}
The logging recorder is registered first, so it is the outermost behavior: it sees the transfer
once, after the resilience behavior retried the busy gateway. The invalid transfer never reached
the handler; Messaging.violations lists each property, message, and rule code. A strategy's
rejection (a timeout, an open circuit, a rate limit) comes back as Refused with its description,
and a caller's cancellation is never retried.