Usage & API¶
The public API lives at the module root (github.com/go-ruby-find/find). It is
Ruby-shaped but Go-idiomatic: Walk mirrors Find.find and ErrPrune
mirrors Find.prune, while the surface follows Go conventions — an explicit
error, an injected Lister for I/O, no global state.
Status: implemented
The library is built and importable as github.com/go-ruby-find/find, bound into rbgo as a native module; see Roadmap.
Install¶
The Lister seam¶
Walk performs no I/O itself; the host injects all filesystem access:
type Lister interface {
Exist(path string) bool // Ruby File.exist? (start paths only)
IsDir(path string) (isDir bool, err error) // Ruby File.lstat(path).directory?
Children(dir string) (entries []string, err error) // Ruby Dir.children (base names, unsorted ok)
}
IsDir mirrors File.lstat(...).directory? — symlinks are not followed, so a
symlink to a directory must report false. Children returns base names in any
order; Walk sorts them itself to match MRI, so the host need not. rbgo
supplies a Lister backed by its own Dir / File objects.
Walk¶
// Walk performs MRI Find.find's traversal over roots, driving the injected
// lister and invoking yield for every visited path in MRI's exact order.
func Walk(roots []string, lister Lister, yield func(path string) error, ignoreError bool) error
// WalkJoin is Walk with a caller-supplied path joiner, for hosts whose File.join
// differs from the default single-"/" rule.
func WalkJoin(roots []string, lister Lister, yield func(path string) error, ignoreError bool, join func(dir, name string) string) error
// ErrPrune, returned by the yield callback, prunes the current path: it was
// already yielded, but if a directory it is not descended into (== Find.prune).
var ErrPrune error
// Join is the default File.join-style single-segment joiner used by Walk.
func Join(dir, name string) string
// MissingPathError reports that a START path does not exist (MRI's Errno::ENOENT).
type MissingPathError struct{ Path string }
Worked example¶
err := find.Walk([]string{"a", "b"}, hostLister, func(p string) error {
if shouldSkipSubtree(p) {
return find.ErrPrune // == Find.prune
}
fmt.Println(p)
return nil
}, true)
MRI-faithful semantics¶
- Order. Each start path is yielded first, then a depth-first walk of its
contents. A directory's children are listed, sorted ascending byte-wise (so
Capital.txtprecedesa, and9precedes letters — MRI'sString#<=>), reversed, and unshifted onto a FIFO queue, giving depth-first ascending order. Find.prune. Returningfind.ErrPrunefrom the yield callback prunes the current path: it has already been yielded, but if it is a directory it is not descended into — the engine's analogue ofthrow :prune. Any other non-nil error fromyieldstops the walk and is returned to the caller.- Errors. A missing start path makes
Walkreturn a*MissingPathErrorbefore anything is yielded (MRI raisesErrno::ENOENT). A per-entryIsDir/Childrenfailure reached mid-walk is swallowed (entry skipped) whenignoreErroris true — MRI's default — and propagated otherwise.
MRI conformance¶
Correctness is defined by reference Ruby. The *_oracle* differential tests
build a real temp tree and diff our visit order against ruby -rfind on every
non-Windows CI lane. The deterministic tests use an in-memory Lister and need
no ruby, so the Windows and qemu arch lanes still pass the coverage gate.
Relationship to Ruby¶
go-ruby-find/find is standalone and reusable, and is the Find backend
bound into go-embedded-ruby by rbgo
— the same way go-ruby-regexp and
go-ruby-erb are bound. The dependency runs the
other way: this library has no dependency on the Ruby runtime, and Dir / File
stay host-side.