pandocomatic 1.1.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/pandocomatic/cli.rb +57 -5
  3. data/lib/pandocomatic/command/command.rb +3 -10
  4. data/lib/pandocomatic/command/convert_dir_command.rb +4 -1
  5. data/lib/pandocomatic/command/convert_file_command.rb +28 -12
  6. data/lib/pandocomatic/command/convert_file_multiple_command.rb +6 -4
  7. data/lib/pandocomatic/command/convert_list_command.rb +4 -2
  8. data/lib/pandocomatic/command/copy_file_command.rb +4 -1
  9. data/lib/pandocomatic/command/create_link_command.rb +4 -1
  10. data/lib/pandocomatic/configuration.rb +124 -12
  11. data/lib/pandocomatic/default_configuration.yaml +1 -0
  12. data/lib/pandocomatic/error/pandocomatic_error.rb +1 -1
  13. data/lib/pandocomatic/input.rb +1 -3
  14. data/lib/pandocomatic/pandoc_metadata.rb +18 -9
  15. data/lib/pandocomatic/pandocomatic.rb +109 -5
  16. data/lib/pandocomatic/pandocomatic_yaml.rb +1 -1
  17. data/lib/pandocomatic/printer/command_printer.rb +1 -1
  18. data/lib/pandocomatic/printer/configuration_errors_printer.rb +8 -2
  19. data/lib/pandocomatic/printer/error_printer.rb +3 -2
  20. data/lib/pandocomatic/printer/finish_printer.rb +1 -1
  21. data/lib/pandocomatic/printer/help_printer.rb +1 -1
  22. data/lib/pandocomatic/printer/printer.rb +1 -0
  23. data/lib/pandocomatic/printer/summary_printer.rb +1 -1
  24. data/lib/pandocomatic/printer/unknown_error_printer.rb +39 -0
  25. data/lib/pandocomatic/printer/version_printer.rb +1 -1
  26. data/lib/pandocomatic/printer/views/cli_error.txt +3 -0
  27. data/lib/pandocomatic/printer/views/help.txt +26 -9
  28. data/lib/pandocomatic/printer/views/pandoc_metadata_error.txt +1 -1
  29. data/lib/pandocomatic/printer/views/unknown_error.txt +2 -0
  30. data/lib/pandocomatic/printer/warning_printer.rb +3 -2
  31. data/lib/pandocomatic/processors/fileinfo_preprocessor.rb +17 -12
  32. data/lib/pandocomatic/processors/metadata_preprocessor.rb +7 -5
  33. data/lib/pandocomatic/template.rb +1 -1
  34. data/lib/pandocomatic/version.rb +2 -2
  35. metadata +50 -20
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 129662fa9a34fe3972fdd52341be5c45cc9c2edd3dbfbc95dca7957986fc6c1b
4
- data.tar.gz: c9353f78f78a8eca9a93939a7d6ec5aaf735ac021088c5f571d65be0ba6082c6
3
+ metadata.gz: 6e382c7e0385533687f62deba0a3a1e468b5a1e0c5157b4df5ece4e8c55ac514
4
+ data.tar.gz: c1d24a4b5f28cf0c902a99fc0f68f598e08fdbb15f06bf4ea9981f6b57829a05
5
5
  SHA512:
6
- metadata.gz: 415038006d314eb3c6a9071814e09f1fedeec6ab85453f9734b08e7f61768d2905753d83b58011f374716522d1a093c08cb225986966be40528855b8c99609db
7
- data.tar.gz: eec87fb9f7379a77726141ec7671b06a8adf0493f5089d14e205bdf9243ca089faf0d5031f5903cb9f74a1e49ab7aaffbbb11936eeee5e653883f90dc1c717d1
6
+ metadata.gz: a7527caccb62a545f59557f6dbb0e2e37edab002530069b8b8cec4fb9d16bbc8ec85d916be1ec6c74f0cfdeedaa62de8c0579ecebc268a9c950828bbd4df4460
7
+ data.tar.gz: 34b755458f0a7346dd53040c870358e4feac2b3ea93ff64341858f4d2a7820f77ac0227c39123ba1cdb71baea3d5848ef054d5674404b9b6aae8c029c6d1414e
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright 2017-2022, Huub de Beer <huub@heerdebeer.org>
4
+ # Copyright 2017-2024, Huub de Beer <huub@heerdebeer.org>
5
5
  #
