resources/asciidoctor/lib/copy_images/copier.rb (70 lines of code) (raw):

# frozen_string_literal: true require 'csv' require 'fileutils' require 'set' require_relative '../log_util' module CopyImages ## # Handles finding images, copying them, and *not* copying them if they have # already been copied. class Copier include LogUtil def initialize # TODO: store this set on the document so we don't duplicate copies # for sub-documents caused by alternative examples @copied = Set[] end def copy_image(block, uri) return unless @copied.add? uri # Skip images we've copied before source = find_source block, uri return unless source # Skip images we can't find info block: block, message: "copying #{source}" copy_image_proc = block.document.attr 'copy_image' if copy_image_proc # Delegate to a proc for copying if one is defined. Used for testing. copy_image_proc.call(uri, source) else perform_copy block, uri, source end end def perform_copy(block, uri, source) destination = File.join block.document.options[:to_dir], uri destination_dir = File.dirname destination FileUtils.mkdir_p destination_dir FileUtils.cp source, destination end ## # Does a breadth first search starting at the base_dir of the document and # any referenced resources. This isn't super efficient but it is how a2x # works and we strive for compatibility. def find_source(block, uri) to_check = roots_to_check block checked = [] while (dir = to_check.shift) checked << block.normalize_system_path(uri, dir) return checked.last if File.readable? checked.last next unless Dir.exist?(dir) to_check += subdirs(dir) end log_missing block, checked, uri nil end ## # The directory roots to check for the file to copy def roots_to_check(block) to_check = [block.document.base_dir] resources = block.document.attr 'resources' return to_check unless resources return to_check if resources.empty? to_check + CSV.parse_line(resources) rescue CSV::MalformedCSVError => error error block: block, message: "Error loading [resources]: #{error}" to_check end def subdirs(dir) Dir.new(dir) .reject { |f| ['.', '..'].include? f } .map { |f| File.join dir, f } .select { |f| File.directory?(f) } end ## # Log a warning for files that we couldn't find. def log_missing(block, checked, uri) # Sort the list of directories we did check so it is consistent from # machine to machine. This is mostly useful for testing, but it nice # if you happen to want to compare CI to a local machine. checked.sort! do |lhs, rhs| by_depth = lhs.scan(%r{/}).count <=> rhs.scan(%r{/}).count if by_depth != 0 by_depth else lhs <=> rhs end end warn block: block, message: "can't read image [#{uri}] at any of #{checked}" end end end