Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 27 additions & 15 deletions lib/net/imap/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ def response_size_msg
# NOTE: Parser attributes are provided for debugging and inspection only.
# Their names and semantics may change incompatibly in any release.
class ResponseParseError < Error
# returns "" for all highlights
ESC_NO_HL = Hash.new("").freeze
private_constant :ESC_NO_HL

# ANSI highlights, but no colors
ESC_NO_COLOR = Hash.new("").update(
reset: "\e[m",
val: "\e[1m", # bold
alt: "\e[1;4m", # bold and underlined
).freeze
private_constant :ESC_NO_COLOR

# Net::IMAP::ResponseParser, unless a custom parser produced the error.
attr_reader :parser_class

Expand Down Expand Up @@ -106,20 +118,21 @@ def initialize(message = "unspecified parse error",
# Most parser method names are based on rules in the IMAP grammar.
def detailed_message(parser_state: Net::IMAP.debug,
parser_backtrace: false,
highlight: false,
**)
return super unless parser_state || parser_backtrace
msg = super.dup
esc = highlight ? ESC_NO_COLOR : ESC_NO_HL
hl = ->str { str % esc }
val = ->str, val { val.nil? ? "nil" : str % esc % val }
if parser_state && (string || pos || lex_state || token)
msg << "\n processed : %p" % processed_string
msg << "\n remaining : %p" % remaining_string
msg << "\n pos : %p" % pos
msg << "\n lex_state : %p" % lex_state
msg << "\n token : "
if token
msg << "%p => %p" % [token.symbol, token.value]
else
msg << "nil"
end
msg << "\n processed : " << val["%{val}%%p%{reset}", processed_string]
msg << "\n remaining : " << val["%{alt}%%p%{reset}", remaining_string]
msg << "\n pos : " << val["%{val}%%p%{reset}", pos]
msg << "\n lex_state : " << val["%{val}%%p%{reset}", lex_state]
msg << "\n token : " << val[
"%{val}%%<symbol>p%{reset} => %{val}%%<value>p%{reset}", token&.to_h
]
end
if parser_backtrace
backtrace_locations&.each_with_index do |loc, idx|
Expand All @@ -130,11 +143,10 @@ def detailed_message(parser_state: Net::IMAP.debug,
else
next unless loc.path&.include?("net/imap/response_parser")
end
msg << "\n caller[%2d]: %-30s (%s:%d)" % [
idx,
loc.base_label,
File.basename(loc.path, ".rb"),
loc.lineno
msg << "\n %s: %s (%s:%d)" % [
"caller[%2d]" % idx,
hl["%{val}%%-30s%{reset}"] % loc.base_label,
File.basename(loc.path, ".rb"), loc.lineno
]
end
end
Expand Down
32 changes: 26 additions & 6 deletions test/net/imap/test_errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"#{BOLD}#{msg} (#{BOLD_UNDERLINE}#{name}#{RESET}#{BOLD})#{RESET}",
err.detailed_message(highlight: true, parser_state: false)
)
assert_equal(<<~MSG.strip, err.detailed_message)

Check failure on line 67 in test/net/imap/test_errors.rb

View workflow job for this annotation

GitHub Actions / build (truffleruby-head / ubuntu-latest)

Failure

<"unexpected QUOTED (expected \"]\") (Net::IMAP::ResponseParseError)\n" + " processed : \"tag OK [Error=\\\"Microsoft.Exchange.Error: foo\\\"\"\n" + " remaining : \"] done\\r\\n\"\n" + " pos : 45\n" + " lex_state : :EXPR_BEG\n" + " token : :QUOTED => \"Microsoft.Exchange.Error: foo\""> expected but was <"unexpected QUOTED (expected \"]\") (Net::IMAP::ResponseParseError)\n" + " processed : \n" + " BUG in Net::IMAP::ResponseParseError#detailed_message: \e[1mkey not found: :val (\e[1;4mKeyError\e[m\e[1m)\e[m">. diff: unexpected QUOTED (expected "]") (Net::IMAP::ResponseParseError) + processed : + BUG in Net::IMAP::ResponseParseError#detailed_message: key not found: :val (KeyError�[m)�[m - processed : "tag OK [Error=\"Microsoft.Exchange.Error: foo\"" - remaining : "] done\r\n" - pos : 45 - lex_state : :EXPR_BEG - token : :QUOTED => "Microsoft.Exchange.Error: foo" folded diff: unexpected QUOTED (expected "]") (Net::IMAP::ResponseParseError) + processed : + BUG in Net::IMAP::ResponseParseError#detailed_message: key not found: :v + al (KeyError�[m)�[m - processed : "tag OK [Error=\"Microsoft.Exchange.Error: foo\"" - remaining : "] done\r\n" - pos : 45 - lex_state : :EXPR_BEG - token : :QUOTED => "Microsoft.Exchange.Error: foo"

Check failure on line 67 in test/net/imap/test_errors.rb

View workflow job for this annotation

GitHub Actions / build (truffleruby-head / ubuntu-latest)

Failure

<"unexpected QUOTED (expected \"]\") (Net::IMAP::ResponseParseError)\n" + " processed : \"tag OK [Error=\\\"Microsoft.Exchange.Error: foo\\\"\"\n" + " remaining : \"] done\\r\\n\"\n" + " pos : 45\n" + " lex_state : :EXPR_BEG\n" + " token : :QUOTED => \"Microsoft.Exchange.Error: foo\""> expected but was <"unexpected QUOTED (expected \"]\") (Net::IMAP::ResponseParseError)\n" + " processed : \n" + " BUG in Net::IMAP::ResponseParseError#detailed_message: \e[1mkey not found: :val (\e[1;4mKeyError\e[m\e[1m)\e[m">. diff: unexpected QUOTED (expected "]") (Net::IMAP::ResponseParseError) + processed : + BUG in Net::IMAP::ResponseParseError#detailed_message: key not found: :val (KeyError�[m)�[m - processed : "tag OK [Error=\"Microsoft.Exchange.Error: foo\"" - remaining : "] done\r\n" - pos : 45 - lex_state : :EXPR_BEG - token : :QUOTED => "Microsoft.Exchange.Error: foo" folded diff: unexpected QUOTED (expected "]") (Net::IMAP::ResponseParseError) + processed : + BUG in Net::IMAP::ResponseParseError#detailed_message: key not found: :v + al (KeyError�[m)�[m - processed : "tag OK [Error=\"Microsoft.Exchange.Error: foo\"" - remaining : "] done\r\n" - pos : 45 - lex_state : :EXPR_BEG - token : :QUOTED => "Microsoft.Exchange.Error: foo"
#{msg} (#{name})
processed : "tag OK [Error=\\"Microsoft.Exchange.Error: foo\\""
remaining : "] done\\r\\n"
Expand All @@ -74,11 +74,11 @@
MSG
assert_equal(<<~MSG.strip, err.detailed_message(highlight: true))
#{BOLD}#{msg} (#{BOLD_UNDERLINE}#{name}#{RESET}#{BOLD})#{RESET}
processed : "tag OK [Error=\\"Microsoft.Exchange.Error: foo\\""
remaining : "] done\\r\\n"
pos : 45
lex_state : :EXPR_BEG
token : :QUOTED => "Microsoft.Exchange.Error: foo"
processed : #{BOLD}"tag OK [Error=\\"Microsoft.Exchange.Error: foo\\""#{RESET}
remaining : #{BOLD_UNDERLINE}"] done\\r\\n"#{RESET}
pos : #{BOLD}45#{RESET}
lex_state : #{BOLD}:EXPR_BEG#{RESET}
token : #{BOLD}:QUOTED#{RESET} => #{BOLD}"Microsoft.Exchange.Error: foo"#{RESET}
MSG

# `parser_state` defaults to `Net::IMAP.debug`:
Expand All @@ -89,14 +89,34 @@
err.detailed_message(highlight: true)
)

# with a nil token
parser_state = [string, :EXPR_BEG, 45, nil]
err = Net::IMAP::ResponseParseError.new(msg, string:, parser_state:)
assert_equal(<<~MSG.strip, err.detailed_message(parser_state: true))
#{msg} (#{name})
processed : "tag OK [Error=\\"Microsoft.Exchange.Error: foo\\""
remaining : "] done\\r\\n"
pos : 45
lex_state : :EXPR_BEG
token : nil
MSG
assert_equal(<<~MSG.strip, err.detailed_message(highlight: true, parser_state: true))
#{BOLD}#{msg} (#{BOLD_UNDERLINE}#{name}#{RESET}#{BOLD})#{RESET}
processed : #{BOLD}"tag OK [Error=\\"Microsoft.Exchange.Error: foo\\""#{RESET}
remaining : #{BOLD_UNDERLINE}"] done\\r\\n"#{RESET}
pos : #{BOLD}45#{RESET}
lex_state : #{BOLD}:EXPR_BEG#{RESET}
token : nil
MSG

# with parser_backtrace
Net::IMAP.debug = false
parser = Net::IMAP::ResponseParser.new
error = parser.parse("* 123 FETCH (UNKNOWN ...)\r\n") rescue $!
no_hl = error.detailed_message(parser_backtrace: true)
no_color = error.detailed_message(parser_backtrace: true, highlight: true)
assert_include no_hl, "caller[ 1]: %-30s (" % "msg_att"
assert_include no_color, "caller[ 1]: %-30s (" % "msg_att"
assert_include no_color, "caller[ 1]: #{BOLD}%-30s#{RESET} (" % "msg_att"
end

test "ResponseTooLargeError" do
Expand Down
Loading