6
6
  # This file is part of pandocomatic.
7
7
  #
@@ -32,11 +32,14 @@ module Pandocomatic
32
32
  # Parse the arguments, returns a triplet with the global options, an
33
33
  # optional subcommand, and the (optional) options for that subcommand.
34
34
  #
35
+ # As a side effect, this method will create and install a logger for
36
+ # Pandocomatic::LOG.
37
+ #
35
38
  # @param args [String, Array] A command-line invocation string or a list of strings like ARGV
36
39
  #
37
40
  # @return [Configuration] The configuration for running pandocomatic given
38
41
  # the command-line options.
39
- def self.parse(args)
42
+ def self.parse!(args)
40
43
  args = args.split if args.is_a? String
41
44
 
42
45
  begin
@@ -56,8 +59,12 @@ module Pandocomatic
56
59
  # General options
57
60
  opt :dry_run, 'Do a dry run', short: '-y'
58
61
  opt :verbose, 'Run verbosely', short: '-V'
59
- opt :debug, 'Debug mode, shows pandoc invocations', short: '-b'
60
62
  opt :modified_only, 'Modified files only', short: '-m'
63
+ opt :enable, 'Enable feature', short: '-e', multi: true, type: String
64
+
65
+ # Logging
66
+ opt :log, 'Log to file', short: '-l', type: String, default: 'pandocomatic.log'
67
+ opt :log_level, 'Log level', type: String, default: 'INFO'
61
68
 
62
69
  # Configuration of the converter
63
70
  opt :data_dir, 'Data dir', short: '-d', type: String
@@ -75,19 +82,33 @@ module Pandocomatic
75
82
  end
76
83
 
77
84
  # All options should be parsed according to the specification given in the parser
85
+ # parser#parse removes arguments from args during parsing. Keep a copy for error reporting.
86
+ given_arguments = args.dup
87
+
78
88
  begin
79
89
  options = parser.parse args
80
90
  rescue Optimist::CommandlineError => e
81
- raise CLIError.new(:problematic_invocation, e, args)
91
+ raise CLIError.new(:problematic_invocation, e, given_arguments)
82
92
  end
83
93
 
94
+ if options[:log_given]
95
+ log_file = options[:log]
96
+ log_level = options[:log_level]
97
+ end
98
+
99
+ Pandocomatic::LOG.install_file_logger(log_file, log_level)
100
+
84
101
  options = use_custom_version options
85
102
  options = use_custom_help options
86
103
 
87
104
  if options_need_to_be_validated? options
105
+ Pandocomatic::LOG.debug 'Validating command-line arguments:'
106
+
88
107
  # if no input option is specified, all items following the last option
89
108
  # are treated as input files.
90
109
  if !(options[:input_given])
110
+ Pandocomatic::LOG.debug '✓ Option \'--input\' not used: ' \
111
+ 'treat all arguments after last option as input files or directories.'
91
112
  options[:input] = args
92
113
  options[:input_given] = true
93
114
  elsif !args.empty?
@@ -100,6 +121,12 @@ module Pandocomatic
100
121
  # Support multiple input files for conversion
101
122
  multiple_inputs = options[:input].size > 1
102
123
 
124
+ if multiple_inputs
125
+ Pandocomatic::LOG.debug '✓ Convert multiple input files or directories.'
126
+ else
127
+ Pandocomatic::LOG.debug '✓ Convert single input file or directory.'
128
+ end
129
+
103
130
  # The input files or directories should exist
104
131
  input = options[:input].map do |input_file|
105
132
  raise CLIError.new(:input_does_not_exist, nil, input_file) unless File.exist? input_file
@@ -113,12 +140,14 @@ module Pandocomatic
113
140
 
114
141
  File.absolute_path input_file
115
142
  end
