epuber 0.9.4 → 0.10.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdced1dd2ebd0dc9d67e06757560ed5acd26d93303db343b53985b3c7aeebe20
4
- data.tar.gz: e979d0957ff1a554bc4f29cb4e2bbe6cd0a113a83a42ed46a6e80c8f049e8008
3
+ metadata.gz: 6190500e356c8d6fd0d642f6e39f2f606948933bee1b17f1285add446e5bdd6d
4
+ data.tar.gz: 63719103a8cce152f0cbb63ba72ab086064e0f5cbaf03c926fad293342be1c42
5
5
  SHA512:
6
- metadata.gz: b2ea5b2448b893e8c1c6cb47d4868cdfcd8e16aefeb54733c880c686107439ba51ec50aa89f0f571b6322998d5fe1d4862d4b8a4e78df003fbe4d1d849ebcbc9
7
- data.tar.gz: 6492a8277ff6d0179a5fd91e3758cc0edc5ff73faf584237336395a52d151654a7c451085ddde6e40532010b3e6d5f9046a3f058237c7879b6da184141b63877
6
+ metadata.gz: dd5056baa29e1b15c5ecad2e570969a21247dec9df18d1f0e18575e4a801c0427b66b20a20a6af44a17889e5b455cc17fb55e042c203e6f154046e648630fa6b
7
+ data.tar.gz: 07a96a039ff2a06774725ca1187bc7e8c09a938513893c52b8f2d7bf6d467f7febd45529cd7beabe1ed746c7dfc4118a7936677b890681c7840edbfe4c31e8dd
@@ -61,8 +61,7 @@ module Epuber
61
61
  matches = text.to_enum(:scan, regexp).map { Regexp.last_match }
62
62
  matches.each do |match|
63
63
  # @type match [MatchData]
64
- UI.print_processing_problem MatchProblem.new(match, message,
65
- Config.instance.pretty_path_from_project(file_path))
64
+ UI.warning MatchProblem.new(match, message, Config.instance.pretty_path_from_project(file_path))
66
65
  end
67
66
  end
68
67
 
@@ -17,16 +17,12 @@ module Epuber
17
17
  }.merge(super)
18
18
  end
19
19
 
20
- def warning(messsage, location: nil)
20
+ def warning(messsage, location: caller_locations.first)
21
21
  UI.warning(messsage, location: location)
22
22
  end
23
23
 
24
- def error(messsage, location: nil)
25
- if Config.instance.release_build
26
- UI.error!(messsage, location: location)
27
- else
28
- UI.error(messsage, location: location)
29
- end
24
+ def error(messsage, location: caller_locations.first)
25
+ UI.error(messsage, location: location)
30
26
  end
31
27
  end
32
28
  end
@@ -40,7 +40,7 @@ module Epuber
40
40
  @release_version = argv.flag?('release', false)
41
41
  @use_cache = argv.flag?('cache', true)
42
42
 
43
- self.debug_steps_times = argv.flag?('debug-steps-times', false)
43
+ UI.logger.debug_steps_times = argv.flag?('debug-steps-times', false)
44
44
 
45
45
  super(argv)
46
46
  end
@@ -56,7 +56,7 @@ module Epuber
56
56
  def run
57
57
  super
58
58
 
59
- UI.puts "building book `#{Config.instance.pretty_path_from_project(book.file_path)}`"
59
+ UI.info "building book `#{Config.instance.pretty_path_from_project(book.file_path)}`"
60
60
 
61
61
  if @release_version
62
62
  # Remove all previous versions of compiled files
@@ -77,9 +77,7 @@ module Epuber
77
77
  FileUtils.remove_file(archive_name) if ::File.exist?(archive_name)
78
78
 
79
79
  archive_path = compiler.archive(archive_name)
80
-
81
- Epubcheck.check(archive_path)
82
-
80
+ run_epubcheck(archive_path, build_path)
83
81
  convert_epub_to_mobi(archive_path, "#{::File.basename(archive_path, '.epub')}.mobi") if target.create_mobi
84
82
 
85
83
  Epuber::Config.instance.release_build = false
@@ -94,13 +92,19 @@ module Epuber
94
92
  use_cache: @use_cache)
95
93
  archive_path = compiler.archive(configuration_suffix: 'debug')
96
94
 
97
- Epubcheck.check(archive_path) if @should_check
95
+ run_epubcheck(archive_path, build_path) if @should_check
98
96
 
