package app import ( "fmt" "sort" "strconv" "strings" ) // parseAppMarkRules extracts "target:id" keys from output_apps chain dump. func parseAppMarkRules(out string) []string { var keys []string for _, line := range strings.Split(out, "\n") { if key, ok := parseTrafficAppMarkRuleLine(line); ok { keys = append(keys, key) } } sort.Strings(keys) return dedupeSortedStrings(keys) } func parseTrafficAppMarkRuleLine(line string) (string, bool) { // comment "svpn_appmark:vpn:123" i := strings.Index(line, appMarkCommentPrefix+":") if i < 0 { return "", false } rest := line[i:] end := len(rest) for j := 0; j < len(rest); j++ { ch := rest[j] if ch == '"' || ch == ' ' || ch == '\t' { end = j break } } tag := rest[:end] parts := strings.Split(tag, ":") if len(parts) != 3 { return "", false } tgt := strings.ToLower(strings.TrimSpace(parts[1])) idRaw := strings.TrimSpace(parts[2]) if tgt != "vpn" && tgt != "direct" { return "", false } id, err := strconv.ParseUint(idRaw, 10, 64) if err != nil || id == 0 { return "", false } return fmt.Sprintf("%s:%d", tgt, id), true } func dedupeSortedStrings(in []string) []string { if len(in) == 0 { return nil } out := in[:0] last := "" for _, it := range in { if it == last { continue } out = append(out, it) last = it } return out }