143
+ Pandocomatic::LOG.debug '✓ Input files and directories exist.'
116
144
 
117
145
  # You cannot use the --stdout option while converting directories
118
146
  if options[:stdout_given] && File.directory?(input.first)
119
147
  options[:stdout] = false
120
148
  raise CLIError, :cannot_use_stdout_with_directory
121
149
  end
150
+ Pandocomatic::LOG.debug '✓ Write output to STDOUT.' if options[:stdout_given]
122
151
 
123
152
  if options[:output_given]
124
153
  # You cannot use --stdout with --output
@@ -126,19 +155,23 @@ module Pandocomatic
126
155
  options[:stdout] = false
127
156
  raise CLIError, :cannot_use_both_output_and_stdout
128
157
  else
158
+ Pandocomatic::LOG.debug "✓ Write output to '#{options[:output]}'."
129
159
  output = File.absolute_path options[:output]
130
160
  # Input and output should be both files or directories
131
161
  match_file_types input.first, output
162
+ Pandocomatic::LOG.debug '✓ Input and outputs types match.'
132
163
 
133
164
  # The output, if it already exist, should be writable
134
165
  unless (!File.exist? output) || File.writable?(output)
135
166
  raise CLIError.new(:output_is_not_writable, nil,
136
167
  output)
137
168
  end
169
+ Pandocomatic::LOG.debug '✓ Existing output is writable.' if File.exist? output
138
170
  end
139
171
  elsif !multiple_inputs && File.directory?(input.first)
140
172
  raise CLIError, :no_output_given
141
173
  end
174
+
142
175
  # If the input is a directory, an output directory should be
143
176
  # specified as well. If the input is a file, the output could be
144
177
  # specified in the input file, or STDOUT could be used.
@@ -154,6 +187,7 @@ module Pandocomatic
154
187
  raise CLIError.new(:data_dir_is_not_readable, nil, data_dir) unless File.readable? data_dir
155
188
  raise CLIError.new(:data_dir_is_not_a_directory, nil, data_dir) unless File.directory? data_dir
156
189
  end
190
+ Pandocomatic::LOG.debug "✓ Can read data directory '#{options[:data_dir]}'." if options[:data_dir_given]
157
191
 
158
192
  # Config file, if specified, should be an existing and readable file
159
193
  if options[:config_given]
@@ -163,10 +197,28 @@ module Pandocomatic
163
197
  raise CLIError.new(:config_file_is_not_readable, nil, config) unless File.readable? config
164
198
  raise CLIError.new(:config_file_is_not_a_file, nil, config) unless File.file? config
165
199
  end
200
+ Pandocomatic::LOG.debug "✓ Can read configuration file '#{options[:config]}'." if options[:config_given]
201
+
202
+ if options[:enable_given]
203
+ Pandocomatic::LOG.info '- Checking feature toggles:'
166
204
 
205
+ features = []
206
+
207
+ options[:enable].each do |feature_string|
208
+ feature = feature_string.downcase.sub('-', '_').to_sym
209
+ unless Pandocomatic::FEATURES.include? feature
210
+ raise CLIError.new(:feature_toggle_does_not_exist, nil, feature_string)
211
+ end
212
+
213
+ Pandocomatic::LOG.info " ✓ Enabling feature '#{feature}'."
214
+ features << feature
215
+ end
216
+
217
+ options[:enable] = features
218
+ end
167
219
  end
168
220
 
169
- Configuration.new options, input
221
+ Configuration.new(options, input)
170
222
  end
171
223
 
172
224
  # rubocop:enable Metrics
@@ -39,7 +39,6 @@ module Pandocomatic
39
39
  @@total = 0
40
40
  @@dry_run = false
41
41
  @@quiet = false
42
- @@debug = false
43
42
  @@src_root = '.'
44
43
  @@modified_only = false
45
44
 
@@ -57,7 +56,6 @@ module Pandocomatic
57
56
  @@src_root = configuration.src_root
58
57
  @@dry_run = configuration.dry_run?
59
58
  @@quiet = configuration.quiet?
