Files
elmprodvpn/selective-vpn-api/app/vpn_locations_cache_parse.go

137 lines
2.6 KiB
Go

package app
import (
"fmt"
"strings"
)
func parseVPNLocationsOutput(raw string) ([]vpnLocationItem, error) {
stdout := stripANSI(raw)
type parsedLocation struct {
iso string
label string
name string
}
var parsed []parsedLocation
for _, ln := range strings.Split(stdout, "\n") {
line := strings.TrimSpace(ln)
if line == "" {
continue
}
if strings.HasPrefix(line, "ISO ") {
continue
}
if strings.HasPrefix(line, "VPN ") || strings.HasPrefix(line, "You can connect") {
continue
}
parts := strings.Fields(line)
if len(parts) < 4 {
continue
}
iso := strings.ToUpper(strings.TrimSpace(parts[0]))
ping := strings.TrimSpace(parts[len(parts)-1])
if len(iso) != 2 {
continue
}
if !isDigitsOnly(ping) {
continue
}
name := strings.Join(parts[1:len(parts)-1], " ")
label := fmt.Sprintf("%s %s (%s ms)", iso, name, ping)
parsed = append(parsed, parsedLocation{
iso: iso,
label: label,
name: name,
})
}
if len(parsed) == 0 {
trimmed := strings.TrimSpace(stdout)
if trimmed == "" {
return nil, fmt.Errorf("empty list-locations output")
}
return nil, fmt.Errorf("no locations parsed from output")
}
namesByISO := map[string][][]string{}
for _, it := range parsed {
namesByISO[it.iso] = append(namesByISO[it.iso], strings.Fields(it.name))
}
commonPrefixByISO := map[string][]string{}
for iso, names := range namesByISO {
if len(names) < 2 {
continue
}
pfx := commonPrefixTokens(names)
if len(pfx) > 0 {
commonPrefixByISO[iso] = pfx
}
}
var out []vpnLocationItem
for _, it := range parsed {
target := strings.ToUpper(strings.TrimSpace(it.iso))
if pfx := commonPrefixByISO[it.iso]; len(pfx) > 0 {
tokens := strings.Fields(it.name)
if len(tokens) > len(pfx) {
city := strings.TrimSpace(strings.Join(tokens[len(pfx):], " "))
if city != "" {
target = city
}
}
}
item, ok := normalizeVPNLocationItem(vpnLocationItem{
Label: it.label,
ISO: it.iso,
Target: target,
})
if !ok {
continue
}
out = append(out, item)
}
return out, nil
}
func commonPrefixTokens(items [][]string) []string {
if len(items) == 0 {
return nil
}
prefix := append([]string{}, items[0]...)
for i := 1; i < len(items); i++ {
row := items[i]
n := len(prefix)
if len(row) < n {
n = len(row)
}
j := 0
for j < n {
if !strings.EqualFold(prefix[j], row[j]) {
break
}
j++
}
prefix = prefix[:j]
if len(prefix) == 0 {
return nil
}
}
return prefix
}
func isDigitsOnly(s string) bool {
if s == "" {
return false
}
for _, ch := range s {
if ch < '0' || ch > '9' {
return false
}
}
return true
}