mirror of
https://github.com/navidrome/navidrome.git
synced 2026-05-03 06:51:16 +00:00
refactor: simplify criteria Expression interface
Replaced the Fields() type switch with a fields() method on the Expression interface, eliminating the need to update a central switch when adding new expression types. Removed the now-redundant criteriaExpression() marker method since fields() alone suffices to restrict the interface. Extracted a conjunction interface for the ChildPlaylistIds() lookup used by All and Any.
This commit is contained in:
parent
5d1c9530ab
commit
0ab10e819f
@ -9,7 +9,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Expression interface {
|
type Expression interface {
|
||||||
criteriaExpression()
|
fields() map[string]any
|
||||||
}
|
}
|
||||||
|
|
||||||
type Criteria struct {
|
type Criteria struct {
|
||||||
@ -53,7 +53,7 @@ func (c Criteria) ChildPlaylistIds() []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if parent, ok := c.Expression.(interface{ ChildPlaylistIds() (ids []string) }); ok {
|
if parent, ok := c.Expression.(conjunction); ok {
|
||||||
return parent.ChildPlaylistIds()
|
return parent.ChildPlaylistIds()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,12 +2,17 @@ package criteria
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
// Conjunctions need to implement this interface, to allow Criteria to extract child playlist IDs recursively
|
||||||
|
type conjunction interface {
|
||||||
|
ChildPlaylistIds() []string
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
All []Expression
|
All []Expression
|
||||||
And = All
|
And = All
|
||||||
)
|
)
|
||||||
|
|
||||||
func (All) criteriaExpression() {}
|
func (All) fields() map[string]any { return nil }
|
||||||
|
|
||||||
func (all All) MarshalJSON() ([]byte, error) {
|
func (all All) MarshalJSON() ([]byte, error) {
|
||||||
return marshalConjunction("all", all)
|
return marshalConjunction("all", all)
|
||||||
@ -22,7 +27,7 @@ type (
|
|||||||
Or = Any
|
Or = Any
|
||||||
)
|
)
|
||||||
|
|
||||||
func (Any) criteriaExpression() {}
|
func (Any) fields() map[string]any { return nil }
|
||||||
|
|
||||||
func (any Any) MarshalJSON() ([]byte, error) {
|
func (any Any) MarshalJSON() ([]byte, error) {
|
||||||
return marshalConjunction("any", any)
|
return marshalConjunction("any", any)
|
||||||
@ -35,128 +40,128 @@ func (any Any) ChildPlaylistIds() (ids []string) {
|
|||||||
type Is map[string]any
|
type Is map[string]any
|
||||||
type Eq = Is
|
type Eq = Is
|
||||||
|
|
||||||
func (Is) criteriaExpression() {}
|
|
||||||
|
|
||||||
func (is Is) MarshalJSON() ([]byte, error) {
|
func (is Is) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("is", is)
|
return marshalExpression("is", is)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (is Is) fields() map[string]any { return is }
|
||||||
|
|
||||||
type IsNot map[string]any
|
type IsNot map[string]any
|
||||||
|
|
||||||
func (IsNot) criteriaExpression() {}
|
func (isn IsNot) MarshalJSON() ([]byte, error) {
|
||||||
|
return marshalExpression("isNot", isn)
|
||||||
func (in IsNot) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalExpression("isNot", in)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Gt map[string]any
|
func (isn IsNot) fields() map[string]any { return isn }
|
||||||
|
|
||||||
func (Gt) criteriaExpression() {}
|
type Gt map[string]any
|
||||||
|
|
||||||
func (gt Gt) MarshalJSON() ([]byte, error) {
|
func (gt Gt) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("gt", gt)
|
return marshalExpression("gt", gt)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Lt map[string]any
|
func (gt Gt) fields() map[string]any { return gt }
|
||||||
|
|
||||||
func (Lt) criteriaExpression() {}
|
type Lt map[string]any
|
||||||
|
|
||||||
func (lt Lt) MarshalJSON() ([]byte, error) {
|
func (lt Lt) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("lt", lt)
|
return marshalExpression("lt", lt)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Before map[string]any
|
func (lt Lt) fields() map[string]any { return lt }
|
||||||
|
|
||||||
func (Before) criteriaExpression() {}
|
type Before map[string]any
|
||||||
|
|
||||||
func (bf Before) MarshalJSON() ([]byte, error) {
|
func (bf Before) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("before", bf)
|
return marshalExpression("before", bf)
|
||||||
}
|
}
|
||||||
|
|
||||||
type After Gt
|
func (bf Before) fields() map[string]any { return bf }
|
||||||
|
|
||||||
func (After) criteriaExpression() {}
|
type After Gt
|
||||||
|
|
||||||
func (af After) MarshalJSON() ([]byte, error) {
|
func (af After) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("after", af)
|
return marshalExpression("after", af)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Contains map[string]any
|
func (af After) fields() map[string]any { return af }
|
||||||
|
|
||||||
func (Contains) criteriaExpression() {}
|
type Contains map[string]any
|
||||||
|
|
||||||
func (ct Contains) MarshalJSON() ([]byte, error) {
|
func (ct Contains) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("contains", ct)
|
return marshalExpression("contains", ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotContains map[string]any
|
func (ct Contains) fields() map[string]any { return ct }
|
||||||
|
|
||||||
func (NotContains) criteriaExpression() {}
|
type NotContains map[string]any
|
||||||
|
|
||||||
func (nct NotContains) MarshalJSON() ([]byte, error) {
|
func (nct NotContains) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("notContains", nct)
|
return marshalExpression("notContains", nct)
|
||||||
}
|
}
|
||||||
|
|
||||||
type StartsWith map[string]any
|
func (nct NotContains) fields() map[string]any { return nct }
|
||||||
|
|
||||||
func (StartsWith) criteriaExpression() {}
|
type StartsWith map[string]any
|
||||||
|
|
||||||
func (sw StartsWith) MarshalJSON() ([]byte, error) {
|
func (sw StartsWith) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("startsWith", sw)
|
return marshalExpression("startsWith", sw)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sw StartsWith) fields() map[string]any { return sw }
|
||||||
|
|
||||||
type EndsWith map[string]any
|
type EndsWith map[string]any
|
||||||
|
|
||||||
func (EndsWith) criteriaExpression() {}
|
func (ew EndsWith) MarshalJSON() ([]byte, error) {
|
||||||
|
return marshalExpression("endsWith", ew)
|
||||||
func (sw EndsWith) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalExpression("endsWith", sw)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type InTheRange map[string]any
|
func (ew EndsWith) fields() map[string]any { return ew }
|
||||||
|
|
||||||
func (InTheRange) criteriaExpression() {}
|
type InTheRange map[string]any
|
||||||
|
|
||||||
func (itr InTheRange) MarshalJSON() ([]byte, error) {
|
func (itr InTheRange) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("inTheRange", itr)
|
return marshalExpression("inTheRange", itr)
|
||||||
}
|
}
|
||||||
|
|
||||||
type InTheLast map[string]any
|
func (itr InTheRange) fields() map[string]any { return itr }
|
||||||
|
|
||||||
func (InTheLast) criteriaExpression() {}
|
type InTheLast map[string]any
|
||||||
|
|
||||||
func (itl InTheLast) MarshalJSON() ([]byte, error) {
|
func (itl InTheLast) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("inTheLast", itl)
|
return marshalExpression("inTheLast", itl)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NotInTheLast map[string]any
|
func (itl InTheLast) fields() map[string]any { return itl }
|
||||||
|
|
||||||
func (NotInTheLast) criteriaExpression() {}
|
type NotInTheLast map[string]any
|
||||||
|
|
||||||
func (nitl NotInTheLast) MarshalJSON() ([]byte, error) {
|
func (nitl NotInTheLast) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("notInTheLast", nitl)
|
return marshalExpression("notInTheLast", nitl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nitl NotInTheLast) fields() map[string]any { return nitl }
|
||||||
|
|
||||||
func startOfPeriod(numDays int64, from time.Time) string {
|
func startOfPeriod(numDays int64, from time.Time) string {
|
||||||
return from.Add(time.Duration(-24*numDays) * time.Hour).Format("2006-01-02")
|
return from.Add(time.Duration(-24*numDays) * time.Hour).Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
||||||
type InPlaylist map[string]any
|
type InPlaylist map[string]any
|
||||||
|
|
||||||
func (InPlaylist) criteriaExpression() {}
|
|
||||||
|
|
||||||
func (ipl InPlaylist) MarshalJSON() ([]byte, error) {
|
func (ipl InPlaylist) MarshalJSON() ([]byte, error) {
|
||||||
return marshalExpression("inPlaylist", ipl)
|
return marshalExpression("inPlaylist", ipl)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ipl InPlaylist) fields() map[string]any { return ipl }
|
||||||
|
|
||||||
type NotInPlaylist map[string]any
|
type NotInPlaylist map[string]any
|
||||||
|
|
||||||
func (NotInPlaylist) criteriaExpression() {}
|
func (nipl NotInPlaylist) MarshalJSON() ([]byte, error) {
|
||||||
|
return marshalExpression("notInPlaylist", nipl)
|
||||||
func (ipl NotInPlaylist) MarshalJSON() ([]byte, error) {
|
|
||||||
return marshalExpression("notInPlaylist", ipl)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (nipl NotInPlaylist) fields() map[string]any { return nipl }
|
||||||
|
|
||||||
func extractPlaylistIds(inputRule any) (ids []string) {
|
func extractPlaylistIds(inputRule any) (ids []string) {
|
||||||
var id string
|
var id string
|
||||||
var ok bool
|
var ok bool
|
||||||
|
|||||||
@ -32,41 +32,6 @@ func Walk(expr Expression, visit Visitor) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fields returns field values for leaf expressions only.
|
|
||||||
// Use Walk to traverse All and Any expressions before calling Fields.
|
|
||||||
func Fields(expr Expression) map[string]any {
|
func Fields(expr Expression) map[string]any {
|
||||||
switch e := expr.(type) {
|
return expr.fields()
|
||||||
case Is:
|
|
||||||
return map[string]any(e)
|
|
||||||
case IsNot:
|
|
||||||
return map[string]any(e)
|
|
||||||
case Gt:
|
|
||||||
return map[string]any(e)
|
|
||||||
case Lt:
|
|
||||||
return map[string]any(e)
|
|
||||||
case Before:
|
|
||||||
return map[string]any(e)
|
|
||||||
case After:
|
|
||||||
return map[string]any(Gt(e))
|
|
||||||
case Contains:
|
|
||||||
return map[string]any(e)
|
|
||||||
case NotContains:
|
|
||||||
return map[string]any(e)
|
|
||||||
case StartsWith:
|
|
||||||
return map[string]any(e)
|
|
||||||
case EndsWith:
|
|
||||||
return map[string]any(e)
|
|
||||||
case InTheRange:
|
|
||||||
return map[string]any(e)
|
|
||||||
case InTheLast:
|
|
||||||
return map[string]any(e)
|
|
||||||
case NotInTheLast:
|
|
||||||
return map[string]any(e)
|
|
||||||
case InPlaylist:
|
|
||||||
return map[string]any(e)
|
|
||||||
case NotInPlaylist:
|
|
||||||
return map[string]any(e)
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
type unknownExpression struct{}
|
type unknownExpression struct{}
|
||||||
|
|
||||||
func (unknownExpression) criteriaExpression() {}
|
func (unknownExpression) fields() map[string]any { return nil }
|
||||||
|
|
||||||
var _ = Describe("Walk", func() {
|
var _ = Describe("Walk", func() {
|
||||||
It("visits the expression tree depth-first", func() {
|
It("visits the expression tree depth-first", func() {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user