60
- @@debug = configuration.debug?
61
59
  @@modified_only = configuration.modified_only?
62
60
  @@total = 0
63
61
  end
@@ -83,13 +81,6 @@ module Pandocomatic
83
81
  @@quiet
84
82
  end
85
83
 
86
- # Is this Command executed in debug mode?
87
- #
88
- # @return [Boolean]
89
- def debug?
90
- @@debug
91
- end
92
-
93
84
  # Is this Command only executed on modified files?
94
85
  #
95
86
  # @return [Boolean]
@@ -127,7 +118,9 @@ module Pandocomatic
127
118
  # Execute this Command. A Command can be dry-run as well, in which it is
128
119
  # not actually run.
129
120
  def execute
130
- CommandPrinter.new(self).print unless quiet?
121
+ description = CommandPrinter.new(self)
122
+ Pandocomatic::LOG.info description
123
+ description.print unless quiet?
131
124
  run if !dry_run? && runnable?
132
125
  end
133
126
 
@@ -128,7 +128,10 @@ module Pandocomatic
128
128
 
129
129
  # Run this command
130
130
  def run
131
- Dir.mkdir @dst_dir if create_directory?
131
+ if create_directory?
132
+ Pandocomatic::LOG.info " Creating directory '#{@dst_dir}'"
133
+ Dir.mkdir @dst_dir
134
+ end
132
135
  rescue SystemError => e
133
136
  raise IOError.new(:error_creating_directory, e, @dst_dir)
134
137
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright 2017-2022, Huub de Beer <Huub@heerdebeer.org>
4
+ # Copyright 2017-2024, Huub de Beer <Huub@heerdebeer.org>
5
5
  #
6
6
  # This file is part of pandocomatic.
7
7
  #
@@ -21,6 +21,7 @@
21
21
  module Pandocomatic
22
22
  require 'paru'
23
23
  require 'shellwords'
24
+ require 'yaml'
24
25
 
25
26
  require_relative 'command'
26
27
  require_relative '../error/io_error'
@@ -100,8 +101,7 @@ module Pandocomatic
100
101
  else
101
102
  template_name
102
103
  end
103
-
104
- @metadata = PandocMetadata.load_file @src
104
+ @metadata = @config.get_metadata @src, @template_name
105
105
  @dst = @config.set_destination @dst, @template_name, @metadata
106
106
 
107
107
  @errors.push IOError.new(:file_does_not_exist, nil, @src) unless File.exist? @src
@@ -149,15 +149,27 @@ module Pandocomatic
149
149
  template = Template.new INTERNAL_TEMPLATE
150
150
  end
151
151
 
152
+ if template.name == INTERNAL_TEMPLATE
153
+ Pandocomatic::LOG.debug ' # Using internal template.'
154
+ else
155
+ Pandocomatic::LOG.debug " # Using template '#{template.name}'."
156
+ end
157
+
152
158
  # Ignore the `--verbose` option, and warn about ignoring it
153
- if pandoc_options.key? 'verbose'
159
+ if pandoc_options.key?('verbose') && !@config.feature_enabled?(:pandoc_verbose)
154
160
  pandoc_options.delete 'verbose'
155
- warn 'WARNING: Ignoring the pandoc option --verbose because it ' \
156
- 'might interfere with the working of pandocomatic.'
161
+ warn 'WARNING: Ignoring the pandoc option "--verbose" because it ' \
162
+ 'might interfere with the working of pandocomatic. If you want to use ' \
163
+ '"--verbose" anyway, use pandocomatic\'s feature toggle ' \
164
+ '"--enable pandoc-verbose".'
157
165
  end
158
166
 
159
167
  template.merge! Template.new(INTERNAL_TEMPLATE, @metadata.pandocomatic) if @metadata.pandocomatic?
160
168
 
169
+ Pandocomatic::LOG.debug ' # Selected template mixed with internal template and pandocomatic metadata ' \
170
+ "gives final template:#{Pandocomatic::LOG.indent(YAML.dump(template.to_h).sub('---', ''),
171
+ 34)}"
172
+
161
173
  # Write out the results of the conversion process to file.
