
Loader.pudu
Pudu105 lines4.5 KB
1/** @Environment.Loader.Module — reads, parses, and merges environment sources */2module PuduLangEnvironment.Loader34import Std.Env as Env5import Std.Map as Map6import PuduLangEnvironment.Discovery as Discovery7import PuduLangEnvironment.Domain.Layers as Layers8import PuduLangEnvironment.Domain.Parser as Parser9import PuduLangEnvironment as Environment10import PuduLangEnvironment.Options as Options11import PuduLangEnvironment.Source as Source12import PuduLangEnvironment.Variables as Variables1314/** @Environment.Loader.Collected — merged values, what was read, what was skipped */15type Collected = { values: Map[Str, Str], sources: Array[Str], skipped: Array[Environment.Problem] }161718export fn read(options: &Options.Options) -> Result[Map[Str, Str], Environment.Problem] {19 readOver(options, &Map.fromPairs(&Env.variables()))20}212223export fn load(options: &Options.Options) -> Result[Variables.Variables, Environment.Problem] {24 loadOver(options, &Map.fromPairs(&Env.variables()))25}26272829export fn readOver(options: &Options.Options, process: &Map[Str, Str]) -> Result[Map[Str, Str], Environment.Problem] {30 Ok(collect(options, process) ?.values)31}323334export fn loadOver(options: &Options.Options, process: &Map[Str, Str]) -> Result[Variables.Variables, Environment.Problem] {35 let collected = collect(options, process) ?36 Ok(Variables.settled(&collected.values, process, options.overwrite, collected.sources, collected.skipped))37}3839/** @Environment.Loader.Loading — a fluent chain ending in a read or load */40export trait Loading {41 fn read(self: &Self) -> Result[Map[Str, Str], Environment.Problem]42 fn load(self: &Self) -> Result[Variables.Variables, Environment.Problem]43}4445impl Loading for Options.Options {46 47 fn read(self: &Self) -> Result[Map[Str, Str], Environment.Problem] { read(self) }4849 50 fn load(self: &Self) -> Result[Variables.Variables, Environment.Problem] { load(self) }51}5253545556fn collect(options: &Options.Options, process: &Map[Str, Str]) -> Result[Collected, Environment.Problem] {57 let problems = Options.validate(options)58 if !problems.isEmpty() { return Err(Environment.InvalidOptions(problems)) }59 var skipped: Array[Environment.Problem] = []60 var sources: Array[Source.Source] = []61 if options.sources.isEmpty() {62 let found = Discovery.discover(options, process) ?63 skipped = found.skipped64 sources = found.paths.map(|path: Str| Source.File(path))65 } else {66 sources = options.sources.map(|given: Source.Source| located(options.directory, given))67 }68 let rules = Parser.Rules {69 trimValues: options.trimValues,70 exportSyntax: options.exportSyntax,71 inlineComments: options.inlineComments,72 expansion: options.expansion,73 overwrite: options.overwrite74 }75 var known: Map[Str, Str] = if options.overwrite { Map.empty() } else { Map.filterValues(process, |held: Str| !held.isEmpty()) }76 var layers: Array[Array[Layers.Entry]] = []77 var opened: Array[Str] = []78 for source in sources {79 let named = Source.label(&source)80 match Source.lines(&source, options.encoding) {81 case Ok(lines) => match Parser.parse(&lines, &rules, known, process) {82 case Ok(parsed) => {83 known = parsed.known84 layers = layers.push(parsed.entries)85 opened = opened.push(named)86 }87 case Err(open) => { return Err(Environment.UnclosedQuote(named, open.line, open.key)) }88 }89 case Err(problem) => {90 if options.strict || !Environment.isSkippable(&problem) { return Err(problem) }91 skipped = skipped.push(problem)92 }93 }94 }95 Ok(Collected{values: Layers.merge(&layers, options.overwrite), sources: opened, skipped: skipped})96}979899fn located(directory: Str, given: Source.Source) -> Source.Source {100 match given {101 case Source.File(path) => Source.File(Discovery.locate(directory, path))102 case _ => given103 }104}105