pandocomatic 1.1.3 → 2.0.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 (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