162
174
  @dst = @metadata.pandoc_options['output'] if @dst.to_s.empty? && @metadata.pandoc_options.key?('output')
163
175
 
@@ -165,6 +177,7 @@ module Pandocomatic
165
177
  setup template
166
178
 
167
179
  # Read in the file to convert
180
+ Pandocomatic::LOG.debug " → Reading source file: '#{@src}'"
168
181
  input = File.read @src
169
182
 
170
183
  # Run the default preprocessors to mix-in information about the file
@@ -181,12 +194,13 @@ module Pandocomatic
181
194
 
182
195
  begin
183
196
  # Either output to file or to STDOUT.
184
-
185
197
  if @config.stdout?
198
+ Pandocomatic::LOG.debug ' ← Writing output to STDOUT.'
186
199
  puts output
187
200
  @dst.close!
188
201
  else
189
202
  unless use_output_option @dst
203
+ Pandocomatic::LOG.debug " ← Writing output to '#{@dst}'."
190
204
  File.open(@dst, 'w') do |file|
191
205
  raise IOError.new(:file_is_not_a_file, nil, @dst) unless File.file? @dst
192
206
  raise IOError.new(:file_is_not_writable, nil, @dst) unless File.writable? @dst
@@ -206,6 +220,7 @@ module Pandocomatic
206
220
  def pandoc(input, options, src_dir)
207
221
  absolute_dst = File.expand_path @dst
208
222
  Dir.chdir(src_dir) do
223
+ Pandocomatic::LOG.debug " # Changing directory to '#{src_dir}'"
209
224
  converter = Paru::Pandoc.new
210
225
  options.each do |option, value|
211
226
  # Options come from a YAML string. In YAML, properties without a value get value nil.
@@ -235,17 +250,16 @@ module Pandocomatic
235
250
  (option == 'rename')
236
251
  # don't let pandoc write the output to enable postprocessing
237
252
  rescue StandardError
238
- if debug?
239
- warn "WARNING: The pandoc option '#{option}' (with value '#{value}') " \
240
- 'is not recognized by paru. This option is skipped.'
241
- end
253
+ Pandocomatic::LOG.warn "WARNING: The pandoc option '#{option}'"
254
+ " (with value '#{value}') is not recognized by paru. This option is skipped."
242
255
  end
243
256
  end
244
257
 
245
258
  converter.send 'output', absolute_dst if use_output_option absolute_dst
246
259
 
247
260
  begin
248
- puts converter.to_command if debug?
261
+ Pandocomatic::LOG.debug ' # Running pandoc'
262
+ Pandocomatic::LOG.debug " | #{Pandocomatic::LOG.indent(converter.to_command, 43)}"
249
263
  converter << input
250
264
  rescue Paru::Error => e
251
265
  raise PandocError.new(:error_running_pandoc, e, input)
@@ -293,6 +307,7 @@ module Pandocomatic
293
307
  def process(input, type, template)
294
308
  if template.send "#{type}?"
295
309
  processors = template.send type
310
+ Pandocomatic::LOG.debug " # Running #{type}:" unless processors.empty?
296
311
  output = input
297
312
  processors.each do |processor|
298
313
  script = if Path.local_path? processor
@@ -313,6 +328,7 @@ module Pandocomatic
313
328
  raise ProcessorError.new(:script_is_not_executable, nil, command) unless File.executable? command
314
329
 
315
330
  begin
331
+ Pandocomatic::LOG.debug " | #{script}"
316
332
  output = Processor.run(script, output)
317
333
  rescue StandardError => e
318
334
  ProcessorError.new(:error_processing_script, e, [script, @src])
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright 2017, 2022, Huub de Beer <Huub@heerdebeer.org>
4
+ # Copyright 2017—2024 Huub de Beer <Huub@heerdebeer.org>
5
5
  #
6
6
  # This file is part of pandocomatic.
7
7
  #
@@ -49,11 +49,11 @@ module Pandocomatic
49
49
  @config = config
50
50
  @src = src
51
51
 
