# frozen_string_literal: true # # find.rb: the Find module for processing all files under a given directory. # # :markup: markdown # # \Module \Find supports the top-down traversal of entries in the file system. module Find # The version string VERSION = "0.2.0" # :markup: markdown # # With a block given, performs a depth-first traversal of each given path in `paths`; # calls the block with each found file or directory path: # # ```ruby # paths = [] # Find.find('bin', 'jit') {|path| paths << path } # paths # # => # # ["bin", # # "bin/gem", # # "jit", # # "jit/Cargo.toml", # # "jit/src", # # "jit/src/lib.rs"] # ``` # # Raises an exception if a given path 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 `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 # Find.find('nosuch') { } # Find.find('lib') {|entry| raise Errno::ENOENT } # ``` # # With no block given, returns a new Enumerator. def find(*paths, ignore_error: true) # :yield: path block_given? or return enum_for(__method__, *paths, ignore_error: ignore_error) fs_encoding = Encoding.find("filesystem") paths.collect!{|d| raise Errno::ENOENT, d unless File.exist?(d); d.dup}.each do |path| path = path.to_path if path.respond_to? :to_path enc = path.encoding == Encoding::US_ASCII ? fs_encoding : path.encoding ps = [path] while file = ps.shift catch(:prune) do yield file.dup begin s = File.lstat(file) rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL raise unless ignore_error next end if s.directory? then begin fs = Dir.children(file, encoding: enc) rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR, Errno::ELOOP, Errno::ENAMETOOLONG, Errno::EINVAL raise unless ignore_error next end fs.sort! fs.reverse_each {|f| f = File.join(file, f) ps.unshift f } end end end end nil end # :markup: markdown # # call-seq: # Find.prune # # This method is meaningful only within a block given with Find.find. # # Inside such a block, # "prunes" the traversed file tree by not descending into the current directory: # # ```ruby # files = [] # Find.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) # => ["./KNOWNBUGS.rb", "./array.rb", "./ast.rb"] # ``` # def prune throw :prune end module_function :find, :prune end