1 module ddotenv.dotenv; 2 3 private enum LineType { Whitespace, Directive }; 4 5 private struct Line 6 { 7 LineType lineType; 8 string key; 9 string value; 10 } 11 12 public class DotEnvFormatException : Exception 13 { 14 pure @safe nothrow this(string file = __FILE__, size_t line = __LINE__, Throwable next = null) 15 { 16 super(".env format error", file, line, next); 17 } 18 } 19 20 public void loadDotEnv() 21 { 22 loadDotEnv(".env", false); 23 } 24 25 public void loadDotEnv(string path) 26 { 27 loadDotEnv(path, true); 28 } 29 30 private void loadDotEnv(string path, bool throwOnError) 31 { 32 import std.stdio : File; 33 import std.exception : ErrnoException; 34 File envFile; 35 try { 36 envFile = File(path, "r"); 37 } catch(ErrnoException e) { 38 if( throwOnError ) throw e; 39 return; 40 } 41 42 foreach(line; envFile.byLine) 43 { 44 auto parsedLine = parseLine(line); 45 if( parsedLine.lineType == LineType.Directive ) 46 { 47 import std.process : environment; 48 environment[parsedLine.key] = parsedLine.value; 49 } 50 } 51 } 52 53 private @safe Line parseLine(const char[] line) 54 { 55 import std.regex : ctRegex, matchFirst; 56 57 Line result; 58 result.lineType = LineType.Whitespace; 59 60 auto patternForBlankLine = ctRegex!(`^\s*(?:#.*)?$`); 61 auto patternForSimpleLine = ctRegex!(`^\s*(?:export\s+)?(\S+)\s*=\s*([^#"]*)(?:#.*)?$`); 62 63 if( matchFirst(line, patternForBlankLine) ) 64 { 65 return result; 66 } 67 68 auto simpleCaptures = matchFirst(line, patternForSimpleLine); 69 70 if( simpleCaptures.empty() ) 71 { 72 throw new DotEnvFormatException(); 73 } 74 75 import std..string : stripRight; 76 77 result.key = simpleCaptures[1].idup; 78 result.value = simpleCaptures[2].idup.stripRight(); 79 result.lineType = LineType.Directive; 80 81 return result; 82 } 83 84 unittest 85 { 86 assert( parseLine("# comment").lineType == LineType.Whitespace ); 87 auto parsed = parseLine("KEY=VALUE"); 88 assert( parsed.key == "KEY" && parsed.value == "VALUE" ); 89 90 parsed = parseLine("KEY=val # hello!"); 91 assert( parsed.value == "val" ); 92 93 parsed = parseLine(" key=value"); 94 assert( parsed.key == "key" ); 95 96 } 97 98 99