Skip to content

Commit eb5e408

Browse files
committed
refactor: parsing to reuse logic
1 parent 4f80495 commit eb5e408

5 files changed

Lines changed: 93 additions & 119 deletions

File tree

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ packages:
6868
path: ".."
6969
relative: true
7070
source: path
71-
version: "6.0.0"
71+
version: "6.0.1"
7272
flutter_lints:
7373
dependency: "direct dev"
7474
description:

lib/src/dotenv.dart

Lines changed: 53 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import 'dart:async';
22

3-
import 'package:flutter/material.dart';
43
import 'package:flutter/services.dart' show rootBundle;
54
import 'package:flutter/widgets.dart';
65

@@ -35,7 +34,7 @@ class DotEnv {
3534
bool _isInitialized = false;
3635
final Map<String, String> _envMap = {};
3736

38-
/// A copy of variables loaded at runtime from a file + any entries from mergeWith when loaded.
37+
/// Variables loaded at runtime from a file + any entries from mergeWith when loaded.
3938
Map<String, String> get env {
4039
if (!_isInitialized) {
4140
throw NotInitializedError();
@@ -60,62 +59,42 @@ class DotEnv {
6059
return value;
6160
}
6261

63-
/// Load the enviroment variable value as an [int]
64-
///
65-
/// If variable with [name] does not exist then [fallback] will be used.
66-
/// However if also no [fallback] is supplied an error will occur.
67-
///
68-
/// Furthermore an [FormatException] will be thrown if the variable with [name]
69-
/// exists but can not be parsed as an [int].
70-
int getInt(String name, {int? fallback}) {
71-
final value = maybeGet(name);
72-
if (value == null && fallback == null) {
73-
throw AssertionError(
74-
'$name variable not found. A non-null fallback is required for missing entries');
75-
}
76-
return value != null ? int.parse(value) : fallback!;
77-
}
62+
/// Throws [AssertionError] if not found and no [fallback] given.
63+
/// Throws [FormatException] if found but not parseable.
64+
int getInt(String name, {int? fallback}) =>
65+
_getTyped(name, fallback, int.parse);
66+
67+
/// Throws [AssertionError] if not found and no [fallback] given.
68+
/// Throws [FormatException] if found but not parseable.
69+
double getDouble(String name, {double? fallback}) =>
70+
_getTyped(name, fallback, double.parse);
71+
72+
/// Accepts `true`/`1` and `false`/`0` (case-insensitive).
73+
/// Throws [AssertionError] if not found and no [fallback] given.
74+
/// Throws [FormatException] if found but not parseable.
75+
bool getBool(String name, {bool? fallback}) =>
76+
_getTyped(name, fallback, _parseBool);
7877

79-
/// Load the enviroment variable value as a [double]
80-
///
81-
/// If variable with [name] does not exist then [fallback] will be used.
82-
/// However if also no [fallback] is supplied an error will occur.
83-
///
84-
/// Furthermore an [FormatException] will be thrown if the variable with [name]
85-
/// exists but can not be parsed as a [double].
86-
double getDouble(String name, {double? fallback}) {
78+
T _getTyped<T>(String name, T? fallback, T Function(String) parse) {
8779
final value = maybeGet(name);
8880
if (value == null && fallback == null) {
8981
throw AssertionError(
9082
'$name variable not found. A non-null fallback is required for missing entries');
9183
}
92-
return value != null ? double.parse(value) : fallback!;
84+
return value != null ? parse(value) : fallback!;
9385
}
9486

95-
/// Load the enviroment variable value as a [bool]
96-
///
97-
/// If variable with [name] does not exist then [fallback] will be used.
98-
/// However if also no [fallback] is supplied an error will occur.
99-
///
100-
/// Furthermore an [FormatException] will be thrown if the variable with [name]
101-
/// exists but can not be parsed as a [bool].
102-
bool getBool(String name, {bool? fallback}) {
103-
final value = maybeGet(name);
104-
if (value == null && fallback == null) {
105-
throw AssertionError(
106-
'$name variable not found. A non-null fallback is required for missing entries');
107-
}
108-
if (value != null) {
109-
if (['true', '1'].contains(value.toLowerCase())) {
87+
static bool _parseBool(String value) {
88+
switch (value.toLowerCase()) {
89+
case 'true':
90+
case '1':
11091
return true;
111-
} else if (['false', '0'].contains(value.toLowerCase())) {
92+
case 'false':
93+
case '0':
11294
return false;
113-
} else {
95+
default:
11496
throw const FormatException('Could not parse as a bool');
115-
}
11697
}
117-
118-
return fallback!;
11998
}
12099

121100
String? maybeGet(String name, {String? fallback}) => env[name] ?? fallback;
@@ -159,15 +138,12 @@ class DotEnv {
159138
}
160139
}
161140

162-
final linesFromMergeWith = mergeWith.entries
163-
.map((entry) => "${entry.key}=${entry.value}")
164-
.toList();
165-
final allLines = linesFromMergeWith
166-
..addAll(linesFromOverrides)
167-
..addAll(linesFromFile);
168-
final envEntries = parser.parse(allLines);
169-
_envMap.addAll(envEntries);
170-
_isInitialized = true;
141+
_mergeAndStore(
142+
linesFromFile: linesFromFile,
143+
linesFromOverrides: linesFromOverrides,
144+
mergeWith: mergeWith,
145+
parser: parser,
146+
);
171147
}
172148

173149
void loadFromString({
@@ -187,21 +163,28 @@ class DotEnv {
187163
throw EmptyEnvFileError();
188164
}
189165
final linesFromFile = envString.split('\n');
190-
final linesFromOverrides = overrideWith
191-
.map((String lines) => lines.split('\n'))
192-
.expand((x) => x)
193-
.toList();
194-
final linesFromMergeWith = mergeWith.entries
195-
.map((entry) => "${entry.key}=${entry.value}")
196-
.toList();
197-
198-
final allLines = linesFromMergeWith
199-
..addAll(linesFromOverrides)
200-
..addAll(linesFromFile);
201-
202-
final envEntries = parser.parse(allLines);
166+
final linesFromOverrides = overrideWith.expand((s) => s.split('\n')).toList();
167+
168+
_mergeAndStore(
169+
linesFromFile: linesFromFile,
170+
linesFromOverrides: linesFromOverrides,
171+
mergeWith: mergeWith,
172+
parser: parser,
173+
);
174+
}
203175

204-
_envMap.addAll(envEntries);
176+
void _mergeAndStore({
177+
required List<String> linesFromFile,
178+
required List<String> linesFromOverrides,
179+
required Map<String, String> mergeWith,
180+
required Parser parser,
181+
}) {
182+
final allLines = [
183+
...mergeWith.entries.map((e) => '${e.key}=${e.value}'),
184+
...linesFromOverrides,
185+
...linesFromFile,
186+
];
187+
_envMap.addAll(parser.parse(allLines));
205188
_isInitialized = true;
206189
}
207190

@@ -213,7 +196,7 @@ class DotEnv {
213196
Future<List<String>> _getEntriesFromFile(String filename) async {
214197
try {
215198
WidgetsFlutterBinding.ensureInitialized();
216-
var envString = await rootBundle.loadString(filename);
199+
final envString = await rootBundle.loadString(filename);
217200
if (envString.isEmpty) {
218201
throw EmptyEnvFileError(filename: filename);
219202
}

lib/src/parser.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ class Parser {
1414
/// Creates a [Map](dart:core).
1515
/// Duplicate keys are silently discarded.
1616
Map<String, String> parse(Iterable<String> lines) {
17-
var envMap = <String, String>{};
18-
for (var line in lines) {
17+
final envMap = <String, String>{};
18+
for (final line in lines) {
1919
final parsedKeyValue = parseOne(line, envMap: envMap);
2020
if (parsedKeyValue.isEmpty) continue;
2121
envMap.putIfAbsent(
@@ -40,7 +40,7 @@ class Parser {
4040
.trim();
4141
final quoteChar = getSurroundingQuoteCharacter(envValue);
4242
var envValueWithoutQuotes = removeSurroundingQuotes(envValue);
43-
// Add any escapted quotes
43+
// Add any escaped quotes
4444
if (quoteChar == _singleQuote) {
4545
envValueWithoutQuotes = envValueWithoutQuotes.replaceAll("\\'", "'");
4646
// Return. We don't expect any bash variables in single quoted strings

test/dotenv_test.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'package:flutter_test/flutter_test.dart';
55
void main() {
66
group('dotenv', () {
77
setUp(() {
8-
print(Directory.current.toString());
98
dotenv.loadFromString(
109
envString: File('test/.env')
1110
.readAsStringSync()); // mergeWith: Platform.environment
@@ -53,7 +52,6 @@ void main() {
5352

5453
group('dotenv with overrides', () {
5554
setUp(() {
56-
print(Directory.current.toString());
5755
dotenv.loadFromString(
5856
envString: File('test/.env').readAsStringSync(),
5957
overrideWith: [File("test/.env-override").readAsStringSync()],
@@ -88,19 +86,19 @@ void main() {
8886
expect(dotenv.env['OVERRIDE_VALUE'], 'overridden');
8987
});
9088
test(
91-
'when getting a vairable that is not in .env, we should get the fallback we defined',
89+
'when getting a variable that is not in .env, we should get the fallback we defined',
9290
() {
9391
expect(dotenv.get('FOO', fallback: 'bar'), 'foo');
9492
expect(dotenv.get('COMMENTS', fallback: 'sample'), 'sample');
9593
expect(dotenv.get('EQUAL_SIGNS', fallback: 'sample'), 'equals==');
9694
});
9795
test(
98-
'when getting a vairable that is not in .env, we should get an error thrown',
96+
'when getting a variable that is not in .env, we should get an error thrown',
9997
() {
10098
expect(() => dotenv.get('COMMENTS'), throwsAssertionError);
10199
});
102100
test(
103-
'when getting a vairable using the nullable getter, we should get null if no fallback is defined',
101+
'when getting a variable using the nullable getter, we should get null if no fallback is defined',
104102
() {
105103
expect(dotenv.maybeGet('COMMENTS'), null);
106104
expect(dotenv.maybeGet('COMMENTS', fallback: 'sample'), 'sample');

0 commit comments

Comments
 (0)