# frozen_string_literal: true
#
class Pathname

  # :markup: markdown
  #
  # call-seq:
  #   Pathname.find(ignore_error: true) -> nil
  #
  # With a block given, performs a depth-first traversal of the path in `self`;
  # calls the block with each found path:
  #
  # ```ruby
  # paths = []
  # Pathname('lib').find {|path| paths << path }
  # paths.size  # => 909
  # paths.take(3)
  # # =>
  # # [#<Pathname:lib>,
  # #  #<Pathname:lib/English.gemspec>,
  # #  #<Pathname:lib/English.rb>]
  # ```
  #
  # When `self` contains `'.'`, the found paths omit the leading `'./'`:
  #
  # ```ruby
  # paths = []
  # Dir.chdir('lib') do
  #   Pathname('.').find {|path| paths << path }
  # end
  # paths.take(3)
  # # # =>
  # # [#<Pathname:.>,
  # #  #<Pathname:English.gemspec>,
  # #  #<Pathname:English.rb>]
  # ```
  #
  # This method calls method Find.find;
  # therefore method Find.prune may be used in the block:
  #
  # ```ruby
  # files = []
  # Pathname('.').find do |path|
  #   Find.prune if File.basename(path) == 'test'
  #   next unless File.file?(path) && File.extname(path) == '.rb'
  #   files << path
  # end
  # files.size # => 6690
  # files.take(3)
  # # # =>
  # # [#<Pathname:KNOWNBUGS.rb>,
  # #  #<Pathname:array.rb>,
  # #  #<Pathname:ast.rb>]
  # ```
  #
  # Raises an exception if the path in `self` cannot be read.
  #
  # When keyword argument `ignore_error` is given as `true` (the default),
  # certain exceptions during traversal are ignored (i.e., silently rescued):
  # Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL;
  # when given as `false`, no exceptions are rescued.
  #
  # Note that these exceptions may be ignored only in `Pathname#find` traversal code;
  # an exception raised before traversal begins,
  # or raised while in the block is not ignored.
  # Each of the calls below raises an Errno::ENOENT exception that is not ignored:
  #
  # ```ruby
  # Pathname('nosuch').find { }
  # Pathname('lib').find {|entry| raise Errno::ENOENT }
  # ```
  #
  # With no block given, returns a new Enumerator.
  def find(ignore_error: true) # :yield: pathname
    return to_enum(__method__, ignore_error: ignore_error) unless block_given?
    require 'find'
    if @path == '.'
      Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f.delete_prefix('./')) }
    else
      Find.find(@path, ignore_error: ignore_error) {|f| yield self.class.new(f) }
    end
  end
end


class Pathname    # * FileUtils *

  # :markup: markdown
  #
  # call-seq:
  #   rmtree -> 0
  #
  # Deletes the entire filetree at the path in `self`; returns `0`:
  #
  # ```ruby
  # dir_pn = Pathname('foo/bar/baz') # => #<Pathname:foo/bar/baz>
  # dir_pn.mkpath                    # Create 'baz' and intermediate directories.
  # file_pn = dir_pn.join('t.tmp')   # => #<Pathname:foo/bar/baz/t.tmp>
  # file_pn.write('foo')             # Create file at nested directory 'baz'.
  # Pathname('foo').rmtree           # Delete the entire tree at directory 'foo'.
  # Pathname('foo').exist?           # => false
  # ```
  #
  # Use method #rmdir to delete a single (empty) directory.
  #
  def rmtree(noop: nil, verbose: nil, secure: nil)
    # The name "rmtree" is borrowed from File::Path of Perl.
    # File::Path provides "mkpath" and "rmtree".
    require 'fileutils'
    FileUtils.rm_rf(@path, noop: noop, verbose: verbose, secure: secure)
    self
  end
end

class Pathname    # * tmpdir *
  # call-seq:
  #   Pathname.mktmpdir -> new_pathname
  #   Pathname.mktmpdir {|pathname| ... } -> object
  #
  # Creates:
  #
  # - A temporary directory via Dir.mktmpdir.
  # - A \Pathname object that contains the path to that directory.
  #
  # With no block given, returns the created pathname;
  # the caller should delete the created directory when it is no longer needed
  # (FileUtils.rm_r is a convenient method for the deletion):
  #
  #   pathname = Pathname.mktmpdir
  #   dirpath = pathname.to_s
  #   Dir.exist?(dirpath) # => true
  #   # Do something with the directory.
  #   require 'fileutils'
  #   FileUtils.rm_r(dirpath)
  #
  # With a block given, calls the block with the created pathname;
  # on block exit, automatically deletes the created directory and all its contents;
  # returns the block's exit value:
  #
  #   pathname = Pathname.mktmpdir do |p|
  #     # Do something with the directory.
  #     p
  #   end
  #   Dir.exist?(pathname.to_s) # => false
  def self.mktmpdir
    require 'tmpdir' unless defined?(Dir.mktmpdir)
    if block_given?
      Dir.mktmpdir do |dir|
        dir = self.new(dir)
        yield dir
      end
    else
      self.new(Dir.mktmpdir)
    end
  end
end
