From c4f30559d331f28e01de4202ffa141bfd1c87d15 Mon Sep 17 00:00:00 2001 From: mockturtl Date: Sat, 9 May 2015 20:03:34 -0400 Subject: [PATCH] wip subs --- CHANGELOG.md | 2 +- README.md | 2 +- lib/src/parser.dart | 19 ++++++++++++++----- test/parser_test.dart | 25 +++++++++++++++++++++++++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 94ab6fc..064d0a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ HEAD ---- - [deprecated] `Parser` methods will become private. [#3][] - - `#unquote` `#strip`, `#swallow`, `#parseOne`, `#surroundingQuote` + - `#unquote` `#strip`, `#swallow`, `#parseOne`, `#surroundingQuote`, `#interpolate` - [deps] migrate to [test][] - [deps] bump [logging][] diff --git a/README.md b/README.md index 3c21a44..4f88588 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ bool get hasEnv => dotenv.isEveryDefined(_requiredEnvVars); ### limitations -Does **not** yet support escaping or substitution. Pull requests gleefully considered. +Does **not** yet support escaping. Pull requests gleefully considered. ###### prior art diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 8d13690..c5d8e5e 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -8,6 +8,7 @@ class Parser { static final _comment = new RegExp(r'#.*$'); static final _keyword = 'export'; static final _surroundQuotes = new RegExp(r'''^(['"])(.*)\1$'''); + static final _bashVar = new RegExp(r'(?:\\)?(\$)([a-zA-Z_][\w]*)+'); const Parser(); @@ -16,7 +17,7 @@ class Parser { Map parse(Iterable lines) { var out = {}; lines.forEach((line) { - var kv = parseOne(line); + var kv = parseOne(line, env: out); if (kv.isEmpty) return; out.putIfAbsent(kv.keys.single, () => kv.values.single); }); @@ -26,7 +27,8 @@ class Parser { /// Parse a single line into a key-value pair. @Deprecated('Exposed for testing') // FIXME - Map parseOne(String line) { + Map parseOne(String line, + {Map env: const {}}) { var stripped = strip(line); if (!_isValid(stripped)) return {}; @@ -42,11 +44,18 @@ class Parser { if (quotChar == "'") // skip substitution in single-quoted values return {k: v}; - // TODO: variable substitution - - return {k: v}; + return {k: interpolate(v, env)}; } + /// Substitute $bash_vars in [val] with values from [env]. + @Deprecated('Exposed for testing') // FIXME + String interpolate(String val, Map env) => val + .replaceAllMapped(_bashVar, (m) { + var k = m.group(2); + if (!env.containsKey(k) || env[k] == null) return ''; + return env[k]; + }); + /// If [val] is wrapped in single or double quotes, return the quote character. /// Otherwise, return the empty string. @Deprecated('Exposed for testing') // FIXME diff --git a/test/parser_test.dart b/test/parser_test.dart index 38dc11b..1336f92 100644 --- a/test/parser_test.dart +++ b/test/parser_test.dart @@ -16,16 +16,36 @@ void main() { test('it skips empty lines', subj.parse_empty); test('it ignores duplicate keys', subj.parse_dup); + test('it substitutes known variables into other values', subj.parse_subs); test('it detects unquoted values', subj.surroundingQuote_none); test('it detects double-quoted values', subj.surroundingQuote_double); test('it detects single-quoted values', subj.surroundingQuote_single); + + test('it performs variable substitution', subj.interpolate); + test('it skips undefined variables', subj.interpolate_missing); + test('it handles explicitly null values in env', subj.interpolate_missing2); }); } const _psr = const Parser(); class ParserTest { + void interpolate() { + var out = _psr.interpolate(r'a$foo$baz', {'foo': 'bar', 'baz': 'qux'}); + expect(out, equals('abarqux')); + } + + void interpolate_missing() { + var out = _psr.interpolate(r'a$foo$baz', {}); + expect(out, equals('a')); + } + + void interpolate_missing2() { + var out = _psr.interpolate(r'a$foo$baz', {'foo': null}); + expect(out, equals('a')); + } + void surroundingQuote_none() { var out = _psr.surroundingQuote('no quotes here!'); expect(out, isEmpty); @@ -93,4 +113,9 @@ class ParserTest { var out = _psr.parse(['foo=bar', 'foo=baz']); expect(out, equals({'foo': 'bar'})); } + + void parse_subs() { + var out = _psr.parse(['foo=bar', r'baz=super$foo']); + expect(out, equals({'foo': 'bar', 'baz': 'superbar'})); + } }