Compare commits
2 Commits
4d11e2867b
...
f6252ef22d
Author | SHA1 | Date | |
---|---|---|---|
f6252ef22d | |||
adbc78bbf7 |
44
src/main.go
Normal file
44
src/main.go
Normal file
@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/realsunyz/geofeed-tools/plugin/validate"
|
||||
)
|
||||
|
||||
const version = "1.0.0"
|
||||
|
||||
var help = fmt.Sprintf(
|
||||
"Geofeed Tools Version %s (compiled to binary)\n"+
|
||||
"Usage: ./geofeed-tools [flags] [filepath]\n\n"+
|
||||
"Flags:\n"+
|
||||
" -h Show this help message\n"+
|
||||
" -v Validate a geofeed file\n", version)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Print(help)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
flag := os.Args[1]
|
||||
|
||||
if flag == "-h" {
|
||||
fmt.Print(help)
|
||||
os.Exit(1)
|
||||
} else if flag == "-v" {
|
||||
if len(os.Args) != 3 {
|
||||
fmt.Println(
|
||||
"Usage: ./geofeed-tools -v [filepath]")
|
||||
os.Exit(1)
|
||||
}
|
||||
filePath := os.Args[2]
|
||||
validate.Execute(filePath)
|
||||
} else {
|
||||
fmt.Printf(
|
||||
"Error: Invalid flag \"%s\"\n"+
|
||||
"Use \"./geofeed-tools -h\" to see available flags.\n", os.Args[1])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
@ -2,7 +2,6 @@ package validate
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"embed"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@ -11,14 +10,12 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/realsunyz/geofeed-tools/plugin/color"
|
||||
"github.com/realsunyz/geofeed-tools/plugin/isocode"
|
||||
)
|
||||
|
||||
//go:embed data/*.json
|
||||
var dataFS embed.FS
|
||||
|
||||
const (
|
||||
countryFile = "data/iso3166-1.json"
|
||||
subdivisionFile = "data/iso3166-2.json"
|
||||
countryFile = "iso3166-1.json"
|
||||
subdivisionFile = "iso3166-2.json"
|
||||
)
|
||||
|
||||
type Country struct {
|
||||
@ -35,7 +32,7 @@ var countries []Country
|
||||
var subdivisions SubdivisionMap
|
||||
|
||||
func readJSON(filename string, v interface{}) error {
|
||||
data, err := dataFS.ReadFile(filename)
|
||||
data, err := isocode.DataFS.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -151,7 +148,7 @@ func Execute(filePath string) {
|
||||
|
||||
scanner, file, err := readCSV(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to %v\n", err)
|
||||
fmt.Printf("Failed to open geofeed file: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
|
164
src/validator.go
164
src/validator.go
@ -1,164 +0,0 @@
|
||||
// validator.go
|
||||
// author: Yanzheng SUN (realSunyz)
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"embed"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
Reset = "\033[0m"
|
||||
Red = "\033[31m"
|
||||
Green = "\033[32m"
|
||||
Yellow = "\033[33m"
|
||||
Magenta = "\033[35m"
|
||||
Cyan = "\033[36m"
|
||||
)
|
||||
|
||||
type Country struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type Subdivision struct {
|
||||
Name string `json:"name"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
|
||||
type SubdivisionMap map[string][]Subdivision
|
||||
|
||||
//go:embed data/*.json
|
||||
var dataFS embed.FS
|
||||
|
||||
func readJSON(filename string, v interface{}) error {
|
||||
data, err := dataFS.ReadFile(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return json.Unmarshal(data, v)
|
||||
}
|
||||
|
||||
func validateGeofeed(geofeedFile string, countries []Country, subdivisions SubdivisionMap) []string {
|
||||
|
||||
countryMap := make(map[string]bool)
|
||||
for _, country := range countries {
|
||||
countryMap[country.Code] = true
|
||||
}
|
||||
|
||||
file, err := os.Open(geofeedFile)
|
||||
if err != nil {
|
||||
return []string{fmt.Sprintf("Failed to open geofeed file: %v", err)}
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
lineNumber := 0
|
||||
var errors []string
|
||||
|
||||
for scanner.Scan() {
|
||||
lineNumber++
|
||||
line := scanner.Text()
|
||||
|
||||
// Skip blank lines
|
||||
if strings.TrimSpace(line) == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip comment lines
|
||||
if strings.HasPrefix(strings.TrimSpace(line), "#") {
|
||||
continue
|
||||
}
|
||||
|
||||
reader := csv.NewReader(strings.NewReader(line))
|
||||
reader.FieldsPerRecord = -1
|
||||
record, _ := reader.Read()
|
||||
|
||||
// Validate geofeed format (prefix, country_code, subdivision_code, city, postal_code)
|
||||
if len(record) < 5 {
|
||||
errors = append(errors, fmt.Sprintf("Line %d: invalid "+Magenta+"geofeed "+Reset+"format", lineNumber))
|
||||
continue
|
||||
}
|
||||
prefix, countryCode, subdivisionCode := record[0], record[1], record[2]
|
||||
|
||||
// Validate prefix format
|
||||
_, _, prefixErr := net.ParseCIDR(prefix)
|
||||
if prefixErr != nil {
|
||||
errors = append(errors, fmt.Sprintf("Line %d: invalid "+Magenta+"prefix "+Reset+"format ("+Cyan+"%s"+Reset+")", lineNumber, prefix))
|
||||
// continue
|
||||
}
|
||||
|
||||
// Skip empty country code
|
||||
if countryCode == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate country code
|
||||
if !countryMap[countryCode] {
|
||||
errors = append(errors, fmt.Sprintf("Line %d: invalid "+Magenta+"country code "+Reset+"("+Cyan+"%s"+Reset+")", lineNumber, countryCode))
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip empty subdivision code
|
||||
if subdivisionCode == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Validate subdivision code
|
||||
validSubdivision := false
|
||||
for _, subdivision := range subdivisions[countryCode] {
|
||||
if subdivision.Code == subdivisionCode {
|
||||
validSubdivision = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validSubdivision {
|
||||
errors = append(errors, fmt.Sprintf("Line %d: invalid "+Magenta+"subdivision code "+Reset+"("+Cyan+"%s"+Reset+") "+"for country ("+Cyan+"%s"+Reset+")", lineNumber, subdivisionCode, countryCode))
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func main() {
|
||||
countryFile := "data/iso3166-1.json"
|
||||
subdivisionFile := "data/iso3166-2.json"
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Usage: ./geofeed-validator <path_to_geofeed.csv>")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
geofeedFile := os.Args[1]
|
||||
|
||||
var countries []Country
|
||||
var subdivisions SubdivisionMap
|
||||
|
||||
if err := readJSON(countryFile, &countries); err != nil {
|
||||
fmt.Printf("Failed to load countries: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := readJSON(subdivisionFile, &subdivisions); err != nil {
|
||||
fmt.Printf("Failed to load subdivisions: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
errors := validateGeofeed(geofeedFile, countries, subdivisions)
|
||||
|
||||
if len(errors) == 0 {
|
||||
fmt.Println("Congratulations! Your geofeed file is " + Green + "VALID" + Reset + ".")
|
||||
} else {
|
||||
fmt.Println("Your geofeed file is " + Red + "INVALID" + Reset + ":")
|
||||
for _, err := range errors {
|
||||
fmt.Println("- " + err)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user