99
97
  convert_epub_to_mobi(archive_path, "#{::File.basename(archive_path, '.epub')}.mobi") if target.create_mobi
100
98
  end
101
99
  end
102
100
 
103
- write_lockfile
101
+ # Exit with error if there are any errors
102
+ if (@release_version || @should_check) && Epuber::UI.logger.error?
103
+ exit(1)
104
+ else
105
+ UI.info('🎉 Build finished successfully.'.ansi.green)
106
+ write_lockfile
107
+ end
104
108
  end
105
109
 
106
110
  private
@@ -131,6 +135,38 @@ module Epuber
131
135
  path
132
136
  end
133
137
 
138
+ def run_epubcheck(archive_path, build_dir)
139
+ UI.info("Running Epubcheck for #{archive_path}")
140
+
141
+ report = Epubcheck.check(archive_path)
142
+ report.problems.each do |problem|
143
+ relative_path = problem.location.path.sub("#{archive_path}/", '')
144
+ file_path = ::File.join(build_dir, relative_path)
145
+
146
+ nice_path = Config.instance.pretty_path_from_project(file_path)
147
+ content = ::File.read(file_path)
148
+
149
+ log_level = case problem.level
150
+ when :fatal, :error then :error
151
+ when :warning then :warning
152
+ else :info
153
+ end
154
+ message = "#{problem.level}(#{problem.code}): #{problem.message}"
155
+
156
+ p = Compiler::Problem.new(problem.level, message, content, line: problem.location.lineno,
157
+ column: problem.location.column,
158
+ length: 1,
159
+ file_path: nice_path)
160
+ UI.send(log_level, p, backtrace: nil)
161
+ end
162
+
163
+ if report.error?
164
+ UI.error('Epubcheck found some errors in epub file.')
165
+ else
166
+ UI.info('Epubcheck finished successfully.')
167
+ end
168
+ end
169
+
134
170
  def find_calibre_app
135
171
  locations = `mdfind "kMDItemCFBundleIdentifier == net.kovidgoyal.calibre"`.split("\n")
136
172
  UI.error!("Can't find location of calibre.app to convert EPUB to MOBI.") if locations.empty?
@@ -52,7 +52,7 @@ module Epuber
52
52
  private
53
53
 
54
54
  def print_good_bye(book_id)
55
- UI.puts <<~TEXT.ansi.green
55
+ UI.info <<~TEXT.ansi.green
56
56
  Project initialized, please review #{book_id}.bookspec file, remove comments and fill some attributes like book title.
57
57
  TEXT
58
58
  end
@@ -124,7 +124,7 @@ module Epuber
124
124
  #
125
125
  def write(file_path, string)
126
126
  File.write(file_path, string)
127
- UI.puts " #{'create'.ansi.green} #{file_path}"
127
+ UI.info " #{'create'.ansi.green} #{file_path}"
128
128
  end
129
129
 
130
130
  # @param [String] string text to file
@@ -148,7 +148,7 @@ module Epuber
148
148
  existing_content << "\n"
149
149
 
150
150
  File.write(file_path, existing_content)
151
- UI.puts " #{'update'.ansi.green} #{file_path}"
151
+ UI.info " #{'update'.ansi.green} #{file_path}"
152
152
  end
153
153
 
154
154
  # @param [String] dir_path path to dir
@@ -157,7 +157,7 @@ module Epuber
157
157
  #
158
158
  def create_folder(dir_path)
159
159
  FileUtils.mkdir_p(dir_path)
160
- UI.puts " #{'create'.ansi.green} #{dir_path}/"
160
+ UI.info " #{'create'.ansi.green} #{dir_path}/"
161
161
  end
162
162
 
163
163
  # @param [String] text
@@ -169,7 +169,7 @@ module Epuber
169
169
  result = $stdin.gets.chomp
170
170
 
171
171
  while result.empty?
172
- UI.puts 'Value cannot be empty, please fill it!'.ansi.red
172
+ UI.info 'Value cannot be empty, please fill it!'.ansi.red
173
173
  print text
174
174
  result = $stdin.gets.chomp
175
175
  end
@@ -51,7 +51,7 @@ module Epuber
51
51
  if @open_web_browser
52
52
  system "open #{uri}"
53
53
  else
54
- UI.puts 'Web browser can be automatically opened by adding --open flag, see --help'
54
+ UI.info 'Web browser can be automatically opened by adding --open flag, see --help'
55
55
  end
56
56
  end
