tdiary 5.5.0 → 5.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +2 -0
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +37 -37
  5. data/doc/UPGRADE.md +1 -1
  6. data/lib/tdiary/admin.rb +11 -13
  7. data/lib/tdiary/application.rb +2 -7
  8. data/lib/tdiary/author_only_base.rb +78 -9
  9. data/lib/tdiary/base.rb +2 -2
  10. data/lib/tdiary/cgi_adapter.rb +7 -5
  11. data/lib/tdiary/cgi_compat.rb +27 -95
  12. data/lib/tdiary/configuration.rb +4 -0
  13. data/lib/tdiary/core_ext.rb +146 -1
  14. data/lib/tdiary/dispatcher/index_main.rb +5 -23
  15. data/lib/tdiary/dispatcher/update_main.rb +2 -17
  16. data/lib/tdiary/dispatcher.rb +29 -15
  17. data/lib/tdiary/filter/default.rb +2 -2
  18. data/lib/tdiary/filter/spam.rb +2 -2
  19. data/lib/tdiary/filter.rb +1 -1
  20. data/lib/tdiary/plugin/00default.rb +18 -3
  21. data/lib/tdiary/plugin/en/00default.rb +15 -0
  22. data/lib/tdiary/plugin/ja/00default.rb +15 -0
  23. data/lib/tdiary/plugin.rb +1 -1
  24. data/lib/tdiary/rack/error_handler.rb +36 -0
  25. data/lib/tdiary/rack.rb +1 -0
  26. data/lib/tdiary/request.rb +12 -2
  27. data/lib/tdiary/server.rb +25 -3
  28. data/lib/tdiary/version.rb +1 -1
  29. data/lib/tdiary/view.rb +33 -28
  30. data/lib/tdiary/view_helper.rb +10 -2
  31. data/lib/tdiary.rb +6 -1
  32. data/misc/plugin/image.rb +1 -1
  33. data/misc/plugin/makerss.rb +3 -3
  34. data/tdiary.conf.sample +8 -0
  35. data/tdiary.conf.sample-en +8 -0
  36. data/vendor/bundle/ruby/3.4.0/cache/net-imap-0.6.6.gem +0 -0
  37. data/vendor/bundle/ruby/3.4.0/cache/net-protocol-0.3.0.gem +0 -0
  38. data/vendor/bundle/ruby/3.4.0/cache/rack-3.2.7.gem +0 -0
  39. data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/cgi-0.5.2/gem_make.out +5 -5
  40. data/vendor/bundle/ruby/3.4.0/extensions/x86_64-linux/3.4.0/date-3.5.1/gem_make.out +5 -5
  41. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/Gemfile +3 -4
  42. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/README.md +1 -1
  43. data/vendor/bundle/ruby/3.4.0/gems/net-imap-0.6.6/Rakefile +24 -0
  44. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/command_data.rb +62 -92
  45. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/config/attr_accessors.rb +8 -9
  46. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/config/attr_inheritance.rb +64 -1
  47. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/config/attr_version_defaults.rb +1 -4
  48. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/config.rb +209 -35
  49. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/connection_state.rb +1 -1
  50. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/data_encoding.rb +127 -27
  51. data/vendor/bundle/ruby/3.4.0/gems/net-imap-0.6.6/lib/net/imap/errors.rb +372 -0
  52. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/esearch_result.rb +6 -0
  53. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/response_data.rb +90 -11
  54. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/response_parser/parser_utils.rb +14 -23
  55. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/response_parser.rb +41 -26
  56. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/response_reader.rb +16 -21
  57. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/scram_authenticator.rb +3 -3
  58. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/search_result.rb +11 -1
  59. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sequence_set.rb +620 -312
  60. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/uidplus_data.rb +2 -63
  61. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap.rb +279 -162
  62. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/net-imap.gemspec +1 -1
  63. data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/rakelib/rdoc.rake +1 -18
  64. data/vendor/bundle/ruby/3.4.0/gems/{net-protocol-0.2.2/LICENSE.txt → net-protocol-0.3.0/BSDL} +3 -3
  65. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.3.0/COPYING +56 -0
  66. data/vendor/bundle/ruby/3.4.0/gems/{net-protocol-0.2.2 → net-protocol-0.3.0}/lib/net/protocol.rb +58 -4
  67. data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/CHANGELOG.md +54 -1
  68. data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/utils.rb +3 -3
  69. data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/version.rb +1 -1
  70. data/vendor/bundle/ruby/3.4.0/specifications/{net-imap-0.5.15.gemspec → net-imap-0.6.6.gemspec} +4 -4
  71. data/vendor/bundle/ruby/3.4.0/specifications/{net-protocol-0.2.2.gemspec → net-protocol-0.3.0.gemspec} +5 -5
  72. data/vendor/bundle/ruby/3.4.0/specifications/{rack-3.2.6.gemspec → rack-3.2.7.gemspec} +3 -3
  73. data/views/tdiary.rconf +2 -0
  74. metadata +127 -131
  75. data/vendor/bundle/ruby/3.4.0/cache/net-imap-0.5.15.gem +0 -0
  76. data/vendor/bundle/ruby/3.4.0/cache/net-protocol-0.2.2.gem +0 -0
  77. data/vendor/bundle/ruby/3.4.0/cache/rack-3.2.6.gem +0 -0
  78. data/vendor/bundle/ruby/3.4.0/gems/net-imap-0.5.15/Rakefile +0 -13
  79. data/vendor/bundle/ruby/3.4.0/gems/net-imap-0.5.15/lib/net/imap/data_lite.rb +0 -226
  80. data/vendor/bundle/ruby/3.4.0/gems/net-imap-0.5.15/lib/net/imap/errors.rb +0 -118
  81. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.2.2/Gemfile +0 -7
  82. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.2.2/Rakefile +0 -10
  83. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.2.2/bin/console +0 -14
  84. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.2.2/bin/setup +0 -8
  85. data/vendor/bundle/ruby/3.4.0/gems/net-protocol-0.2.2/net-protocol.gemspec +0 -33
  86. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/BSDL +0 -0
  87. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/COPYING +0 -0
  88. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/LICENSE.txt +0 -0
  89. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/docs/styles.css +0 -0
  90. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/authenticators.rb +0 -0
  91. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/config/attr_type_coercion.rb +0 -0
  92. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/deprecated_client_options.rb +0 -0
  93. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/fetch_data.rb +0 -0
  94. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/flags.rb +0 -0
  95. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/anonymous_authenticator.rb +0 -0
  96. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/authentication_exchange.rb +0 -0
  97. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/authenticators.rb +0 -0
  98. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/client_adapter.rb +0 -0
  99. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/cram_md5_authenticator.rb +0 -0
  100. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/digest_md5_authenticator.rb +0 -0
  101. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/external_authenticator.rb +0 -0
  102. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/gs2_header.rb +0 -0
  103. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/login_authenticator.rb +0 -0
  104. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/oauthbearer_authenticator.rb +0 -0
  105. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/plain_authenticator.rb +0 -0
  106. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/protocol_adapters.rb +0 -0
  107. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/scram_algorithm.rb +0 -0
  108. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/stringprep.rb +0 -0
  109. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl/xoauth2_authenticator.rb +0 -0
  110. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl.rb +0 -0
  111. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/sasl_adapter.rb +0 -0
  112. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep/nameprep.rb +0 -0
  113. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep/saslprep.rb +0 -0
  114. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep/saslprep_tables.rb +0 -0
  115. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep/tables.rb +0 -0
  116. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep/trace.rb +0 -0
  117. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/stringprep.rb +0 -0
  118. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/lib/net/imap/vanished_data.rb +0 -0
  119. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/rakelib/benchmarks.rake +0 -0
  120. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/rakelib/rfcs.rake +0 -0
  121. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/rakelib/saslprep.rake +0 -0
  122. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/rakelib/string_prep_tables_generator.rb +0 -0
  123. /data/vendor/bundle/ruby/3.4.0/gems/{net-imap-0.5.15 → net-imap-0.6.6}/sample/net-imap.rb +0 -0
  124. /data/vendor/bundle/ruby/3.4.0/gems/{net-protocol-0.2.2 → net-protocol-0.3.0}/README.md +0 -0
  125. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/CONTRIBUTING.md +0 -0
  126. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/MIT-LICENSE +0 -0
  127. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/README.md +0 -0
  128. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/SPEC.rdoc +0 -0
  129. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/auth/abstract/handler.rb +0 -0
  130. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/auth/abstract/request.rb +0 -0
  131. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/auth/basic.rb +0 -0
  132. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/bad_request.rb +0 -0
  133. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/body_proxy.rb +0 -0
  134. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/builder.rb +0 -0
  135. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/cascade.rb +0 -0
  136. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/common_logger.rb +0 -0
  137. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/conditional_get.rb +0 -0
  138. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/config.rb +0 -0
  139. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/constants.rb +0 -0
  140. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/content_length.rb +0 -0
  141. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/content_type.rb +0 -0
  142. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/deflater.rb +0 -0
  143. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/directory.rb +0 -0
  144. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/etag.rb +0 -0
  145. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/events.rb +0 -0
  146. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/files.rb +0 -0
  147. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/head.rb +0 -0
  148. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/headers.rb +0 -0
  149. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/lint.rb +0 -0
  150. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/lock.rb +0 -0
  151. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/media_type.rb +0 -0
  152. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/method_override.rb +0 -0
  153. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/mime.rb +0 -0
  154. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/mock.rb +0 -0
  155. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/mock_request.rb +0 -0
  156. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/mock_response.rb +0 -0
  157. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/multipart/generator.rb +0 -0
  158. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/multipart/parser.rb +0 -0
  159. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/multipart/uploaded_file.rb +0 -0
  160. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/multipart.rb +0 -0
  161. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/null_logger.rb +0 -0
  162. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/query_parser.rb +0 -0
  163. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/recursive.rb +0 -0
  164. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/reloader.rb +0 -0
  165. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/request.rb +0 -0
  166. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/response.rb +0 -0
  167. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/rewindable_input.rb +0 -0
  168. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/runtime.rb +0 -0
  169. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/sendfile.rb +0 -0
  170. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/show_exceptions.rb +0 -0
  171. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/show_status.rb +0 -0
  172. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/static.rb +0 -0
  173. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/tempfile_reaper.rb +0 -0
  174. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack/urlmap.rb +0 -0
  175. /data/vendor/bundle/ruby/3.4.0/gems/{rack-3.2.6 → rack-3.2.7}/lib/rack.rb +0 -0