52
- metadata = PandocMetadata.load_file @src
52
+ metadata = @config.get_metadata @src
53
53
 
54
54
  subcommands = []
55
55
 
56
- if metadata.template?
56
+ if metadata&.template?
57
57
  # There are templates in this document's metadata, try to use
58
58
  # those.
59
59
  metadata.templates.each do |template_name|
@@ -99,7 +99,9 @@ module Pandocomatic
99
99
  def execute
100
100
  return if @subcommands.empty?
101
101
 
102
- CommandPrinter.new(self).print unless quiet? || (@subcommands.size == 1)
102
+ description = CommandPrinter.new(self)
103
+ Pandocomatic::LOG.info description
104
+ description.print unless quiet? || (@subcommands.size == 1)
103
105
  run if !dry_run? && runnable?
104
106
 
105
107
  @subcommands.each(&:execute)
@@ -36,7 +36,7 @@ module Pandocomatic
36
36
 
37
37
  # Create a new ConvertListCommand
38
38
  def initialize
39
- super()
39
+ super
40
40
  @subcommands = []
41
41
  end
42
42
 
@@ -89,7 +89,9 @@ module Pandocomatic
89
89
  def execute
90
90
  return if @subcommands.empty?
91
91
 
92
- CommandPrinter.new(self).print unless quiet?
92
+ description = CommandPrinter.new(self)
93
+ Pandocomatic::LOG.info description
94
+ description.print unless quiet?
93
95
  run if !dry_run? && runnable?
94
96
 
95
97
  @subcommands.each(&:execute)
@@ -45,7 +45,10 @@ module Pandocomatic
45
45
 
46
46
  # Run this CopyFileCommand
47
47
  def run
48
- FileUtils.cp(@src, @dst) if file_modified?(@src, @dst)
48
+ if file_modified?(@src, @dst)
49
+ Pandocomatic::LOG.info "Copying '#{@src}' → '#{@dst}'"
50
+ FileUtils.cp(@src, @dst)
51
+ end
49
52
  rescue StandardError => e
50
53
  raise IOError.new(:unable_to_copy_file, e, [@src, @dst])
51
54
  end
@@ -67,7 +67,10 @@ module Pandocomatic
67
67
 
68
68
  # Run this CreateLinkCommand
69
69
  def run
70
- File.symlink @dst_target, @dst unless File.exist? @dst
70
+ unless File.exist? @dst
71
+ File.symlink @dst_target, @dst
72
+ Pandocomatic::LOG.info "Creating symlink '#{@dst_target}' → '#{@dst}'"
73
+ end
71
74
  rescue StandardError => e
72
75
  raise IOError.new(:unable_to_create_symbolic_link, e, [@src, @dst])
73
76
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics
3
4
  #--
4
- # Copyright 2014—2022 Huub de Beer <Huub@heerdebeer.org>
5
+ # Copyright 2014—2024 Huub de Beer <Huub@heerdebeer.org>
5
6
  #
6
7
  # This file is part of pandocomatic.
7
8
  #
@@ -41,6 +42,7 @@ module Pandocomatic
41
42
  # "unskip" option
42
43
  DEFAULT_SETTINGS = {
43
44
  'skip' => ['.*', 'pandocomatic.yaml'],
45
+ 'extract-metadata-from' => [],
44
46
  'recursive' => true,
45
47
  'follow-links' => false,
46
48
  'match-files' => 'first'
@@ -111,7 +113,71 @@ module Pandocomatic
111
113
  'zimwiki' => 'zimwiki'
112
114
  }.freeze
113
115
 