57
57
  end
@@ -25,32 +25,23 @@ module Epuber
25
25
  self.plugin_prefixes = plugin_prefixes + %w[epuber]
26
26
 
27
27
  def self.run(argv = [])
28
- UI.current_command = self
29
28
  super
30
- UI.current_command = nil
31
29
  rescue Interrupt
32
30
  UI.error('[!] Cancelled')
33
31
  rescue StandardError => e
34
32
  UI.error!(e)
35
-
36
- UI.current_command = nil
37
33
  end
38
34
 
39
- def validate!
35
+ def initialize(argv)
40
36
  super
41
- UI.current_command = self
42
- end
43
37
 
44
- def run
45
- UI.current_command = self
38
+ UI.logger.verbose = verbose?
46
39
  end
47
40
 
48
- attr_reader :debug_steps_times
41
+ def run; end
49
42
 
50
43
  protected
51
44
 
52
- attr_writer :debug_steps_times
53
-
54
45
  # @return [Epuber::Book::Book]
55
46
  #
56
47
  def book
@@ -62,9 +53,16 @@ module Epuber
62
53
  # @raise PlainInformative if no .bookspec file don't exists or there are too many
63
54
  #
64
55
  def verify_one_bookspec_exists!
65
- bookspec_files = Config.instance.find_all_bookspecs
66
- raise PlainInformative, "No `.bookspec' found in the project directory." if bookspec_files.empty?
67
- raise PlainInformative, "Multiple `.bookspec' found in current directory" if bookspec_files.count > 1
56
+ project_path = Config.instance.project_path
57
+ bookspec_files = Config.find_bookspec_files(project_path)
58
+
59
+ if bookspec_files.empty?
60
+ raise PlainInformative, "No `.bookspec' found in the project directory (or in any parent folders)."
61
+ end
62
+
63
+ if bookspec_files.count > 1
64
+ raise PlainInformative, "Multiple `.bookspec' found in directory (directory: #{project_path})"
65
+ end
68
66
  end
69
67
 
70
68
  def write_lockfile
@@ -76,10 +74,17 @@ module Epuber
76
74
  def pre_build_checks
77
75
  Config.instance.warn_for_outdated_versions!
78
76
 
79
- return unless !Config.instance.same_version_as_last_run? && File.exist?(Config.instance.working_path)
80
-
81
- UI.warning('Using different version of Epuber or Bade, removing all build caches')
82
- Config.instance.remove_build_caches
77
+ # remove build caches if we are using different version of Epuber or Bade
78
+ if !Config.instance.same_version_as_last_run? && File.exist?(Config.instance.working_path)
79
+ UI.warning('Using different version of Epuber or Bade, removing all build caches')
80
+ Config.instance.remove_build_caches
81
+ end
82
+
83
+ # ensure we are in the project directory
84
+ if Dir.pwd != Config.instance.project_path
85
+ UI.debug("Changing directory to project directory: #{Config.instance.project_path}")
86
+ Dir.chdir(Config.instance.project_path)
87
+ end
83
88
  end
84
89
  end
85
90
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Epuber
5
4
  class Compiler
6
5
  class CompilationContext
@@ -37,13 +36,14 @@ module Epuber
37
36
  file_resolver.add_file(file)
38
37
  end
39
38
  plugin
40
- rescue LoadError
41
- UI.error "Can't find plugin at path #{path}"
39
+ rescue LoadError => e
40
+ UI.error "Can't find plugin at path #{path}, #{e}"
42
41
  end.compact
43
42
  end
44
43
 
45
44
  # @param [Class] klass class of thing you want to perform (Checker or Transformer)
46
45
  # @param [Symbol] source_type source type of that thing (Checker or Transformer)
46
+ # @param [String] processing_time_step_name name of step for processing time
47
47
  #
48
48
  # @yield
49
49
  # @yieldparam [Epuber::CheckerTransformerBase] instance of checker or transformer
@@ -58,7 +58,11 @@ module Epuber
58
58
  next if instance.source_type != source_type
59
59
  next if instance.options.include?(:run_only_before_release) && !release_build
60
60
 
61
- yield instance
61
+ location = instance.block.source_location.map(&:to_s).join(':')
62
+ message = "performing #{source_type.inspect} from plugin #{location}"
63
+ UI.print_step_processing_time(message) do
64
+ yield instance
65
+ end
62
66
  end
63
67
  end
64
68
  end
@@ -27,7 +27,7 @@ module Epuber
27
27
 