@@ -0,0 +1,372 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
6
+ # Superclass of IMAP errors.
7
+ class Error < StandardError
8
+ end
9
+
10
+ class LoginDisabledError < Error
11
+ def initialize(msg = "Remote server has disabled the LOGIN command", ...)
12
+ super
13
+ end
14
+ end
15
+
16
+ # Error raised when data is in the incorrect format.
17
+ class DataFormatError < Error
18
+ end
19
+
20
+ # Error raised when the socket cannot be read, due to a Config limit.
21
+ class ResponseReadError < Error
22
+ end
23
+
24
+ # Error raised when a response is larger than IMAP#max_response_size.
25
+ class ResponseTooLargeError < ResponseReadError
26
+ attr_reader :bytes_read, :literal_size
27
+ attr_reader :max_response_size
28
+
29
+ def initialize(msg = nil, *args,
30
+ bytes_read: nil,
31
+ literal_size: nil,
32
+ max_response_size: nil,
33
+ **kwargs)
34
+ @bytes_read = bytes_read
35
+ @literal_size = literal_size
36
+ @max_response_size = max_response_size
37
+ msg ||= [
38
+ "Response size", response_size_msg, "exceeds max_response_size",
39
+ max_response_size && "(#{max_response_size}B)",
40
+ ].compact.join(" ")
41
+ super(msg, *args, **kwargs)
42
+ end
43
+
44
+ private
45
+
46
+ def response_size_msg
47
+ if bytes_read && literal_size
48
+ "(#{bytes_read}B read + #{literal_size}B literal)"
49
+ end
50
+ end
51
+ end
52
+
53
+ # Error raised when a response from the server is non-parsable.
54
+ #
55
+ # NOTE: Parser attributes are provided for debugging and inspection only.
56
+ # Their names and semantics may change incompatibly in any release.
57
+ class ResponseParseError < Error
58
+ # returns "" for all highlights
59
+ ESC_NO_HL = Hash.new("").freeze
60
+ private_constant :ESC_NO_HL
61
+
62
+ # Translates hash[:"/foo"] to hash[:reset] when hash.key?(:foo), else ""
63
+ #
64
+ # TODO: DRY this up with Config::AttrTypeCoercion.safe
65
+ if defined?(::Ractor.shareable_proc)
66
+ default_highlight = Ractor.shareable_proc {|hash, key|
67
+ %r{\A/(.+)} =~ key && hash.key?($1.to_sym) ? hash[:reset] : ""
68
+ }
69
+ else
70
+ default_highlight = nil.instance_eval { Proc.new {|hash, key|
71
+ %r{\A/(.+)} =~ key && hash.key?($1.to_sym) ? hash[:reset] : ""
72
+ } }
73
+ ::Ractor.make_shareable(default_highlight) if defined?(::Ractor)
74
+ end
75
+
76
+ # ANSI highlights, but no colors
77
+ ESC_NO_COLOR = Hash.new(&default_highlight).update(
78
+ reset: "\e[m",
79
+ val: "\e[1m", # bold
80
+ alt: "\e[1;4m", # bold and underlined
81
+ sym: "\e[1m", # bold
82
+ label: "\e[1m", # bold
83
+ ).freeze
84
+ private_constant :ESC_NO_COLOR
85
+
86
+ # ANSI highlights, with color
87
+ ESC_COLORS = Hash.new(&default_highlight).update(
88
+ reset: "\e[m",
89
+ key: "\e[95m", # bright magenta
90
+ idx: "\e[34m", # blue
91
+ val: "\e[36;40m", # cyan on black (to ensure contrast)
92
+ alt: "\e[1;33;40m", # bold; yellow on black
93
+ sym: "\e[33;40m", # yellow on black
94
+ label: "\e[1m", # bold
95
+ nil: "\e[35m", # magenta
96
+ ).freeze
97
+ private_constant :ESC_COLORS
98
+
99
+ # Net::IMAP::ResponseParser, unless a custom parser produced the error.
100
+ attr_reader :parser_class
101
+
102
+ # The full raw response string which was being parsed.
103
+ attr_reader :string
104
+
105
+ # The parser's byte position in #string when the error was raised.
106
+ #
107
+ # _NOTE:_ This attribute is provided for debugging and inspection only.
108
+ # Its name and semantics may change incompatibly in any release.
109
+ attr_reader :pos
110
+
111
+ # The parser's lex state
112
+ #
113
+ # _NOTE:_ This attribute is provided for debugging and inspection only.
114
+ # Its name and semantics may change incompatibly in any release.
115
+ attr_reader :lex_state
116
+
117
+ # The last lexed token
118
+ #
119
+ # May be +nil+ when the parser has accepted the last token and peeked at
120
+ # the next byte without generating a token.
121
+ #
122
+ # _NOTE:_ This attribute is provided for debugging and inspection only.
123
+ # Its name and semantics may change incompatibly in any release.
124
+ attr_reader :token
125
+
126
+ def initialize(message = "unspecified parse error",
127
+ parser_class: Net::IMAP::ResponseParser,
128
+ parser_state: nil,
129
+ string: parser_state&.at(0), # see ParserUtils#parser_state
130
+ lex_state: parser_state&.at(1), # see ParserUtils#parser_state
131
+ pos: parser_state&.at(2), # see ParserUtils#parser_state
132
+ token: parser_state&.at(3)) # see ParserUtils#parser_state
133
+ @parser_class = parser_class
134
+ @string = string
135
+ @pos = pos
136
+ @lex_state = lex_state
137
+ @token = token
138
+ super(message)
139
+ end
140
+
141
+ # When +parser_state+ is true, debug info about the parser state is
142
+ # included. Defaults to the value of Net::IMAP.debug.
143
+ #
144
+ # When +parser_backtrace+ is true, a simplified backtrace is included,
145
+ # containing only frames for methods in parser_class (since ruby 3.4) or
146
+ # which have "net/imap/response_parser" in the path (before ruby 3.4).
147
+ # Most parser method names are based on rules in the IMAP grammar.
148
+ #
149
+ # When +highlight+ is not explicitly set, highlights may be enabled
150
+ # automatically, based on +TERM+ and +FORCE_COLOR+ environment variables.
151
+ #
152
+ # By default, +highlight+ uses colors from the basic ANSI palette. When
153
+ # +highlight_no_color+ is true or the +NO_COLOR+ environment variable is
154
+ # not empty, only monochromatic highlights are used: bold, underline, etc.
155
+ def detailed_message(parser_state: Net::IMAP.debug,
156
+ parser_backtrace: false,
157
+ highlight: default_highlight_from_env,
158
+ highlight_no_color: (ENV["NO_COLOR"] || "") != "",
159
+ **)
160
+ return super unless parser_state || parser_backtrace
161
+ msg = super.dup
162
+ esc = !highlight ? ESC_NO_HL : highlight_no_color ? ESC_NO_COLOR : ESC_COLORS
163
+ hl = ->str { str % esc }
164
+ val = ->str, val { hl[val.nil? ? "%{nil}%%p%{/nil}" : str] % val }
165
+ if parser_state && (string || pos || lex_state || token)
166
+ msg << hl["\n %{key}processed %{/key}: "] << val["%{val}%%p%{/val}", processed_string]
167
+ msg << hl["\n %{key}remaining %{/key}: "] << val["%{alt}%%p%{/alt}", remaining_string]
168
+ msg << hl["\n %{key}pos %{/key}: "] << val["%{val}%%p%{/val}", pos]
169
+ msg << hl["\n %{key}lex_state %{/key}: "] << val["%{sym}%%p%{/sym}", lex_state]
170
+ msg << hl["\n %{key}token %{/key}: "] << val[
171
+ "%{sym}%%<symbol>p%{/sym} => %{val}%%<value>p%{/val}", token&.to_h
172
+ ]
173
+ end
174
+ if parser_backtrace
175
+ normalized_parser_backtrace.each do |idx, path, lineno, label, base_label|
176
+ msg << "\n %s: %s (%s:%d)" % [
177
+ hl["%{key}caller[%{/key}%{idx}%%2d%{/idx}%{key}]%{/key}"] % idx,
178
+ hl["%{label}%%-30s%{/label}"] % base_label,
179
+ File.basename(path, ".rb"), lineno
180
+ ]
181
+ end
182
+ end
183
+ msg
184
+ rescue => error
185
+ msg ||= super.dup
186
+ msg << "\n BUG in %s#%s: %s" % [self.class, __method__,
187
+ error.detailed_message]
188
+ msg
189
+ end
190
+
191
+ def processed_string = string && pos && string[...pos]
192
+ def remaining_string = string && pos && string[pos..]
193
+
194
+ # Returns true when all attributes are equal, except for #backtrace and
195
+ # #backtrace_locations which are replaced with #parser_methods. This
196
+ # allows deserialized errors to be compared.
197
+ def ==(other)
198
+ return false if self.class != other.class
199
+ methods = parser_methods
200
+ other_methods = other.parser_methods
201
+ message == other.message &&
202
+ methods == other_methods &&
203
+ string == other.string &&
204
+ pos == other.pos &&
205
+ lex_state == other.lex_state &&
206
+ token == other.token
207
+ end
208
+
209
+ # Lists the methods (from #backtrace_locations or #backtrace) called on
210
+ # parser_class (since ruby 3.4) or which have "net/imap/response_parser"
211
+ # in the path (before ruby 3.4). Most parser method names are based on
212
+ # rules in the IMAP grammar.
213
+ def parser_methods = normalized_parser_backtrace.map(&:last)
214
+
215
+ private
216
+
217
+ def normalized_parser_backtrace
218
+ normalize_backtrace
219
+ .take_while {|_, _, _, _, base_label| base_label != "parse" }
220
+ .reject {|_, _, _, _, base_label| base_label.nil? }
221
+ .reject {|_, _, _, _, base_label| base_label.include? "parse_error" }
222
+ .select {|_, path, _, label, _|
223
+ if label.include?("#") # => Class#method, since ruby 3.4
224
+ label.include?(parser_class.name)
225
+ else
226
+ path.include?("net/imap/response_parser")
227
+ end
228
+ }
229
+ end
230
+
231
+ def normalize_backtrace
232
+ (backtrace_locations&.each_with_index&.map {|loc, idx|
233
+ [idx, loc.path, loc.lineno, loc.label, loc.base_label]
234
+ } || backtrace&.each_with_index&.map {|bt, idx|
235
+ [idx, *bt.match(/\A(\S+):(\d+):in [`'](.*?([\w]+[?!]?))'\z/)&.captures]
236
+ } || [])
237
+ end
238
+
239
+ def default_highlight_from_env
240
+ (ENV["FORCE_COLOR"] || "") !~ /\A(?:0|)\z/ ||
241
+ (ENV["TERM"] || "") !~ /\A(?:dumb|unknown|)\z/i
242
+ end
243
+
244
+ end
245
+
246
+ # Superclass of all errors used to encapsulate "fail" responses
247
+ # from the server.
248
+ class ResponseError < Error
249
+
250
+ # The response that caused this error
251
+ attr_accessor :response
252
+
253
+ def initialize(response)
254
+ @response = response
255
+
256
+ super @response.data.text
257
+ end
258
+
259
+ end
260
+
261
+ # Error raised upon a "NO" response from the server, indicating
262
+ # that the client command could not be completed successfully.
263
+ class NoResponseError < ResponseError
264
+ end
265
+
266
+ # Error raised upon a "BAD" response from the server, indicating
267
+ # that the client command violated the IMAP protocol, or an internal
268
+ # server failure has occurred.
269
+ class BadResponseError < ResponseError
270
+ end
271
+
272
+ # Error raised upon a "BYE" response from the server, indicating
273
+ # that the client is not being allowed to login, or has been timed
274
+ # out due to inactivity.
275
+ class ByeResponseError < ResponseError
276
+ end
277
+
278
+ # Error raised when the server sends an invalid response.
279
+ #
280
+ # This is different from UnknownResponseError: the response has been
281
+ # rejected. Although it may be parsable, the server is forbidden from
282
+ # sending it in the current context.
283
+ #
284
+ # This could be caused by a bug in the server or in Net::IMAP. Or it
285
+ # might indicate a malicious server, a man-in-the-middle attack, or
286
+ # client-side command injection. So the client should automatically
287
+ # disconnect, abruptly (without logout).
288
+ #
289
+ # Note that InvalidResponseError does not inherit from ResponseError: it
290
+ # can be raised before the response is fully parsed. A related
291
+ # ResponseParseError or ResponseError may be the #cause.
292
+ class InvalidResponseError < Error
293
+ end
294
+
295
+ # Error raised when the server sends a tagged #response that is invalid for
296
+ # the #command #state.
297
+ #
298
+ # This could be caused by a bug in the server or in Net::IMAP. Or it
299
+ # might indicate a malicious server, a man-in-the-middle attack, or
300
+ # client-side command injection, so the client should disconnect
301
+ # automatically and abruptly (without logout).
302
+ class InvalidTaggedResponseError < InvalidResponseError
303
+ # A symbol representing the state of the matching tagged command.
304
+ #
305
+ # +:unknown+::
306
+ # #response does not match any known command (#command will be +nil+).
307
+ # +:unstarted+::
308
+ # Any tagged #response is invalid before #command starts sending.
309
+ # +:incomplete+::
310
+ # A tagged +OK+ #response is invalid before #command is fully sent.
311
+ # +:completed+::
312
+ # Multiple tagged responses were received for the same command.
313
+ #
314
+ # NOTE: Command state is neither anticipated nor remembered indefinitely.
315
+ # +:unknown+ is used before the matching command is called and after the
316
+ # it is forgotten.
317
+ #
318
+ # NOTE: This version of Net::IMAP does not detect or raise an exception
319
+ # for all of these states.
320
+ attr_reader :state
321
+
322
+ # Metadata about the matching IMAP command.
323
+ #
324
+ # Returns +nil+ when #state is +:unknown+.
325
+ #
326
+ # NOTE: The non-nil return type is an unstable API, for debug only. It
327
+ # may be changed by any release, without warning or deprecation.
328
+ attr_reader :command
329
+
330
+ # The TaggedResponse which triggered this error
331
+ attr_reader :response
332
+
333
+ def initialize(state, response:, command: nil)
334
+ response => TaggedResponse[tag:, name: status]
335
+ case [state, status, command]
336
+ in :unknown, _, nil
337
+ in :incomplete, "OK", {tag: ^tag, name:}
338
+ in :unstarted | :completed, _, {tag: ^tag, name:}
339
+ end
340
+ @state, @command, @response = state, command, response
341
+ cmd_desc = name ? "#{state} #{name}" : state
342
+ super "Received tagged #{status} to #{cmd_desc} command (tag=#{tag})"
343
+ rescue NoMatchingPatternError => err
344
+ raise ArgumentError, err.message
345
+ end
346
+
347
+ def detailed_message(**)
348
+ "#{message}.\n" \
349
+ "Disconnecting: This could indicate a malicious server, a " \
350
+ "man-in-the-middle attack, a client-side command injection, or " \
351
+ "a bug in net-imap.\n" \
352
+ "response.data=#{response.data.inspect}"
353
+ end
354
+ end
355
+
356
+ # Error raised upon an unknown response from the server.
357
+ #
358
+ # This is different from InvalidResponseError: the response may be a
359
+ # valid extension response and the server may be allowed to send it in
360
+ # this context, but Net::IMAP either does not know how to parse it or
361
+ # how to handle it. This could result from enabling unknown or
362
+ # unhandled extensions. The connection may still be usable,
363
+ # but—depending on context—it may be prudent to disconnect.
364
+ class UnknownResponseError < ResponseError
365
+ end
366
+
367
+ RESPONSE_ERRORS = Hash.new(ResponseError) # :nodoc:
368
+ RESPONSE_ERRORS["NO"] = NoResponseError
369
+ RESPONSE_ERRORS["BAD"] = BadResponseError
370
+
371
+ end
372
+ end
@@ -25,6 +25,12 @@ module Net
25
25
  # Some search extensions may result in the server sending ESearchResult
26
26
  # responses after the initiating command has completed. Use
27
27
  # IMAP#add_response_handler to handle these responses.
28
+ #
29
+ # ==== Compatibility with SearchResult
30
+ #
31
+ # Note that both SearchResult and ESearchResult implement +each+, +to_a+,
32
+ # and +to_sequence_set+. These methods can be used regardless of whether
33
+ # the server returns +SEARCH+ or +ESEARCH+ data (or no data).
28
34
  class ESearchResult < Data.define(:tag, :uid, :data)
29
35
  def initialize(tag: nil, uid: nil, data: nil)
30
36
  tag => String | nil; tag = -tag if tag
@@ -6,7 +6,6 @@ module Net
6
6
  autoload :FetchData, "#{__dir__}/fetch_data"
7
7
  autoload :UIDFetchData, "#{__dir__}/fetch_data"
8
8
  autoload :SearchResult, "#{__dir__}/search_result"
9
- autoload :UIDPlusData, "#{__dir__}/uidplus_data"
10
9
  autoload :AppendUIDData, "#{__dir__}/uidplus_data"
11
10
  autoload :CopyUIDData, "#{__dir__}/uidplus_data"
12
11
  autoload :VanishedData, "#{__dir__}/vanished_data"
@@ -76,9 +75,18 @@ module Net
76
75
  #
77
76
  # Net::IMAP::UnparsedData represents data for unknown response types or
78
77
  # unknown extensions to response types without a well-defined extension
79
- # grammar.
78
+ # grammar. UnparsedData represents the portion of the response which the
79
+ # parser has skipped over, without attempting to parse it.
80
80
  #
81
- # See also: UnparsedNumericResponseData, ExtensionData, IgnoredResponse
81
+ # parser = Net::IMAP::ResponseParser.new
82
+ # response = parser.parse "* X-UNKNOWN-TYPE can't parse this\r\n"
83
+ # response => Net::IMAP::UntaggedResponse(
84
+ # name: "X-UNKNOWN-TYPE",
85
+ # data: Net::IMAP::UnparsedData(unparsed_data: "can't parse this"),
86
+ # )
87
+ #
88
+ # See also: UnparsedNumericResponseData, ExtensionData, IgnoredResponse,
89
+ # InvalidParseData.
82
90
  class UnparsedData < Struct.new(:unparsed_data)
83
91
  ##
84
92
  # method: unparsed_data
@@ -87,6 +95,61 @@ module Net
87
95
  # The unparsed data
88
96
  end
89
97
 
98
+ # **Note:** This represents an intentionally _unstable_ API. Where
99
+ # instances of this class are returned, future releases may return a
100
+ # different (incompatible) object <em>without deprecation or warning</em>.
101
+ #
102
+ # When the response parser encounters a recoverable error,
103
+ # Net::IMAP::InvalidParseData represents that portion of the response which
104
+ # could not be parsed, allowing the parser to parse the remainder of the
105
+ # response. InvalidParseData is always associated with a ResponseParseError
106
+ # which has been rescued.
107
+ #
108
+ # This could be caused by a malformed server response, by a bug in
109
+ # Net::IMAP::ResponseParser, or by an unsupported extension to the response
110
+ # syntax. For example, if a server supports +UIDPLUS+, but sends an invalid
111
+ # +COPYUID+ response code:
112
+ #
113
+ # parser = Net::IMAP::ResponseParser.new
114
+ # parsed = parser.parse "* OK [COPYUID 701 ] copied one message\r\n"
115
+ # parsed => {
116
+ # data: Net::IMAP::ResponseText(
117
+ # code: Net::IMAP::ResponseCode(
118
+ # name: "COPYUID",
119
+ # data: Net::IMAP::InvalidParseData(
120
+ # parse_error: Net::IMAP::ResponseParseError,
121
+ # unparsed_data: "701 ",
122
+ # parsed_data: nil,
123
+ # )
124
+ # )
125
+ # )
126
+ # }
127
+ #
128
+ # In this example, although <tt>[COPYUID 701 ]</tt> uses valid syntax for a
129
+ # _generic_ ResponseCode, it is _invalid_ syntax for a +COPYUID+ response
130
+ # code.
131
+ #
132
+ # See also: UnparsedData, ExtensionData
133
+ class InvalidParseData < Data.define(:parse_error, :unparsed_data, :parsed_data)
134
+ ##
135
+ # method: parse_error
136
+ # :call-seq: parse_error -> ResponseParseError
137
+ #
138
+ # Returns the rescued ResponseParseError.
139
+
140
+ ##
141
+ # method: unparsed_data
142
+ # :call-seq: unparsed_data -> string
143
+ #
144
+ # Returns the raw string which was skipped over by the parser.
145
+
146
+ ##
147
+ # method: parsed_data
148
+ #
149
+ # May return a partial parse result for unparsed_data, which had already
150
+ # been parsed before the parse_error.
151
+ end
152
+
90
153
  # **Note:** This represents an intentionally _unstable_ API. Where
91
154
  # instances of this class are returned, future releases may return a
92
155
  # different (incompatible) object <em>without deprecation or warning</em>.
@@ -94,7 +157,17 @@ module Net
94
157
  # Net::IMAP::UnparsedNumericResponseData represents data for unhandled
95
158
  # response types with a numeric prefix. See the documentation for #number.
96
159
  #
97
- # See also: UnparsedData, ExtensionData, IgnoredResponse
160
+ # parser = Net::IMAP::ResponseParser.new
161
+ # response = parser.parse "* 123 X-UNKNOWN-TYPE can't parse this\r\n"
162
+ # response => Net::IMAP::UntaggedResponse(
163
+ # name: "X-UNKNOWN-TYPE",
164
+ # data: Net::IMAP::UnparsedNumericData(
165
+ # number: 123,
166
+ # unparsed_data: "can't parse this"
167
+ # ),
168
+ # )
169
+ #
170
+ # See also: UnparsedData, ExtensionData, IgnoredResponse, InvalidParseData
98
171
  class UnparsedNumericResponseData < Struct.new(:number, :unparsed_data)
99
172
  ##
100
173
  # method: number
@@ -260,8 +333,8 @@ module Net
260
333
  #
261
334
  # === +UIDPLUS+ extension
262
335
  # See {[RFC4315 §3]}[https://www.rfc-editor.org/rfc/rfc4315#section-3].
263
- # * +APPENDUID+, #data is UIDPlusData. See IMAP#append.
264
- # * +COPYUID+, #data is UIDPlusData. See IMAP#copy.
336
+ # * +APPENDUID+, #data is AppendUIDData. See IMAP#append.
337
+ # * +COPYUID+, #data is CopyUIDData. See IMAP#copy.
265
338
  # * +UIDNOTSTICKY+, #data is +nil+. See IMAP#select.
266
339
  #
267
340
  # === +SEARCHRES+ extension
@@ -333,9 +406,10 @@ module Net
333
406
  #
334
407
  # Response codes are backwards compatible: Servers are allowed to send new
335
408
  # response codes even if the client has not enabled the extension that
336
- # defines them. When Net::IMAP does not know how to parse response
337
- # code text, #data returns the unparsed string.
338
- #
409
+ # defines them. When ResponseParser does not know how to parse the response
410
+ # code data, #data may return the unparsed string, ExtensionData, or
411
+ # UnparsedData. When ResponseParser attempts but fails to parse the
412
+ # response code data, #data returns InvalidParseData.
339
413
  class ResponseCode < Struct.new(:name, :data)
340
414
  ##
341
415
  # method: name
@@ -350,8 +424,13 @@ module Net
350
424
  #
351
425
  # Returns the parsed response code data, e.g: an array of capabilities
352
426
  # strings, an array of character set strings, a list of permanent flags,
353
- # an Integer, etc. The response #code determines what form the response
354
- # code data can take.
427
+ # an Integer, etc. The response #name determines what form the response
428
+ # code #data can take.
429
+ #
430
+ # When ResponseParser does not know how to parse the response code data,
431
+ # #data may return the unparsed string, ExtensionData, or UnparsedData.
432
+ # When ResponseParser attempts but fails to parse the response code data,
433
+ # #data returns InvalidParseData.
355
434
  end
356
435
 
357
436
  # MailboxList represents the data of an untagged +LIST+ response, for a
@@ -215,29 +215,20 @@ module Net
215
215
  @token = nil
216
216
  end
217
217
 
218
- def parse_error(fmt, *args)
219
- msg = format(fmt, *args)
220
- if config.debug?
221
- local_path = File.dirname(__dir__)
222
- tok = @token ? "%s: %p" % [@token.symbol, @token.value] : "nil"
223
- warn "%s %s: %s" % [self.class, __method__, msg]
224
- warn " tokenized : %s" % [@str[...@pos].dump]
225
- warn " remaining : %s" % [@str[@pos..].dump]
226
- warn " @lex_state: %s" % [@lex_state]
227
- warn " @pos : %d" % [@pos]
228
- warn " @token : %s" % [tok]
229
- caller_locations(1..20).each_with_index do |cloc, idx|
230
- next unless cloc.path&.start_with?(local_path)
231
- warn " caller[%2d]: %-30s (%s:%d)" % [
232
- idx,
233
- cloc.base_label,
234
- File.basename(cloc.path, ".rb"),
235
- cloc.lineno
236
- ]
237
- end
238
- end
239
- raise ResponseParseError, msg
240
- end
218
+ def parse_error(fmt, *args) = raise exception format(fmt, *args)
219
+
220
+ def exception(message) = ResponseParseError.new(
221
+ message, parser_state:, parser_class: self.class
222
+ )
223
+
224
+ # This can be used to backtrack after a parse error, and re-attempt to
225
+ # parse using a fallback.
226
+ #
227
+ # NOTE: Reckless backtracking could lead to O(n²) situations, so this
228
+ # should very rarely be used. Ideally, fallbacks should not backtrack.
229
+ def restore_state(state) = (@lex_state, @pos, @token = state)
230
+ def current_state = [@lex_state, @pos, @token]
231
+ def parser_state = [@str, *current_state]
241
232
 
242
233
  end
243
234
  end