initial commit

This commit is contained in:
2026-08-31 22:25:00 +01:00
commit d13ee5cf13
7 changed files with 216 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# Sqan
A simple package to help scan files in a directory.
```go
s := NewScanner("my/dir").
WithExtensions("md", "txt").
WithIgnorePatterns("**/.private/**")
for file := range s.Scan() {
fmt.Println(file)
}
```
+37
View File
@@ -0,0 +1,37 @@
package sqan
import (
"errors"
"time"
)
type File struct {
RootDir string `json:"rootDir,omitempty"`
AbsolutePath string `json:"absolutePath,omitempty"`
RelativeDir string `json:"relativeDir,omitempty"`
RelativePath string `json:"relativePath,omitempty"`
Filename string `json:"filename,omitempty"`
Extension string `json:"extension,omitempty"`
Hash string `json:"hash,omitempty"`
Size int64 `json:"size,omitempty"`
Modified time.Time `json:"modified,omitempty"`
}
func (f *File) Process() error {
return errors.New("unimplemented")
}
func NewFile(rootDir, absPath string) *File {
relPath, relDir, filename, extension := ParsePath(rootDir, absPath)
f := &File{
RootDir: rootDir,
AbsolutePath: absPath,
RelativeDir: relDir,
RelativePath: relPath,
Filename: filename,
Extension: extension,
}
return f
}
+5
View File
@@ -0,0 +1,5 @@
module code.aneur.in/go/sqan
go 1.25.7
require github.com/gobwas/glob v1.0.0
+2
View File
@@ -0,0 +1,2 @@
github.com/gobwas/glob v1.0.0 h1:p+FKbLEIsK1yZ39/OINwFvqNb5oyPY4H8xcy6uYu8dg=
github.com/gobwas/glob v1.0.0/go.mod h1:oWCdo522i2P1n/hMXGNWs7yoV4wy/ciZuUIbvKj5rkc=
+27
View File
@@ -0,0 +1,27 @@
package sqan
import "strings"
func ParsePath(rootDir, absPath string) (relPath, relDir, filename, extension string) {
if absPath[0:len(rootDir)] != rootDir {
return
}
relPath = absPath[len(rootDir):]
if i := strings.LastIndexByte(relPath, '/'); i > -1 {
if i > 0 {
relDir = relPath[0:i]
} else {
relDir = "/"
}
filename = relPath[i+1:]
}
if i := strings.LastIndexByte(filename, '.'); i > -1 {
extension = strings.ToLower(filename[i+1:])
}
return
}
+5
View File
@@ -0,0 +1,5 @@
package sqan
type Result struct {
Files map[string]*File
}
+127
View File
@@ -0,0 +1,127 @@
package sqan
import (
"iter"
"os"
"path"
"slices"
"strings"
"github.com/gobwas/glob"
)
type Scanner struct {
rootDir string
extensions []string
ignorePatterns []string
compiledPatterns []*glob.Pattern
onError func(error)
}
func (s *Scanner) OnError(onError func(error)) *Scanner {
s.onError = onError
return s
}
func (s *Scanner) Scan() iter.Seq[*File] {
if err := s.compile(); err != nil {
s.onError(err)
}
return func(yield func(*File) bool) {
s.scan(s.rootDir, s.rootDir, yield)
}
}
func (s *Scanner) compile() error {
compiledPatterns := []*glob.Pattern{}
for _, text := range s.ignorePatterns {
pattern, err := glob.Compile(text)
if err != nil {
return err
}
compiledPatterns = append(compiledPatterns, pattern)
}
s.compiledPatterns = compiledPatterns
return nil
}
func (s *Scanner) scan(rootDir, dir string, yield func(*File) bool) {
entries, err := os.ReadDir(dir)
if err != nil {
s.onError(err)
return
}
for _, entry := range entries {
absPath := path.Join(dir, entry.Name())
ignore := false
for _, pattern := range s.compiledPatterns {
if pattern.Match(absPath) {
ignore = true
break
}
}
if ignore {
continue
}
if entry.IsDir() {
s.scan(rootDir, absPath, yield)
continue
}
f := NewFile(rootDir, absPath)
if len(s.extensions) > 0 && !slices.Contains(s.extensions, f.Extension) {
continue
}
info, err := entry.Info()
if err != nil {
s.onError(err)
continue
}
f.Size = info.Size()
f.Modified = info.ModTime()
if !yield(f) {
return
}
}
}
func (s *Scanner) WithExtensions(exts ...string) *Scanner {
lower := []string{}
for _, ext := range exts {
lower = append(lower, strings.ToLower(ext))
}
s.extensions = append(s.extensions, lower...)
return s
}
func (s *Scanner) WithIgnorePatterns(patterns ...string) *Scanner {
s.ignorePatterns = append(s.ignorePatterns, patterns...)
return s
}
func NewScanner(rootDir string) *Scanner {
if rootDir[0] != '/' {
cwd, _ := os.Getwd()
rootDir = path.Join(cwd, rootDir)
}
s := &Scanner{
rootDir: rootDir,
onError: func(err error) {},
}
return s
}