28
28
  PATH_TYPES = [:spine, :manifest, :package, nil].freeze
29
29
 
30
- # @return [String] path where should look for source files
30
+ # @return [String] path where should look for source files (relative to project root)
31
31
  #
32
32
  attr_reader :source_path
33
33
 
@@ -6,7 +6,7 @@ module Epuber
6
6
  require_relative 'abstract_file'
7
7
 
8
8
  class SourceFile < AbstractFile
9
- # @return [String] relative source path
9
+ # @return [String] relative source path (from project root)
10
10
  #
11
11
  attr_reader :source_path
12
12
 
@@ -133,7 +133,7 @@ module Epuber
133
133
  rescue XHTMLProcessor::UnparseableLinkError,
134
134
  FileFinders::FileNotFoundError,
135
135
  FileFinders::MultipleFilesFoundError => e
136
- UI.warning(e.to_s, location: location)
136
+ UI.error(e.to_s, location: location)
137
137
  return nil
138
138
  end
139
139
  end
@@ -142,18 +142,14 @@ module Epuber
142
142
  end
143
143
 
144
144
  # perform transformations
145
- UI.print_step_processing_time('performing final transformations') do
146
- compilation_context.perform_plugin_things(Transformer, :result_text_xhtml_string) do |transformer|
147
- xhtml_string = transformer.call(final_destination_path, xhtml_string, compilation_context)
148
- end
145
+ compilation_context.perform_plugin_things(Transformer, :result_text_xhtml_string) do |transformer|
146
+ xhtml_string = transformer.call(final_destination_path, xhtml_string, compilation_context)
149
147
  end
150
148
 
151
149
  # perform custom validation
152
150
  if compilation_context.should_check
153
- UI.print_step_processing_time('performing final validations') do
154
- compilation_context.perform_plugin_things(Checker, :result_text_xhtml_string) do |checker|
155
- checker.call(final_destination_path, xhtml_string, compilation_context)
156
- end
151
+ compilation_context.perform_plugin_things(Checker, :result_text_xhtml_string) do |checker|
152
+ checker.call(final_destination_path, xhtml_string, compilation_context)
157
153
  end
158
154
  end
159
155
 
@@ -175,7 +171,7 @@ module Epuber
175
171
  # @param [Compiler::CompilationContext] compilation_context
176
172
  # @param [Hash<String, XHTMLFile>] global_ids
177
173
  #
178
- def process_global_ids(compilation_context, global_ids)
174
+ def process_global_ids(_compilation_context, global_ids)
179
175
  return if self.global_ids.empty? && global_links.empty?
180
176
 
181
177
  xhtml_doc = XHTMLProcessor.xml_document_from_string(File.read(final_destination_path), final_destination_path)
@@ -196,12 +192,8 @@ module Epuber
196
192
  node['href'] = "#{rel_path}##{href}"
197
193
  else
198
194
  message = "Can't find global id '#{href}' from link in file #{source_path}"
199
- location = UserInterface::Location.new(path: final_destination_path, lineno: node.line)
200
- if compilation_context.release_build?
201
- UI.error!(message, location: location)
202
- else
203
- UI.warning(message, location: location)
204
- end
195
+ location = Epuber::Location.new(path: final_destination_path, lineno: node.line)
196
+ UI.error(message, location: location)
205
197
  end
206
198
  end
207
199
 
@@ -25,7 +25,7 @@ module Epuber
25
25
 
26
26
  if /\A[\n\r ]+(<\?xml)/ =~ text
27
27
  UI.warning('XML header must be at the beginning of document',
28
- location: UI::Location.new(path: file_path, lineno: 1))
28
+ location: Epuber::Location.new(path: file_path, lineno: 1))
29
29
 
30
30
  text = text.lstrip
31
31
  end
@@ -198,7 +198,7 @@ module Epuber
198
198
  # @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
199
199
  # for file from tag <img>
200
200
  # @param [String] file_path path to file from which is searching for other file
201
- # @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
201
+ # @param [Epuber::Compiler::FileFinders::Abstract] file_finder finder for searching for files
202
202
  #
203
203
  # @raise UnparseableLinkError, FileFinder::FileNotFoundError, FileFinder::MultipleFilesFoundError
204
204
  #
@@ -71,7 +71,7 @@ module Epuber
71
71
 
72
72
  FileUtils.mkdir_p(build_folder)
73
73
 
