From bfecffa9f7a1d21f30566e178135a068ca8cfec3 Mon Sep 17 00:00:00 2001 From: James Collins Date: Fri, 18 Oct 2024 20:20:10 +1300 Subject: [PATCH] refactor: rename variables --- lib/src/parser.dart | 63 ++++++++++++++++++++++--------------------- test/parser_test.dart | 24 ++++++++--------- 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/lib/src/parser.dart b/lib/src/parser.dart index 92ee5b6..2a7ebe2 100644 --- a/lib/src/parser.dart +++ b/lib/src/parser.dart @@ -1,7 +1,7 @@ /// Creates key-value pairs from strings formatted as environment /// variable definitions. class Parser { - static const _singleQuot = "'"; + static const _singleQuote = "'"; static final _leadingExport = RegExp(r'''^ *export ?'''); static final _comment = RegExp(r'''#[^'"]*$'''); static final _commentWithQuotes = RegExp(r'''#.*$'''); @@ -14,38 +14,40 @@ class Parser { /// Creates a [Map](dart:core). /// Duplicate keys are silently discarded. Map parse(Iterable lines) { - var out = {}; + var envMap = {}; for (var line in lines) { - var kv = parseOne(line, env: out); - if (kv.isEmpty) continue; - out.putIfAbsent(kv.keys.single, () => kv.values.single); + final parsedKeyValue = parseOne(line, envMap: envMap); + if (parsedKeyValue.isEmpty) continue; + envMap.putIfAbsent(parsedKeyValue.keys.single, () => parsedKeyValue.values.single); } - return out; + return envMap; } /// Parses a single line into a key-value pair. Map parseOne(String line, - {Map env = const {}}) { - var stripped = strip(line); - if (!_isValid(stripped)) return {}; + {Map envMap = const {}}) { + final lineWithoutComments = removeCommentsFromLine(line); + if (!_isStringWithEqualsChar(lineWithoutComments)) return {}; - var idx = stripped.indexOf('='); - var lhs = stripped.substring(0, idx); - var k = swallow(lhs); - if (k.isEmpty) return {}; + final indexOfEquals = lineWithoutComments.indexOf('='); + final envKey = trimExportKeyword(lineWithoutComments.substring(0, indexOfEquals)); + if (envKey.isEmpty) return {}; - var rhs = stripped.substring(idx + 1, stripped.length).trim(); - var quotChar = surroundingQuote(rhs); - var v = unquote(rhs); - if (quotChar == _singleQuot) { - v = v.replaceAll("\\'", "'"); - return {k: v}; + final envValue = lineWithoutComments.substring(indexOfEquals + 1, lineWithoutComments.length).trim(); + final quoteChar = getSurroundingQuoteCharacter(envValue); + var envValueWithoutQuotes = removeSurroundingQuotes(envValue); + // Add any escapted quotes + if (quoteChar == _singleQuote) { + envValueWithoutQuotes = envValueWithoutQuotes.replaceAll("\\'", "'"); + // Return. We don't expect any bash variables in single quoted strings + return {envKey: envValueWithoutQuotes}; } - if (quotChar == '"') { - v = v.replaceAll('\\"', '"').replaceAll('\\n', '\n'); + if (quoteChar == '"') { + envValueWithoutQuotes = envValueWithoutQuotes.replaceAll('\\"', '"').replaceAll('\\n', '\n'); } - final interpolatedValue = interpolate(v, env).replaceAll("\\\$", "\$"); - return {k: interpolatedValue}; + // Interpolate bash variables + final interpolatedValue = interpolate(envValueWithoutQuotes, envMap).replaceAll("\\\$", "\$"); + return {envKey: interpolatedValue}; } /// Substitutes $bash_vars in [val] with values from [env]. @@ -54,7 +56,7 @@ class Parser { if ((m.group(1) ?? "") == "\\") { return m.input.substring(m.start, m.end); } else { - var k = m.group(3)!; + final k = m.group(3)!; if (!_has(env, k)) return ''; return env[k]!; } @@ -62,28 +64,27 @@ class Parser { /// If [val] is wrapped in single or double quotes, returns the quote character. /// Otherwise, returns the empty string. - - String surroundingQuote(String val) { + String getSurroundingQuoteCharacter(String val) { if (!_surroundQuotes.hasMatch(val)) return ''; return _surroundQuotes.firstMatch(val)!.group(1)!; } /// Removes quotes (single or double) surrounding a value. - String unquote(String val) { + String removeSurroundingQuotes(String val) { if (!_surroundQuotes.hasMatch(val)) { - return strip(val, includeQuotes: true).trim(); + return removeCommentsFromLine(val, includeQuotes: true).trim(); } return _surroundQuotes.firstMatch(val)!.group(2)!; } /// Strips comments (trailing or whole-line). - String strip(String line, {bool includeQuotes = false}) => + String removeCommentsFromLine(String line, {bool includeQuotes = false}) => line.replaceAll(includeQuotes ? _commentWithQuotes : _comment, '').trim(); /// Omits 'export' keyword. - String swallow(String line) => line.replaceAll(_leadingExport, '').trim(); + String trimExportKeyword(String line) => line.replaceAll(_leadingExport, '').trim(); - bool _isValid(String s) => s.isNotEmpty && s.contains('='); + bool _isStringWithEqualsChar(String s) => s.isNotEmpty && s.contains('='); /// [ null ] is a valid value in a Dart map, but the env var representation is empty string, not the string 'null' bool _has(Map map, String key) => diff --git a/test/parser_test.dart b/test/parser_test.dart index 7f39e4d..221e1d6 100644 --- a/test/parser_test.dart +++ b/test/parser_test.dart @@ -11,10 +11,10 @@ void main() { group('[Parser]', () { setUp(() => rand = Random()); test('it swallows leading "export"', () { - var out = psr.swallow(' export foo = bar '); + var out = psr.trimExportKeyword(' export foo = bar '); expect(out, equals('foo = bar')); - out = psr.swallow(' foo = bar export'); + out = psr.trimExportKeyword(' foo = bar export'); expect(out, equals('foo = bar export')); }); @@ -61,19 +61,19 @@ void main() { }); test('it handles unquoted values', () { - var out = psr.unquote(' str '); + var out = psr.removeSurroundingQuotes(' str '); expect(out, equals('str')); }); test('it handles double quoted values', () { - var out = psr.unquote('"val "'); + var out = psr.removeSurroundingQuotes('"val "'); expect(out, equals('val ')); }); test('it handles single quoted values', () { - var out = psr.unquote("' val'"); + var out = psr.removeSurroundingQuotes("' val'"); expect(out, equals(' val')); }); test('retain trailing single quote', () { - var out = psr.unquote("retained'"); + var out = psr.removeSurroundingQuotes("retained'"); expect(out, equals("retained'")); }); @@ -109,15 +109,15 @@ void main() { }); test('it detects unquoted values', () { - var out = psr.surroundingQuote('no quotes here!'); + var out = psr.getSurroundingQuoteCharacter('no quotes here!'); expect(out, isEmpty); }); test('it detects double-quoted values', () { - var out = psr.surroundingQuote('"double quoted"'); + var out = psr.getSurroundingQuoteCharacter('"double quoted"'); expect(out, equals('"')); }); test('it detects single-quoted values', () { - var out = psr.surroundingQuote("'single quoted'"); + var out = psr.getSurroundingQuoteCharacter("'single quoted'"); expect(out, equals("'")); }); @@ -153,17 +153,17 @@ void main() { test('it skips var substitution in single quotes', () { var r = rand.nextInt(ceil); // avoid runtime collision with real env vars - var out = psr.parseOne("some_var='my\$key_$r'", env: {'key_$r': 'val'}); + var out = psr.parseOne("some_var='my\$key_$r'", envMap: {'key_$r': 'val'}); expect(out['some_var'], equals('my\$key_$r')); }); test('it performs var subs in double quotes', () { var r = rand.nextInt(ceil); // avoid runtime collision with real env vars - var out = psr.parseOne('some_var="my\$key_$r"', env: {'key_$r': 'val'}); + var out = psr.parseOne('some_var="my\$key_$r"', envMap: {'key_$r': 'val'}); expect(out['some_var'], equals('myval')); }); test('it performs var subs without quotes', () { var r = rand.nextInt(ceil); // avoid runtime collision with real env vars - var out = psr.parseOne("some_var=my\$key_$r", env: {'key_$r': 'val'}); + var out = psr.parseOne("some_var=my\$key_$r", envMap: {'key_$r': 'val'}); expect(out['some_var'], equals('myval')); }); });