diff --git a/src/dotenv.net/Parser.cs b/src/dotenv.net/Parser.cs index 11b7f49..a8b0b5b 100644 --- a/src/dotenv.net/Parser.cs +++ b/src/dotenv.net/Parser.cs @@ -1,89 +1,75 @@ using System; using System.Collections.Generic; -using System.Text; namespace dotenv.net { internal static class Parser { - private const string SingleQuote = "'"; - private const string DoubleQuotes = "\""; - - internal static ReadOnlySpan> Parse(ReadOnlySpan rawEnvRows, - bool trimValues) + private static readonly char[] SingleQuote = {'\''}; + private static readonly char[] DoubleQuotes = {'"'}; + + internal static ReadOnlySpan> Parse(ReadOnlySpan dotEnvRows, + bool shouldTrimValue) { - var keyValuePairs = new List>(); + var validEntries = new List>(); - for (var i = 0; i < rawEnvRows.Length; i++) + foreach (var dotEnvRow in dotEnvRows) { - var rawEnvRow = rawEnvRows[i]; - - if(rawEnvRow.StartsWith("#")) continue; - - if (rawEnvRow.Contains("=\"")) - { - var key = rawEnvRow.Substring(0, rawEnvRow.IndexOf("=\"", StringComparison.Ordinal)); - var valueStringBuilder = new StringBuilder(); - valueStringBuilder.Append(rawEnvRow.Substring(rawEnvRow.IndexOf("=\"", StringComparison.Ordinal) + 2)); - - while (!rawEnvRow.EndsWith("\"")) - { - i++; - if (i >= rawEnvRows.Length) - { - break; - } - rawEnvRow = rawEnvRows[i]; - valueStringBuilder.Append(rawEnvRow); - } - //Remove last " - valueStringBuilder.Remove(valueStringBuilder.Length - 1, 1); - - var value = valueStringBuilder.ToString(); - if (trimValues) - { - value = value.Trim(); - } - - keyValuePairs.Add(new KeyValuePair(key, value)); - } - else - { - //Check that line is not empty - var rawEnvEmpty = rawEnvRow.Trim(); - if(string.IsNullOrEmpty(rawEnvEmpty)) continue; - - // Regular key-value pair - var keyValue = rawEnvRow.Split(new[] { '=' }, 2); - - var key = keyValue[0].Trim(); - var value = keyValue[1]; - - if(string.IsNullOrEmpty(key)) continue; - - if (IsQuoted(value)) - { - value = StripQuotes(value); - } - - if (trimValues) - { - value = value.Trim(); - } - - keyValuePairs.Add(new KeyValuePair(key, value)); - } + var row = new ReadOnlySpan(dotEnvRow.TrimStart().ToCharArray()); + + if (row.IsEmpty) + continue; + + if (row.IsComment()) + continue; + + if (row.HasNoKey(out var index)) + continue; + + var key = row.Key(index); + var value = row.Value(index, shouldTrimValue); + validEntries.Add(new KeyValuePair(key, value)); } - return keyValuePairs.ToArray(); + return new ReadOnlySpan>(validEntries.ToArray()); } - private static bool IsQuoted(string value) => (value.StartsWith(SingleQuote) && value.EndsWith(SingleQuote)) - || (value.StartsWith(DoubleQuotes) && value.EndsWith(DoubleQuotes)); + private static bool IsComment(this ReadOnlySpan row) => row[0] == '#'; + + private static bool HasNoKey(this ReadOnlySpan row, out int index) + { + index = row.IndexOf('='); + return index <= 0; + } + + private static bool IsQuoted(this ReadOnlySpan row) => (row.StartsWith(SingleQuote) && row.EndsWith(SingleQuote)) + || (row.StartsWith(DoubleQuotes) && row.EndsWith(DoubleQuotes)); + + private static ReadOnlySpan StripQuotes(this ReadOnlySpan row) => row.Trim('\'').Trim('\"'); - private static string StripQuotes(string value) + private static string Key(this ReadOnlySpan row, int index) { - return value.Substring(1, value.Length - 2); + var untrimmedKey = row.Slice(0, index); + return untrimmedKey.Trim().ToString(); + } + + private static string Value(this ReadOnlySpan row, int index, bool trimValue) + { + var value = row.Slice(index + 1); + + // handle quoted values + if (value.IsQuoted()) + { + value = value.StripQuotes(); + } + + // trim output if requested + if (trimValue) + { + value = value.Trim(); + } + + return value.ToString(); } } } \ No newline at end of file diff --git a/tests/dotenv.net.Tests/DotEnv.Fluent.Tests.cs b/tests/dotenv.net.Tests/DotEnv.Fluent.Tests.cs index da83777..da1d058 100644 --- a/tests/dotenv.net.Tests/DotEnv.Fluent.Tests.cs +++ b/tests/dotenv.net.Tests/DotEnv.Fluent.Tests.cs @@ -12,7 +12,6 @@ public class DotEnvFluentTests private const string WhitespacesEnvFileName = "whitespaces.env"; private const string NonExistentEnvFileName = "non-existent.env"; private const string QuotationsEnvFileName = "quotations.env"; - private const string MultiLinesEnvFileName = "multi-lines.env"; private const string AsciiEnvFileName = "ascii.env"; private const string GenericEnvFileName = "generic.env"; private const string IncompleteEnvFileName = "incomplete.env"; @@ -53,7 +52,7 @@ public void ConfigShouldLoadEnvWithTrimOptions() EnvReader.GetStringValue("DB_DATABASE") .Should() .Be("laravel"); - + DotEnv.Fluent() .WithEnvFiles(WhitespacesEnvFileName) .WithoutTrimValues() @@ -68,7 +67,7 @@ public void ConfigShouldLoadEnvWithTrimOptions() public void ConfigShouldLoadEnvWithExistingVarOverwriteOptions() { Environment.SetEnvironmentVariable("Generic", "Existing"); - + DotEnv.Fluent() .WithEnvFiles(GenericEnvFileName) .WithoutOverwriteExistingVars() @@ -77,7 +76,7 @@ public void ConfigShouldLoadEnvWithExistingVarOverwriteOptions() EnvReader.GetStringValue("Generic") .Should() .Be("Existing"); - + DotEnv.Fluent() .WithEnvFiles(GenericEnvFileName) .WithOverwriteExistingVars() @@ -98,7 +97,7 @@ public void ConfigShouldLoadDefaultEnvWithProbeOptions() action.Should() .ThrowExactly(); - + action = () => DotEnv.Fluent() .WithProbeForEnv(5) .WithExceptions() @@ -128,25 +127,6 @@ public void ConfigShouldLoadEnvWithQuotedValues() .Be("single"); } - [Fact] - public void ConfigLoadsMultilineEnvs() - { - DotEnv.Fluent() - .WithEnvFiles(MultiLinesEnvFileName) - .WithTrimValues() - .Load(); - - EnvReader.GetStringValue("DOUBLE_QUOTE") - .Should() - .Be("double"); - EnvReader.GetStringValue("DOUBLE_QUOTE_MULTI_LINE") - .Should() - .Be("doubler"); - EnvReader.GetStringValue("DOUBLE_QUOTE_EVEN_MORE_LINES") - .Should() - .Be("doublest"); - } - [Fact] public void ConfigShouldLoadEnvWithInvalidEnvEntries() { diff --git a/tests/dotenv.net.Tests/multi-lines.env b/tests/dotenv.net.Tests/multi-lines.env deleted file mode 100644 index af1abf0..0000000 --- a/tests/dotenv.net.Tests/multi-lines.env +++ /dev/null @@ -1,8 +0,0 @@ -DOUBLE_QUOTE="double" -DOUBLE_QUOTE_MULTI_LINE="dou -bler" -DOUBLE_QUOTE_EVEN_MORE_LINES="dou - -b - -lest"