module Exa class Visitor def initialize(root) @root = root end def gather_leaves(branch=@root, depth: 10) return [] if depth < 0 if branch.children.any? branch.children.flat_map do |child| gather_leaves(child, depth: depth-1) end.uniq else # we are a leaf! [ branch ] end end def gather_branches(branch=@root, depth: 10) return [] if depth < 0 if branch.children.any? [ branch ] + branch.children.flat_map do |child| gather_branches(child, depth: depth-1) end.uniq else # we are a leaf! [ ] end end def seek(path, create_missing: true) path = '/' + path unless path.start_with?('/') _root, *path_segments = path.split('/') current = @root path_segments.each do |segment| next_child = current.children.detect do |child| child.name == segment end current = if next_child next_child else return nil unless create_missing current.create_child(child_name: segment) end end current end def query(path) path = '/' + path unless path.start_with?('/') _root, next_segment, *remaining_segments = path.split('/') remaining_path = '/' + remaining_segments.join('/') current = @root return [current] unless next_segment if next_segment == '*' # need to multiplex remaining query across *all* children next_children = current.children if remaining_segments.any? next_children.flat_map do |child| child.query(remaining_path).uniq end else next_children end elsif next_segment == '**' # this is more subtle, and really points to: # do we need to be treating the path query as a regular expression? # and matching against *all* filenames? if remaining_segments.any? gather_branches(current).flat_map do |folder| folder.query(remaining_path).uniq end else gather_leaves(current) end elsif next_segment == '..' # need to back up... parent = current.parent if remaining_segments.any? parent.children.flat_map do |child| child.query(remaining_path).uniq end.uniq else [ parent ] end else # need to find just child matching *this* segment next_child = current.children.detect { |c| c.name == next_segment } if next_child if remaining_segments.any? next_child.query(remaining_path).uniq else [ next_child ] end else [] end end end end end