From b9e71677b9ee97de157bd586b8fea4bd55aa84a4 Mon Sep 17 00:00:00 2001 From: Antonio De Lucreziis Date: Mon, 16 Jan 2023 02:32:58 +0100 Subject: [PATCH] fix: using files from context in source --- operation/source.go | 11 ++---- path/path.go | 91 ++++++++++++++++++++++++--------------------- path/path_test.go | 35 +++++++++++++++++ 3 files changed, 87 insertions(+), 50 deletions(-) diff --git a/operation/source.go b/operation/source.go index 1619526..f86a2ca 100644 --- a/operation/source.go +++ b/operation/source.go @@ -56,11 +56,6 @@ func (op *Source) Configure(config map[string]any) error { var _ cabret.ListOperation = &Source{} func (op Source) ProcessList(ctx *cabret.Context, contents []cabret.Content) ([]cabret.Content, error) { - files, err := cabret.FindFiles() - if err != nil { - return nil, err - } - matches := []cabret.MatchResult{} for _, rawPattern := range op.Patterns { @@ -69,10 +64,10 @@ func (op Source) ProcessList(ctx *cabret.Context, contents []cabret.Content) ([] return nil, err } - for _, f := range files { - if ok, captures, _ := pattern.Match(f); ok { + for _, file := range ctx.Files { + if ok, captures, _ := pattern.Match(file); ok { matches = append(matches, cabret.MatchResult{ - File: f, + File: file, Captures: captures, }) } diff --git a/path/path.go b/path/path.go index 1ee9639..0e447ea 100644 --- a/path/path.go +++ b/path/path.go @@ -16,6 +16,11 @@ type PathPart interface { regex() string } +var _ PathPart = PathLiteral{} +var _ PathPart = PathShortPattern{} +var _ PathPart = PathLongPattern{} +var _ PathPart = PathString{} + type PathLiteral struct { LongAny bool `parser:" @'**'"` ShortAny bool `parser:"| @'*' "` @@ -25,7 +30,7 @@ type PathLiteral struct { func (p PathLiteral) regex() string { switch { case p.Slash: - return "/" + return regexp.QuoteMeta("/") case p.ShortAny: return "([^/]+?)" case p.LongAny: @@ -52,7 +57,7 @@ func (p PathLongPattern) regex() string { } type PathString struct { - Value string `parser:"@Ident"` + Value string `parser:"@(Ident|Rune)+"` } func (p PathString) regex() string { @@ -63,18 +68,44 @@ type Pattern struct { Parts []PathPart `parser:"@@+"` } -func (pp Pattern) regex() string { - sb := &strings.Builder{} +var parser = participle.MustBuild[Pattern]( + participle.Union[PathPart]( + &PathLiteral{}, + &PathLongPattern{}, + &PathShortPattern{}, + &PathString{}, + ), + participle.Lexer( + lexer.MustSimple([]lexer.SimpleRule{ + {Name: "Slash", Pattern: `/`}, + {Name: "LongAny", Pattern: `\*\*`}, + {Name: "ShortAny", Pattern: `\*`}, + {Name: "PatternLongOpen", Pattern: `{{`}, + {Name: "PatternLongClose", Pattern: `}}`}, + {Name: "PatternShortOpen", Pattern: `{`}, + {Name: "PatternShortClose", Pattern: `}`}, + {Name: "Ident", Pattern: `[a-zA-Z][a-zA-Z0-9\_]*`}, + {Name: "Rune", Pattern: `[^/{}]+`}, + }), + ), +) - for _, p := range pp.Parts { - sb.WriteString(p.regex()) +func ParsePattern(path string) (*Pattern, error) { + return parser.ParseString("", path) +} + +func MustParsePattern(path string) *Pattern { + p, err := ParsePattern(path) + if err != nil { + panic(err) } - return sb.String() + return p } -func (pp Pattern) Match(s string) (bool, map[string]string, error) { - r, err := regexp.Compile("^" + pp.regex() + "$") +// Match takes a path "s" and matches it against this pattern and returns whether it matched and in that case a map containing all captures for this pattern. This will throw an error if it can't compile the internal regex (should never happen). +func (p Pattern) Match(s string) (bool, map[string]string, error) { + r, err := regexp.Compile("^" + p.regex() + "$") if err != nil { return false, nil, err } @@ -96,26 +127,15 @@ func (pp Pattern) Match(s string) (bool, map[string]string, error) { return true, ctx, nil } -var parser = participle.MustBuild[Pattern]( - participle.Union[PathPart]( - &PathLiteral{}, - &PathLongPattern{}, - &PathShortPattern{}, - &PathString{}, - ), - participle.Lexer( - lexer.MustSimple([]lexer.SimpleRule{ - {Name: "Slash", Pattern: `/`}, - {Name: "LongAny", Pattern: `\*\*`}, - {Name: "ShortAny", Pattern: `\*`}, - {Name: "PatternLongOpen", Pattern: `{{`}, - {Name: "PatternLongClose", Pattern: `}}`}, - {Name: "PatternShortOpen", Pattern: `{`}, - {Name: "PatternShortClose", Pattern: `}`}, - {Name: "Ident", Pattern: `[^/{}]+`}, - }), - ), -) +func (p Pattern) regex() string { + sb := &strings.Builder{} + + for _, part := range p.Parts { + sb.WriteString(part.regex()) + } + + return sb.String() +} func RenderTemplate(tmpl string, ctx map[string]string) string { s := tmpl @@ -124,16 +144,3 @@ func RenderTemplate(tmpl string, ctx map[string]string) string { } return s } - -func ParsePattern(path string) (*Pattern, error) { - return parser.ParseString("", path) -} - -func MustParsePattern(path string) *Pattern { - p, err := ParsePattern(path) - if err != nil { - panic(err) - } - - return p -} diff --git a/path/path_test.go b/path/path_test.go index 7edb0a4..a6cebca 100644 --- a/path/path_test.go +++ b/path/path_test.go @@ -2,6 +2,7 @@ package path_test import ( "testing" + "unicode" "github.com/aziis98/cabret/path" "gotest.tools/assert" @@ -170,3 +171,37 @@ func TestMultipleGroups(t *testing.T) { }) }) } + +func FuzzMatch(f *testing.F) { + f.Add(`{a}.txt`) + f.Add(`{a}/{b}.txt`) + f.Add(`src/{{a}}/index.html`) + f.Add(`/a/{b}/c-{d}.go`) + + path.MustParsePattern(`{a}.txt`) + path.MustParsePattern(`{a}/{b}.txt`) + path.MustParsePattern(`src/{{a}}/index.html`) + path.MustParsePattern(`/a/{b}/c-{d}.go`) + + f.Fuzz(func(t *testing.T, pattern string) { + goodPattern := make([]rune, 0, len(pattern)) + + // filter in only ascii runes + for _, c := range pattern { + if c <= unicode.MaxASCII { + goodPattern = append(goodPattern, c) + } + } + + pattern = string(goodPattern) + + p, err := path.ParsePattern(pattern) + if err != nil { + return + } + + if _, _, err := p.Match(""); err != nil { + t.Errorf(`invalid regexp generated by pattern %q`, pattern) + } + }) +}