Skip to content

Problems And Strictness

Chris Michael edited this page Oct 7, 2026 · 1 revision

Pudu

Problems And Strictness

Every refusal is a PuduLangEnvironment.Problem. Environment.describe turns one into a sentence:

Variant Meaning Skippable
BlankPath a file source with a blank path yes
MissingFile(path) the file does not exist yes
Unreadable(path, reason) the file exists but could not be read yes
Undecodable(source, encoding) the bytes are not valid text in the encoding yes
NotFound(paths) probing or the cascade found nothing; lists every path looked for yes
UnclosedQuote(source, line, key) a quoted value never closes no
UnsafeEnvironmentName(name) the cascade's name could leave its directory no
InvalidOptions(problems) the options conflict no
Missing(keys) variables that are unset or empty —
Malformed(key, kind) a value that does not convert to the kind asked for —
Invalid(failures) settings a validator refused, as property (code) —

No variant carries a value read from a file, so any problem can be logged as it is.

A lenient load — the default — records a skippable problem in variables.skipped and goes on. A strict load (withStrict()) refuses it. Problems that are not skippable refuse every load, because each means the program would run on a configuration other than the one written:

module Ex05Strict

import Std.Io as Io
import PuduLangEnvironment as Environment
import PuduLangEnvironment.Loader as Loader
import PuduLangEnvironment.Options as Options
import PuduLangEnvironment.Source as Source
import PuduLangEnvironment.Variables as Variables

fn report(label: Str, outcome: Result[Variables.Variables, Environment.Problem]) -> () {
  let line = match outcome {
    case Ok(variables) => label + ": loaded, skipped " + show(variables.skipped.map(|problem: Environment.Problem| Environment.describe(&problem)))
    case Err(problem) => label + ": " + Environment.describe(&problem)
  }
  let _shown = Io.writeLine(line)
}

export fn main() -> Int {
  let base = Options.defaults().withFiles(&["/nonexistent/app.env"])
  report("lenient", base.load())
  report("strict", base.withStrict().load())
  report("unclosed", Options.defaults().withSources(&[Source.Text("inline", "TOKEN=\"abc")]).load())
  report("conflict", base.withProbe().withCascade().load())
  report("unsafe", Options.defaults().withEnvironment("../../etc").load())
  0
}

Output:

lenient: loaded, skipped ["The environment file '/nonexistent/app.env' does not exist."]
strict: The environment file '/nonexistent/app.env' does not exist.
unclosed: The value of 'TOKEN' in 'inline' opens a quote on line 1 that is never closed.
conflict: The environment options are invalid: files cannot be given while probing; files cannot be given with the environment cascade
unsafe: The environment name '../../etc' is blank or holds a path separator, '..', or a character a file name cannot hold.

Related

Clone this wiki locally