Repository navigation
Problems And Strictness
Chris Michael edited this page Oct 7, 2026
·
1 revision
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.