support some filtering
This commit is contained in:
114
internal/selectors.go
Normal file
114
internal/selectors.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func Any() Selector {
|
||||
return func(r Record) bool { return true }
|
||||
}
|
||||
|
||||
func And(a, b Selector) Selector {
|
||||
return func(r Record) bool {
|
||||
return a(r) && b(r)
|
||||
}
|
||||
}
|
||||
|
||||
func OnlyNature(n Nature) Selector {
|
||||
return func(r Record) bool {
|
||||
return r.Nature() == n
|
||||
}
|
||||
}
|
||||
|
||||
func OnlyAssetCountry(c int64) Selector {
|
||||
return func(r Record) bool {
|
||||
return r.AssetCountry() == c
|
||||
}
|
||||
}
|
||||
|
||||
// selectorParser is a function that parses a selector value string into a Selector
|
||||
type selectorParser func(string) (Selector, error)
|
||||
|
||||
// parsers maps selector keys to their parser functions
|
||||
var parsers = map[string]selectorParser{
|
||||
"code": parseNature,
|
||||
"assetCountry": parseAssetCountry,
|
||||
}
|
||||
|
||||
func parseNature(value string) (Selector, error) {
|
||||
if value == "" {
|
||||
return nil, fmt.Errorf("code selector requires a non-empty value")
|
||||
}
|
||||
nature := Nature(value)
|
||||
if !nature.Valid() {
|
||||
return nil, fmt.Errorf("invalid nature code %q", value)
|
||||
}
|
||||
return OnlyNature(nature), nil
|
||||
}
|
||||
|
||||
func parseAssetCountry(value string) (Selector, error) {
|
||||
if value == "" {
|
||||
return nil, fmt.Errorf("assetCountry selector requires a non-empty value")
|
||||
}
|
||||
i, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("assetCountry value must be a valid integer: %w", err)
|
||||
}
|
||||
return OnlyAssetCountry(i), nil
|
||||
}
|
||||
|
||||
// ParseSelectors parses a list of selector strings into a composed Selector.
|
||||
// Each selector string must be in the format "key:value" where key is one of:
|
||||
// - code: filter by nature (e.g., "code:G01")
|
||||
// - assetCountry: filter by asset country code (e.g., "assetCountry:840")
|
||||
//
|
||||
// Multiple selectors are combined with AND logic.
|
||||
// If no selectors are provided, returns Any() which matches all records.
|
||||
func ParseSelectors(sl []string) (Selector, error) {
|
||||
if len(sl) == 0 {
|
||||
return Any(), nil
|
||||
}
|
||||
|
||||
// Parse the first selector
|
||||
first, err := parseSingleSelector(sl[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there's only one, return it
|
||||
if len(sl) == 1 {
|
||||
return first, nil
|
||||
}
|
||||
|
||||
// Recursively parse the rest and combine with AND
|
||||
rest, err := ParseSelectors(sl[1:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return And(first, rest), nil
|
||||
}
|
||||
|
||||
func parseSingleSelector(s string) (Selector, error) {
|
||||
key, value, found := strings.Cut(s, ":")
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid selector format %q: must be 'key:value'", s)
|
||||
}
|
||||
|
||||
parser, ok := parsers[key]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unknown selector key %q: supported keys are %v", key, supportedKeys())
|
||||
}
|
||||
|
||||
return parser(value)
|
||||
}
|
||||
|
||||
func supportedKeys() []string {
|
||||
keys := make([]string, 0, len(parsers))
|
||||
for k := range parsers {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
Reference in New Issue
Block a user