Skip to content

Commit c15aad2

Browse files
committed
Implement raw string sigils for CLI field customization.
^ to use literal string without interpretation @ to read string from file % to read string from environment variable
1 parent 983529f commit c15aad2

File tree

1 file changed

+68
-9
lines changed

1 file changed

+68
-9
lines changed

core/src/program.rs

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,11 @@ use crate::{
3131
identifier::LocIdent,
3232
label::Label,
3333
metrics::increment,
34+
parser::grammar::StaticFieldPathParser,
3435
term::{
3536
make::{self as mk_term, builder},
3637
record::Field,
38+
string::NickelString,
3739
BinaryOp, MergePriority, RichTerm, Term,
3840
},
3941
};
@@ -148,6 +150,26 @@ impl FieldOverride {
148150
let input_id = cache.replace_string(SourcePath::CliFieldAssignment, assignment);
149151
let s = cache.source(input_id);
150152

153+
if s.contains("=@") || s.contains("=%") || s.contains("=^") {
154+
let parser = StaticFieldPathParser::new();
155+
let splitted = s.split_once('=').unwrap();
156+
let field_name: &str = splitted.0;
157+
let path = parser
158+
.parse_strict(input_id, Lexer::new(field_name))
159+
// We just need to report an error here
160+
.map_err(|mut errs| {
161+
errs.errors.pop().expect(
162+
"because parsing of the field assignment failed, the error \
163+
list must be non-empty, put .pop() failed",
164+
)
165+
})?;
166+
return Ok(FieldOverride {
167+
path: FieldPath(path),
168+
value: splitted.1.to_owned(),
169+
priority,
170+
});
171+
}
172+
151173
let parser = CliFieldAssignmentParser::new();
152174
let (path, _, span_value) = parser
153175
.parse_strict(input_id, Lexer::new(s))
@@ -435,15 +457,52 @@ impl<EC: EvalCache> Program<EC> {
435457
let mut record = builder::Record::new();
436458

437459
for ovd in self.overrides.iter().cloned() {
438-
let value_file_id = self
439-
.vm
440-
.import_resolver_mut()
441-
.add_string(SourcePath::Override(ovd.path.clone()), ovd.value);
442-
self.vm.prepare_eval(value_file_id)?;
443-
record = record
444-
.path(ovd.path.0)
445-
.priority(ovd.priority)
446-
.value(Term::ResolvedImport(value_file_id));
460+
if let Some(s) = ovd.value.strip_prefix('^') {
461+
// literal string
462+
let v = Term::Str(NickelString::from(s));
463+
record = record.path(ovd.path.0).priority(ovd.priority).value(v);
464+
} else if let Some(s) = ovd.value.strip_prefix('@') {
465+
// read raw string from file
466+
match std::fs::read_to_string(s) {
467+
Ok(val) => {
468+
let v = Term::Str(NickelString::from(val));
469+
record = record.path(ovd.path.0).priority(ovd.priority).value(v);
470+
}
471+
Err(e) => {
472+
return Err(Error::IOError(IOError(format!(
473+
"Error reading file `{s}`: {e}"
474+
))))
475+
}
476+
}
477+
} else if let Some(s) = ovd.value.strip_prefix('%') {
478+
// read raw string from specified environment variable
479+
match std::env::var(s) {
480+
Ok(val) => {
481+
let v = Term::Str(NickelString::from(val));
482+
record = record.path(ovd.path.0).priority(ovd.priority).value(v);
483+
}
484+
Err(std::env::VarError::NotPresent) => {
485+
return Err(Error::IOError(IOError(format!(
486+
"Environment variable `{s}` not found"
487+
))))
488+
}
489+
Err(std::env::VarError::NotUnicode(..)) => {
490+
return Err(Error::IOError(IOError(format!(
491+
"Environment variable `{s}` has non-unicode content"
492+
))))
493+
}
494+
}
495+
} else {
496+
let value_file_id = self
497+
.vm
498+
.import_resolver_mut()
499+
.add_string(SourcePath::Override(ovd.path.clone()), ovd.value);
500+
self.vm.prepare_eval(value_file_id)?;
501+
record = record
502+
.path(ovd.path.0)
503+
.priority(ovd.priority)
504+
.value(Term::ResolvedImport(value_file_id));
505+
}
447506
}
448507

449508
let t = self.vm.prepare_eval(self.main_id)?;

0 commit comments

Comments
 (0)