Skip to content

Commit

Permalink
Refactor parser to avoid repeated scan/gsub
Browse files Browse the repository at this point in the history
  • Loading branch information
bkeepers committed Dec 12, 2024
1 parent 94d0a62 commit 25bc9d6
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 40 deletions.
72 changes: 33 additions & 39 deletions lib/dotenv/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,23 @@ class Parser
]

LINE = /
(?:^|\A) # beginning of line
\s* # leading whitespace
(?:export\s+)? # optional export
([\w.]+) # key
(?:\s*=\s*?|:\s+?) # separator
( # optional value begin
\s*'(?:\\'|[^'])*' # single quoted value
| # or
\s*"(?:\\"|[^"])*" # double quoted value
| # or
[^\#\r\n]+ # unquoted value
)? # value end
\s* # trailing whitespace
(?:\#.*)? # optional comment
(?:$|\z) # end of line
(?:^|\A) # beginning of line
\s* # leading whitespace
(?<export>export\s+)? # optional export
(?<key>[\w.]+) # key
(?: # optional separator and value
(?:\s*=\s*?|:\s+?) # separator
(?<value> # optional value begin
\s*'(?:\\'|[^'])*' # single quoted value
| # or
\s*"(?:\\"|[^"])*" # double quoted value
| # or
[^\#\n]+ # unquoted value
)? # value end
)? # separator and value end
\s* # trailing whitespace
(?:\#.*)? # optional comment
(?:$|\z) # end of line
/x

class << self
Expand All @@ -40,25 +42,29 @@ def call(...)
end

def initialize(string, overwrite: false)
@string = string
# Convert line breaks to same format
@string = string.gsub(/[\n\r]+/, "\n")
@hash = {}
@overwrite = overwrite
end

def call
# Convert line breaks to same format
lines = @string.gsub(/\r\n?/, "\n")
# Process matches
lines.scan(LINE).each do |key, value|
# Skip parsing values that will be ignored
next if ignore?(key)
@string.scan(LINE) do
match = $LAST_MATCH_INFO

@hash[key] = parse_value(value || "")
end
# Process non-matches
lines.gsub(LINE, "").split(/[\n\r]+/).each do |line|
parse_line(line)
# Skip parsing values that will be ignored
next if ignore?(match[:key])

# Check for exported variable with no value
if match[:export] && !match[:value]
if !@hash.member?(match[:key])
raise FormatError, "Line #{match.to_s.inspect} has an unset variable"
end
else
@hash[match[:key]] = parse_value(match[:value] || "")
end
end

@hash
end

Expand All @@ -69,14 +75,6 @@ def ignore?(key)
!@overwrite && key != "DOTENV_LINEBREAK_MODE" && ENV.key?(key)
end

def parse_line(line)
if line.split.first == "export"
if variable_not_set?(line)
raise FormatError, "Line #{line.inspect} has an unset variable"
end
end
end

QUOTED_STRING = /\A(['"])(.*)\1\z/m
def parse_value(value)
# Remove surrounding quotes
Expand Down Expand Up @@ -106,9 +104,5 @@ def expand_newlines(value)
value.gsub('\n', "\\\\\\n").gsub('\r', "\\\\\\r")
end
end

def variable_not_set?(line)
!line.split[1..].all? { |var| @hash.member?(var) }
end
end
end
2 changes: 1 addition & 1 deletion lib/dotenv/template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def is_comment?(line)

def var_defined?(line)
match = Dotenv::Parser::LINE.match(line)
match && match[1]
match && match[:key]
end

def line_blank?(line)
Expand Down

0 comments on commit 25bc9d6

Please sign in to comment.