module Underpass
class ErrorParser
PATTERNS = {
timeout: /Query timed out.*?at line (\d+) after (\d+) seconds/i,
memory: /Query run out of memory.*?(\d+)\s*MB/i,
syntax: /parse error:?\s*(.+)/i,
runtime: /runtime error:?\s*(.+)/i
}.freeze
def self.parse(response_body, status_code)
return rate_limit_result if status_code == 429
text = (response_body)
parse_error_text(text, status_code)
end
def self.(body)
return '' if body.nil? || body.empty?
if (match = body.match(%r{<strong[^>]*>(.*?)</strong>}im))
return match[1].gsub(/<[^>]+>/, '').strip
end
if (match = body.match(%r{<p[^>]*>(.*?)</p>}im))
return match[1].gsub(/<[^>]+>/, '').strip
end
body.gsub(/<[^>]+>/, ' ').gsub(/\s+/, ' ').strip
end
private_class_method :extract_error_text
def self.parse_error_text(text, status_code)
parse_timeout(text) ||
parse_memory(text) ||
parse_syntax(text) ||
parse_runtime(text) ||
unknown_result(text, status_code)
end
private_class_method :parse_error_text
def self.parse_timeout(text)
return unless (match = text.match(PATTERNS[:timeout]))
{ code: 'timeout', message: text, details: { line: match[1].to_i, timeout_seconds: match[2].to_i } }
end
private_class_method :parse_timeout
def self.parse_memory(text)
return unless (match = text.match(PATTERNS[:memory]))
{ code: 'memory', message: text, details: { memory_mb: match[1].to_i } }
end
private_class_method :parse_memory
def self.parse_syntax(text)
return unless (match = text.match(PATTERNS[:syntax]))
{ code: 'syntax', message: match[1].strip, details: (match[1]) }
end
private_class_method :parse_syntax
def self.parse_runtime(text)
return unless (match = text.match(PATTERNS[:runtime]))
{ code: 'runtime', message: match[1].strip, details: {} }
end
private_class_method :parse_runtime
def self.(message)
if (match = message.match(/line\s+(\d+)/i))
{ line: match[1].to_i }
else
{}
end
end
private_class_method :extract_syntax_details
def self.rate_limit_result
{
code: 'rate_limit',
message: 'Rate limited by the Overpass API',
details: {}
}
end
private_class_method :rate_limit_result
def self.unknown_result(text, status_code)
message = text.empty? ? "HTTP #{status_code} error" : text
{
code: 'unknown',
message: message,
details: {}
}
end
private_class_method :unknown_result
end
end