Refactor parseEntry

- add value/attribute checker(check missing space)
- allow multiple spaces
- sort attributes
- improve readablity
This commit is contained in:
MkQtS
2025-12-30 18:36:28 +08:00
parent 6b10d69246
commit bbd5b64219

96
main.go
View File

@@ -29,6 +29,12 @@ const (
RuleTypeInclude string = "include"
)
var (
TypeChecker = regexp.MustCompile(`^(domain|full|keyword|regexp|include)$`)
ValueChecker = regexp.MustCompile(`^[a-z0-9!\.-]+$`)
AttrChecker = regexp.MustCompile(`^[a-z0-9!-]+$`)
)
type Entry struct {
Type string
Value string
@@ -79,11 +85,6 @@ func (l *ParsedList) toProto() (*router.GeoSite, error) {
})
case RuleTypeRegexp:
// check regexp validity to avoid runtime error
_, err := regexp.Compile(entry.Value)
if err != nil {
return nil, fmt.Errorf("invalid regexp in list %s: %s", l.Name, entry.Value)
}
site.Domain = append(site.Domain, &router.Domain{
Type: router.Domain_Regex,
Value: entry.Value,
@@ -103,9 +104,6 @@ func (l *ParsedList) toProto() (*router.GeoSite, error) {
Value: entry.Value,
Attribute: entry.Attrs,
})
default:
return nil, fmt.Errorf("unknown domain type: %s", entry.Type)
}
}
return site, nil
@@ -123,60 +121,56 @@ func exportPlainTextList(list []string, refName string, pl *ParsedList) {
}
}
func parseDomain(domain string, entry *Entry) error {
kv := strings.Split(domain, ":")
func parseEntry(line string) (Entry, error) {
var entry Entry
parts := strings.Fields(line)
// Parse/Check type and value
rawTypeVal := parts[0]
kv := strings.Split(rawTypeVal, ":")
if len(kv) == 1 {
entry.Type = RuleTypeDomain
entry.Value = strings.ToLower(kv[0])
return nil
}
if len(kv) == 2 {
entry.Type = RuleTypeDomain // Default type
entry.Value = strings.ToLower(rawTypeVal)
} else if len(kv) == 2 {
entry.Type = strings.ToLower(kv[0])
if strings.EqualFold(entry.Type, RuleTypeRegexp) {
if entry.Type == RuleTypeRegexp {
entry.Value = kv[1]
} else {
entry.Value = strings.ToLower(kv[1])
}
return nil
} else {
return entry, fmt.Errorf("invalid format: %s", line)
}
return fmt.Errorf("invalid format: %s", domain)
}
func parseAttribute(attr string) (*router.Domain_Attribute, error) {
var attribute router.Domain_Attribute
if len(attr) == 0 || attr[0] != '@' {
return &attribute, fmt.Errorf("invalid attribute: %s", attr)
if !TypeChecker.MatchString(entry.Type) {
return entry, fmt.Errorf("invalid type: %s", entry.Type)
}
attribute.Key = strings.ToLower(attr[1:]) // Trim attribute prefix `@` character
attribute.TypedValue = &router.Domain_Attribute_BoolValue{BoolValue: true}
return &attribute, nil
}
func parseEntry(line string) (Entry, error) {
parts := strings.Split(line, " ")
var entry Entry
if len(parts) == 0 {
return entry, fmt.Errorf("empty entry")
}
if err := parseDomain(parts[0], &entry); err != nil {
return entry, err
}
for i := 1; i < len(parts); i++ {
attr, err := parseAttribute(parts[i])
if err != nil {
return entry, err
if entry.Type == RuleTypeRegexp {
if _, err := regexp.Compile(entry.Value); err != nil {
return entry, fmt.Errorf("invalid regexp: %s", entry.Value)
}
entry.Attrs = append(entry.Attrs, attr)
} else if !ValueChecker.MatchString(entry.Value) {
return entry, fmt.Errorf("invalid value: %s", entry.Value)
}
// Parse/Check attributes
for _, part := range parts[1:] {
if !strings.HasPrefix(part, "@") {
return entry, fmt.Errorf("invalid attribute: %s", part)
}
attrKey := strings.ToLower(part[1:]) // Trim attribute prefix `@` character
if !AttrChecker.MatchString(attrKey) {
return entry, fmt.Errorf("invalid attribute key: %s", attrKey)
}
entry.Attrs = append(entry.Attrs, &router.Domain_Attribute{
Key: attrKey,
TypedValue: &router.Domain_Attribute_BoolValue{BoolValue: true},
})
}
// Sort attributes
sort.Slice(entry.Attrs, func(i, j int) bool {
return entry.Attrs[i].Key < entry.Attrs[j].Key
})
return entry, nil
}