114
- # rubocop:disable Metrics
116
+ # Pandoc's mapping from file extensions to pandoc source format. Taken from
117
+ # https://github.com/jgm/pandoc/blob/main/src/Text/Pandoc/Format.hs
118
+ PANDOCS_EXTENSION_TO_FORMAT_MAPPING = {
119
+ '.Rmd' => 'markdown',
120
+ '.adoc' => 'asciidoc',
121
+ '.asciidoc' => 'asciidoc',
122
+ '.bib' => 'biblatex',
123
+ '.context' => 'context',
124
+ '.csv' => 'csv',
125
+ '.ctx' => 'context',
126
+ '.db' => 'docbook',
127
+ '.dj' => 'djot',
128
+ '.docx' => 'docx',
129
+ '.dokuwiki' => 'dokuwiki',
130
+ '.epub' => 'epub',
131
+ '.fb2' => 'fb2',
132
+ '.htm' => 'html',
133
+ '.html' => 'html',
134
+ '.icml' => 'icml',
135
+ '.ipynb' => 'ipynb',
136
+ '.json' => 'json',
137
+ '.latex' => 'latex',
138
+ '.lhs' => 'markdown',
139
+ '.ltx' => 'latex',
140
+ '.markdown' => 'markdown',
141
+ '.markua' => 'markua',
142
+ '.md' => 'markdown',
143
+ '.mdown' => 'markdown',
144
+ '.mdwn' => 'markdown',
145
+ '.mkd' => 'markdown',
146
+ '.mkdn' => 'markdown',
147
+ '.ms' => 'ms',
148
+ '.muse' => 'muse',
149
+ '.native' => 'native',
150
+ '.odt' => 'odt',
151
+ '.opml' => 'opml',
152
+ '.org' => 'org',
153
+ '.pptx' => 'pptx',
154
+ '.ris' => 'ris',
155
+ '.roff' => 'ms',
156
+ '.rst' => 'rst',
157
+ '.rtf' => 'rtf',
158
+ '.s5' => 's5',
159
+ '.t2t' => 't2t',
160
+ '.tei' => 'tei',
161
+ '.tex' => 'latex',
162
+ '.texi' => 'texinfo',
163
+ '.texinfo' => 'texinfo',
164
+ '.text' => 'markdown',
165
+ '.textile' => 'textile',
166
+ '.tsv' => 'tsv',
167
+ '.typ' => 'typst',
168
+ '.txt' => 'markdown',
169
+ '.wiki' => 'mediawiki',
170
+ '.xhtml' => 'html',
171
+ '.1' => 'man',
172
+ '.2' => 'man',
173
+ '.3' => 'man',
174
+ '.4' => 'man',
175
+ '.5' => 'man',
176
+ '.6' => 'man',
177
+ '.7' => 'man',
178
+ '.8' => 'man',
179
+ '.9' => 'man'
180
+ }.freeze
115
181
 
116
182
  # Configuration models a pandocomatic configuration.
117
183
  class Configuration
@@ -142,7 +208,7 @@ module Pandocomatic
142
208
  @output = if output?
143
209
  options[:output]
144
210
  elsif to_stdout? options
145
- Tempfile.new(@input.base)
211
+ Tempfile.new(@input.base) unless @input.nil?
146
212
  elsif @input.is_a? Input
147
213
  @input.base
148
214
  end
@@ -238,18 +304,11 @@ module Pandocomatic
238
304
  @options[:verbose_given] and @options[:verbose]
239
305
  end
240
306
 
241
- # Is the debug CLI option given?
242
- #
243
- # @return [Boolean]
244
- def debug?
245
- @options[:debug_given] and @options[:debug]
246
- end
247
-
248
307
  # Run pandocomatic in quiet mode?
249
308
  #
250
309
  # @return [Boolean]
251
310
  def quiet?
252
- [verbose?, debug?, dry_run?].none?
311
+ [verbose?, dry_run?].none?
253
312
  end
254
313
 
255
314
  # Is the modified only CLI option given?
@@ -294,6 +353,14 @@ module Pandocomatic
294
353
  @options[:config_given]
295
354
  end
296
355
 
356
+ # Should given feature be enabled?
357
+ #
358
+ # @param feature [Symbol] feature toggle to check
359
+ # @return [Boolean]
360
+ def feature_enabled?(feature)
361
+ @options[:enable_given] and Pandocomatic::FEATURES.include?(feature) and @options[:enable].include?(feature)
362
+ end
363
+
297
364
  # Is the output CLI option given and can that output be used?
298
365
  #
299
366
  # @return [Boolean]