74
- UI.puts " #{<<~MSG}"
74
+ UI.info " #{<<~MSG}"
75
75
  building target #{@target.name.inspect} (build dir: #{Config.instance.pretty_path_from_project(build_folder)})
76
76
  MSG
77
77
 
@@ -89,6 +89,7 @@ module Epuber
89
89
  process_all_target_files
90
90
  generate_other_files
91
91
 
92
+ # run :after_all_text_files transformers
92
93
  compilation_context.perform_plugin_things(Transformer, :after_all_text_files) do |transformer|
93
94
  transformer.call(@book, compilation_context)
94
95
  end
@@ -131,7 +132,7 @@ module Epuber
131
132
  old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
132
133
  diff = old_paths - new_paths
133
134
  diff.each do |file_to_remove|
134
- UI.puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
135
+ UI.debug "removing file from result EPUB: #{file_to_remove}"
135
136
  zip_file.remove(file_to_remove)
136
137
  end
137
138
  end
@@ -174,7 +175,7 @@ module Epuber
174
175
  .select { |d| File.directory?(d) }
175
176
  .select { |d| (Dir.entries(d) - %w[. ..]).empty? }
176
177
  .each do |d|
177
- UI.puts "DEBUG: removing empty folder `#{d}`" if compilation_context.verbose?
178
+ UI.debug "removing empty folder `#{d}`"
178
179
  Dir.rmdir(d)
179
180
  end
180
181
  end
@@ -188,7 +189,7 @@ module Epuber
188
189
  end
189
190
  unnecessary_paths.each do |path|
190
191
  if compilation_context.verbose?
191
- UI.puts "DEBUG: removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
192
+ UI.debug "removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
192
193
  end
193
194
 
194
195
  File.delete(path)
@@ -283,11 +284,11 @@ module Epuber
283
284
  #
284
285
  def process_all_target_files
285
286
  @file_resolver.manifest_files.each_with_index do |file, idx|
286
- UI.print_processing_file(file, idx, @file_resolver.manifest_files.count)
287
+ UI.start_processing_file(file, idx, @file_resolver.manifest_files.count)
287
288
  process_file(file)
288
289
  end
289
290
 
290
- UI.processing_files_done
291
+ UI.end_processing
291
292
  end
292
293
 
293
294
  # @param [Epuber::Book::TocItem] toc_item
@@ -305,11 +306,13 @@ module Epuber
305
306
  end
306
307
 
307
308
  def process_global_ids
308
- xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
309
- global_ids = validate_global_ids(xhtml_files)
309
+ UI.print_step_processing_time('Processing global ids') do
310
+ xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
311
+ global_ids = validate_global_ids(xhtml_files)
310
312
 
311
- xhtml_files.each do |file|
312
- file.process_global_ids(compilation_context, global_ids)
313
+ xhtml_files.each do |file|
314
+ file.process_global_ids(compilation_context, global_ids)
315
+ end
313
316
  end
314
317
  end
315
318
 
data/lib/epuber/config.rb CHANGED
@@ -8,10 +8,13 @@ module Epuber
8
8
  class Config
9
9
  WORKING_PATH = '.epuber'
10
10
 
11
- # @return [String]
11
+ # @return [String] path to project directory (where .bookspec file is located or current directory if not found)
12
12
  #
13
13
  def project_path
14
- @project_path ||= Dir.pwd.unicode_normalize
14
+ @project_path ||= begin
15
+ path = self.class.find_project_dir(Dir.pwd) || Dir.pwd
16
+ path.unicode_normalize
17
+ end
15
18
  end
16
19
 
17
20
  # @param [String] of_file absolute path to file
@@ -19,7 +22,9 @@ module Epuber
19
22
  # @return [String] relative path to file from root of project
20
23
  #
21
24
  def pretty_path_from_project(of_file)
22
- Pathname.new(of_file.unicode_normalize).relative_path_from(Pathname.new(project_path)).to_s
25
+ Pathname.new(of_file.unicode_normalize)
26
+ .relative_path_from(Pathname.new(project_path))
27
+ .to_s
23
28
  end
24
29
 
25
30
  # @return [String]
@@ -31,7 +36,7 @@ module Epuber
31
36
  # @return [String]
32
37
  #
33
38
  def bookspec_path
34
- @bookspec_path ||= find_all_bookspecs.first
39
+ @bookspec_path ||= self.class.find_bookspec_files(project_path).first
35
40
  end
36
41
 
37
42
  # @return [String]
