diff --git a/README.md b/README.md index a8ba369..6979973 100644 --- a/README.md +++ b/README.md @@ -395,3 +395,122 @@ func main() { } } ``` + +## COUNTRY CLASS + +## Methods +Below are the methods supported in this package. + +|Method Name|Description| +|---|---| +|func OpenCountryInfo(csvFile string) (*CI, error)|Expect a IP2Location Country Information CSV file. This database is free for download at https://www.ip2location.com/free/country-information| +|func (c *CI) GetCountryInfo(countryCode ...string) ([]CountryInfoRecord, error)|Returns the country information for specified country or all countries.| + +## Usage + +```go +package main + +import ( + "github.com/ip2location/ip2location-go" + "fmt" +) + +func main() { + c, err := ip2location.OpenCountryInfo("./IP2LOCATION-COUNTRY-INFORMATION.CSV") + + if err != nil { + fmt.Print(err) + return + } + + res, err := c.GetCountryInfo("US") + + if err != nil { + fmt.Print(err) + return + } + + fmt.Printf("country_code: %s\n", res[0].Country_code) + fmt.Printf("country_name: %s\n", res[0].Country_name) + fmt.Printf("country_alpha3_code: %s\n", res[0].Country_alpha3_code) + fmt.Printf("country_numeric_code: %s\n", res[0].Country_numeric_code) + fmt.Printf("capital: %s\n", res[0].Capital) + fmt.Printf("country_demonym: %s\n", res[0].Country_demonym) + fmt.Printf("total_area: %s\n", res[0].Total_area) + fmt.Printf("population: %s\n", res[0].Population) + fmt.Printf("idd_code: %s\n", res[0].Idd_code) + fmt.Printf("currency_code: %s\n", res[0].Currency_code) + fmt.Printf("currency_name: %s\n", res[0].Currency_name) + fmt.Printf("currency_symbol: %s\n", res[0].Currency_symbol) + fmt.Printf("lang_code: %s\n", res[0].Lang_code) + fmt.Printf("lang_name: %s\n", res[0].Lang_name) + fmt.Printf("cctld: %s\n", res[0].Cctld) + fmt.Print("==============================================\n") + + res2, err := c.GetCountryInfo() + + if err != nil { + fmt.Print(err) + return + } + + for _, v := range res2 { + fmt.Printf("country_code: %s\n", v.Country_code) + fmt.Printf("country_name: %s\n", v.Country_name) + fmt.Printf("country_alpha3_code: %s\n", v.Country_alpha3_code) + fmt.Printf("country_numeric_code: %s\n", v.Country_numeric_code) + fmt.Printf("capital: %s\n", v.Capital) + fmt.Printf("country_demonym: %s\n", v.Country_demonym) + fmt.Printf("total_area: %s\n", v.Total_area) + fmt.Printf("population: %s\n", v.Population) + fmt.Printf("idd_code: %s\n", v.Idd_code) + fmt.Printf("currency_code: %s\n", v.Currency_code) + fmt.Printf("currency_name: %s\n", v.Currency_name) + fmt.Printf("currency_symbol: %s\n", v.Currency_symbol) + fmt.Printf("lang_code: %s\n", v.Lang_code) + fmt.Printf("lang_name: %s\n", v.Lang_name) + fmt.Printf("cctld: %s\n", v.Cctld) + fmt.Print("==============================================\n") + } +} +``` + +## REGION CLASS + +## Methods +Below are the methods supported in this package. + +|Method Name|Description| +|---|---| +|func OpenRegionInfo(csvFile string) (*RI, error)|Expect a IP2Location ISO 3166-2 Subdivision Code CSV file. This database is free for download at https://www.ip2location.com/free/iso3166-2| +|func (r *RI) GetRegionCode(countryCode string, regionName string) (string, error)|Returns the region code for the supplied country code and region name.| + +## Usage + +```go +package main + +import ( + "github.com/ip2location/ip2location-go" + "fmt" +) + +func main() { + r, err := ip2location.OpenRegionInfo("./IP2LOCATION-ISO3166-2.CSV") + + if err != nil { + fmt.Print(err) + return + } + + res, err := r.GetRegionCode("US", "California") + + if err != nil { + fmt.Print(err) + return + } + + fmt.Printf("region code: %s\n", res) +} +``` \ No newline at end of file diff --git a/country.go b/country.go new file mode 100644 index 0000000..202ce31 --- /dev/null +++ b/country.go @@ -0,0 +1,129 @@ +package ip2location + +import ( + "encoding/csv" + "errors" + "os" +) + +// The CountryInfoRecord struct stores all of the available +// country info found in the country information CSV file. +type CountryInfoRecord struct { + Country_code string + Country_name string + Country_alpha3_code string + Country_numeric_code string + Capital string + Country_demonym string + Total_area string + Population string + Idd_code string + Currency_code string + Currency_name string + Currency_symbol string + Lang_code string + Lang_name string + Cctld string +} + +// The CI struct is the main object used to read the country information CSV. +type CI struct { + resultsArr []CountryInfoRecord + resultsMap map[string]CountryInfoRecord +} + +// OpenCountryInfo initializes with the path to the country information CSV file. +func OpenCountryInfo(csvFile string) (*CI, error) { + var ci = &CI{} + + _, err := os.Stat(csvFile) + if os.IsNotExist(err) { + return nil, errors.New("The CSV file '" + csvFile + "' is not found.") + } + + f, err := os.Open(csvFile) + if err != nil { + return nil, errors.New("Unable to read '" + csvFile + "'.") + } + + defer f.Close() + + csvReader := csv.NewReader(f) + data, err := csvReader.ReadAll() + if err != nil { + return nil, errors.New("Unable to read '" + csvFile + "'.") + } + + ci.resultsMap = make(map[string]CountryInfoRecord) + var headerArr []string + + for i, line := range data { + if i == 0 { // headers + for _, field := range line { + headerArr = append(headerArr, field) + } + } else { + var rec CountryInfoRecord + for j, field := range line { + switch headerArr[j] { + case "country_code": + rec.Country_code = field + case "country_name": + rec.Country_name = field + case "country_alpha3_code": + rec.Country_alpha3_code = field + case "country_numeric_code": + rec.Country_numeric_code = field + case "capital": + rec.Capital = field + case "country_demonym": + rec.Country_demonym = field + case "total_area": + rec.Total_area = field + case "population": + rec.Population = field + case "idd_code": + rec.Idd_code = field + case "currency_code": + rec.Currency_code = field + case "currency_name": + rec.Currency_name = field + case "currency_symbol": + rec.Currency_symbol = field + case "lang_code": + rec.Lang_code = field + case "lang_name": + rec.Lang_name = field + case "cctld": + rec.Cctld = field + } + } + if rec.Country_code == "" { + return nil, errors.New("Invalid country information CSV file.") + } + ci.resultsArr = append(ci.resultsArr, rec) + ci.resultsMap[rec.Country_code] = rec + } + } + return ci, nil +} + +// GetCountryInfo returns the country information for the specified country or all countries if not specified +func (c *CI) GetCountryInfo(countryCode ...string) ([]CountryInfoRecord, error) { + if len(c.resultsArr) == 0 { + return nil, errors.New("No record available.") + } + + if len(countryCode) == 1 { + cc := countryCode[0] + if rec, ok := c.resultsMap[cc]; ok { + var x []CountryInfoRecord + x = append(x, rec) + return x, nil // return record + } else { + return nil, errors.New("No record found.") + } + } else { + return c.resultsArr, nil // return all countries + } +} diff --git a/ip2location.go b/ip2location.go index 1da2198..66e8f35 100644 --- a/ip2location.go +++ b/ip2location.go @@ -144,7 +144,7 @@ var usagetype_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, var addresstype_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21} var category_position = [26]uint8{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22} -const api_version string = "9.4.1" +const api_version string = "9.5.0" var max_ipv4_range = big.NewInt(4294967295) var max_ipv6_range = big.NewInt(0) diff --git a/region.go b/region.go new file mode 100644 index 0000000..c06e330 --- /dev/null +++ b/region.go @@ -0,0 +1,96 @@ +package ip2location + +import ( + "encoding/csv" + "errors" + "os" + "strings" +) + +// The RegionInfoRecord struct stores all of the available +// region info found in the region information CSV file. +type RegionInfoRecord struct { + Country_code string + Name string + Code string +} + +// The RI struct is the main object used to read the region information CSV. +type RI struct { + resultsMap map[string][]RegionInfoRecord +} + +// OpenRegionInfo initializes with the path to the region information CSV file. +func OpenRegionInfo(csvFile string) (*RI, error) { + var ri = &RI{} + + _, err := os.Stat(csvFile) + if os.IsNotExist(err) { + return nil, errors.New("The CSV file '" + csvFile + "' is not found.") + } + + f, err := os.Open(csvFile) + if err != nil { + return nil, errors.New("Unable to read '" + csvFile + "'.") + } + + defer f.Close() + + csvReader := csv.NewReader(f) + data, err := csvReader.ReadAll() + if err != nil { + return nil, errors.New("Unable to read '" + csvFile + "'.") + } + + ri.resultsMap = make(map[string][]RegionInfoRecord) + var headerArr []string + var resultsArr []RegionInfoRecord + + for i, line := range data { + if i == 0 { // headers + for _, field := range line { + headerArr = append(headerArr, field) + } + } else { + var rec RegionInfoRecord + for j, field := range line { + switch headerArr[j] { + case "country_code": + rec.Country_code = field + case "subdivision_name": + rec.Name = field + case "code": + rec.Code = field + } + } + if rec.Name == "" { + return nil, errors.New("Invalid region information CSV file.") + } + resultsArr = append(resultsArr, rec) + } + } + for _, elem := range resultsArr { + if _, ok := ri.resultsMap[elem.Country_code]; !ok { + var arr []RegionInfoRecord + ri.resultsMap[elem.Country_code] = arr + } + ri.resultsMap[elem.Country_code] = append(ri.resultsMap[elem.Country_code], elem) + } + return ri, nil +} + +// GetRegionCode returns the region code for the specified country and region name +func (r *RI) GetRegionCode(countryCode string, regionName string) (string, error) { + if len(r.resultsMap) == 0 { + return "", errors.New("No record available.") + } + + if arr, ok := r.resultsMap[countryCode]; ok { + for _, elem := range arr { + if strings.ToUpper(elem.Name) == strings.ToUpper(regionName) { + return elem.Code, nil + } + } + } + return "", errors.New("No record found.") +}