@@ -357,6 +424,40 @@ module Pandocomatic
357
424
  end
358
425
  end
359
426
 
427
+ # Get a pandoc metadata object for given source file and template.
428
+ #
429
+ # @param src [String] path to source file
430
+ # @param template_name [String] template used; optional parameter
431
+ # @return [PandocMetadata] Pandoc's metadata for given file and template.
432
+ def get_metadata(src, template_name = nil)
433
+ if extract_metadata_from? src
434
+ PandocMetadata.load_file src
435
+ else
436
+ src_format = nil
437
+
438
+ # Determine source format based on template
439
+ if template_name && @templates.key?(template_name) && @templates[template_name].pandoc?
440
+ pandoc = @templates[template_name].pandoc
441
+ src_format = pandoc['from'] if pandoc.key? 'from'
442
+ end
443
+
444
+ if src_format.nil?
445
+ # Determine source format based on extension like pandoc does.
446
+ # See https://github.com/jgm/pandoc/blob/main/src/Text/Pandoc/Format.hs
447
+ # for that mapping
448
+ src_extension = File.extname src
449
+ src_format = PANDOCS_EXTENSION_TO_FORMAT_MAPPING[src_extension]
450
+ end
451
+
452
+ if !src_format || src_format == 'markdown'
453
+ # Behave like pandoc: If no source format can be determined, assume markdown
454
+ PandocMetadata.load_file src
455
+ else
456
+ PandocMetadata.empty src_format
457
+ end
458
+ end
459
+ end
460
+
360
461
  # Should the source file be converted given this Configuration?
361
462
  #
362
463
  # @param src [String] True if this source file matches the 'glob'
@@ -577,6 +678,8 @@ module Pandocomatic
577
678
  case setting
578
679
  when 'skip'
579
680
  @settings['skip'] = @settings['skip'].concat(value).uniq
681
+ when 'extract-metadata-from'
682
+ @settings['extract-metadata-from'] = @settings['extract-metadata-from'].concat(value).uniq
580
683
  when 'data-dir'
581
684
  next # skip data-dir setting; is set once in initialization
582
685
  else
@@ -761,6 +864,15 @@ module Pandocomatic
761
864
  path
762
865
  end
763
866
  end
867
+
868
+ # Should we try to extract pandoc YAML metadata from source file?
869
+ def extract_metadata_from?(src)
870
+ if @settings.key? 'extract-metadata-from'
871
+ @settings['extract-metadata-from'].any? { |glob| File.fnmatch glob, File.basename(src) }
872
+ else
873
+ false
874
+ end
875
+ end
764
876
  end
765
- # rubocop:enable Metrics
766
877
  end
878
+ # rubocop:enable Metrics
@@ -2,6 +2,7 @@ settings:
2
2
  recursive: true
3
3
  follow-symlinks: false
4
4
  skip: ['.*', 'pandocomatic.yaml']
5
+ extract-metadata-from: []
5
6
  match-files: 'first'
6
7
  templates:
7
8
  empty:
@@ -41,7 +41,7 @@ module Pandocomatic
41
41
  # @param data [Object = nil] extra information attached to this
42
42
  # PandocomaticError, if any; optional
43
43
  def initialize(type = :unknown, error = nil, data = nil)
44
- super type.to_s.gsub('_', ' ').capitalize
44
+ super(type.to_s.gsub('_', ' ').capitalize)
45
45
  @type = type
46
46
  @error = error
47
47
  @data = data
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  #--
4
- # Copyright 2019 Huub de Beer <Huub@heerdebeer.org>
4
+ # Copyright 2019—2024 Huub de Beer <Huub@heerdebeer.org>
5
5
  #
6
6
  # This file is part of pandocomatic.
7
7
  #
@@ -19,8 +19,6 @@
19
19
  # with pandocomatic. If not, see <http://www.gnu.org/licenses/>.
20
20
  #++
21
21
  module Pandocomatic
22
- require_relative 'configuration'
23
-
24
22
  # Generic class to handle input files and directories in a general manner.
25
23
  class Input
26
24
  attr_reader :errors