@@ -40,16 +45,6 @@ module Epuber
40
45
  "#{bookspec_path}.lock"
41
46
  end
42
47
 
43
- # @return [Array<String>]
44
- #
45
- def find_all_bookspecs
46
- Dir.chdir(project_path) do
47
- Dir.glob('*.bookspec').map do |path|
48
- File.expand_path(path)
49
- end
50
- end
51
- end
52
-
53
48
  # @return [Epuber::Book]
54
49
  #
55
50
  def bookspec
@@ -182,6 +177,32 @@ module Epuber
182
177
 
183
178
  book
184
179
  end
180
+
181
+ # Find all bookspec files in given directory
182
+ #
183
+ # @param [String] dir
184
+ #
185
+ def find_bookspec_files(dir)
186
+ Dir.chdir(dir) do
187
+ Dir.glob('*.bookspec').map do |path|
188
+ File.expand_path(path)
189
+ end
190
+ end
191
+ end
192
+
193
+ # Find project directory by searching for .bookspec files in current and parent directories
194
+ #
195
+ # @param [String] dir
196
+ # @return [String, nil]
197
+ #
198
+ def find_project_dir(dir)
199
+ return dir if find_bookspec_files(dir).any?
200
+
201
+ parent = File.dirname(dir)
202
+ return nil if parent == dir
203
+
204
+ find_project_dir(parent)
205
+ end
185
206
  end
186
207
 
187
208
  self.test = false
@@ -1,14 +1,94 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+ require 'json'
5
+
6
+ require_relative 'utils/location'
7
+
3
8
  module Epuber
4
9
  class Epubcheck
10
+ Report = Struct.new(:problems, keyword_init: true) do
11
+ # !attribute [r] problems
12
+ # @return [Array<Problem>] problems found by epubcheck
13
+
14
+ def error?
15
+ problems.any?(&:error?)
16
+ end
17
+ end
18
+
19
+ Problem = Struct.new(:level, :code, :location, :message, keyword_init: true) do
20
+ # !attribute [r] level
21
+ # @return [Symbol] level of the problem (:fatal, :error, :warning, :info, :usage, :suppressed)
22
+
23
+ # !attribute [r] code
24
+ # @return [String] code of the problem (for example: RSC-005)
25
+
26
+ # !attribute [r] location
27
+ # @return [Epuber::Location, nil] location of the problem
28
+
29
+ # !attribute [r] message
30
+ # @return [String] message of the problem
31
+
32
+ def to_s
33
+ "#{level}(#{code}): #{location.path}(#{location.lineno},#{location.column}): #{message}"
34
+ end
35
+
36
+ def error?
37
+ level == :error || level == :fatal
38
+ end
39
+
40
+ def self.from_json(json)
41
+ json_location = json['locations'].first
42
+
43
+ location = if json_location
44
+ Epuber::Location.new(
45
+ path: json_location['path'],
46
+ lineno: json_location['line'],
47
+ column: json_location['column'],
48
+ )
49
+ end
50
+
51
+ new(
52
+ level: json['severity'].downcase.to_sym,
53
+ code: json['ID'],
54
+ message: json['message'],
55
+ location: location,
56
+ )
57
+ end
58
+ end
59
+
5
60
  class << self
6
61
  # @param [String] path path to file
7
62
  #
63
+ # @return [Report] report of the epubcheck
64
+ #
8
65
  def check(path)
9
- res = system('epubcheck', path)
66
+ report = nil
67
+
68
+ Dir.mktmpdir('epubcheck-') do |tmpdir|
69
+ json_path = File.join(tmpdir, 'epubcheck.json')
70
+ Open3.popen3('epubcheck', path, '--json', json_path) do |_stdin, _stdout, stderr, wait_thr|
71
+ exit_status = wait_thr.value
72
+
73
+ if exit_status.success?
74
+ report = _parse_json(File.read(json_path))
75
+ else
76
+ UI.error(stderr.gets.chomp)
77
+ end
78
+ end
79
+ end
80
+
81
+ report
82
+ end
83
+
84
+ # @param [String] string json string
85
+ # @return [Report]
86
+ #
87
+ def _parse_json(string)
88
+ json = JSON.parse(string)
89
+ messages = json['messages']
10
90
 
11
- UI.error!('Epubcheck failed') unless res
91
+ Report.new(problems: messages.map { |msg| Problem.from_json(msg) })
12
92
  end
13
93
  end
14
94
  end