Skip to content

Commit e264e9a

Browse files
committed
feat: add new version for common code
1 parent 62be2c1 commit e264e9a

File tree

7 files changed

+271
-0
lines changed

7 files changed

+271
-0
lines changed

carrier.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package gophonenumbers
2+
3+
type CarrierNumberInfo struct {
4+
E164Number string `json:"e164Number"`
5+
MobileCountryCode string `json:"mobileCountryCode,omitempty"`
6+
MobileNetworkCode string `json:"mobileNetworkCode,omitempty"`
7+
Name string `json:"name,omitempty"`
8+
LineType string `json:"lineType,omitempty"`
9+
ErrorCode string `json:"errorCode,omitempty"`
10+
}

components.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package gophonenumbers
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
var rxNANPFormat = regexp.MustCompile(`^\+(1([0-9]{3})([0-9]{3})([0-9]{4}))$`)
11+
12+
type Components struct {
13+
E164Number string
14+
E164NumberUint uint
15+
RegionCode string
16+
CountryCode uint
17+
NANPAreaCode uint // NPA - Numbering plan area code
18+
NANPExchangeCode uint // NXX - Central office (exchange) code
19+
NANPLineNumber uint // xxxx - Line number or subscriber number
20+
}
21+
22+
func (num *Number) NANPComponents() (Components, error) {
23+
num.E164Number = strings.TrimSpace(num.E164Number)
24+
comp := Components{E164Number: num.E164Number}
25+
m := rxNANPFormat.FindAllStringSubmatch(num.E164Number, -1)
26+
if len(m) == 0 {
27+
return comp, fmt.Errorf("number is not E.164 [%s]", num.E164Number)
28+
}
29+
comp.CountryCode = 1
30+
e164int, err := strconv.Atoi(m[0][1])
31+
if err != nil {
32+
panic(fmt.Sprintf("ParseE164 [%v]", err.Error()))
33+
}
34+
comp.E164NumberUint = uint(e164int)
35+
areaCode, err := strconv.Atoi(m[0][2])
36+
if err != nil {
37+
panic(fmt.Sprintf("ParseE164 [%v]", err.Error()))
38+
}
39+
comp.NANPAreaCode = uint(areaCode)
40+
exchangeCode, err := strconv.Atoi(m[0][3])
41+
if err != nil {
42+
panic(fmt.Sprintf("ParseE164 [%v]", err.Error()))
43+
}
44+
comp.NANPExchangeCode = uint(exchangeCode)
45+
lineNumber, err := strconv.Atoi(m[0][4])
46+
if err != nil {
47+
panic(fmt.Sprintf("ParseE164 [%v]", err.Error()))
48+
}
49+
comp.NANPLineNumber = uint(lineNumber)
50+
return comp, nil
51+
}

components_test.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package gophonenumbers
2+
3+
import (
4+
"testing"
5+
)
6+
7+
var parseE164Tests = []struct {
8+
e164Number string
9+
e164Uint uint
10+
countryCode uint
11+
areaCode uint
12+
exchangeCode uint
13+
lineNumber uint
14+
}{
15+
{"+16505550100", uint(16505550100), uint(1), uint(650), uint(555), uint(100)},
16+
{"+14155550199", uint(14155550199), uint(1), uint(415), uint(555), uint(199)},
17+
}
18+
19+
func TestParseE164(t *testing.T) {
20+
for _, tt := range parseE164Tests {
21+
num := Number{E164Number: tt.e164Number}
22+
got, err := num.NANPComponents()
23+
if err != nil {
24+
t.Errorf("Number.NANPComponents(\"%s\") Want: [%v] Got: [%v]",
25+
tt.e164Number, tt.e164Uint, got.E164NumberUint)
26+
}
27+
if got.E164NumberUint != tt.e164Uint {
28+
t.Errorf("Number.NANPComponents() Want: [%v] Got: [%v]",
29+
tt.e164Uint, got.E164NumberUint)
30+
}
31+
if got.CountryCode != tt.countryCode {
32+
t.Errorf("Number.NANPComponents() Want: [%v] Got: [%v]",
33+
tt.countryCode, got.CountryCode)
34+
}
35+
if got.NANPAreaCode != tt.areaCode {
36+
t.Errorf("Number.NANPComponents(\"%s\") Want: [%v] Got: [%v]",
37+
tt.e164Number, tt.areaCode, got.NANPAreaCode)
38+
}
39+
if got.NANPExchangeCode != tt.exchangeCode {
40+
t.Errorf("Number.NANPComponents(\"%s\") Want: [%v] Got: [%v]",
41+
tt.e164Number, tt.exchangeCode, got.NANPExchangeCode)
42+
}
43+
if got.NANPLineNumber != tt.lineNumber {
44+
t.Errorf("Number.NANPComponents(\"%s\") Want: [%v] Got: [%v]",
45+
tt.e164Number, tt.lineNumber, got.NANPLineNumber)
46+
}
47+
}
48+
}

constants.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package gophonenumbers
2+
3+
const (
4+
CarrierATT = "att"
5+
CarrierSprint = "sprint"
6+
CarrierTMobile = "tmobile"
7+
CarrierVerizon = "verizon"
8+
9+
LineTypeLocal = "local"
10+
LineTypeMobile = "mobile"
11+
LineTypeTollFree = "tollfree"
12+
13+
SourceEkata = "ekata"
14+
SourceNumverify = "numverify"
15+
SourcePlivo = "plivo"
16+
SourceTwilio = "twilio"
17+
)

lookup.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package gophonenumbers
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"sort"
7+
"time"
8+
)
9+
10+
type Lookup struct {
11+
CarrierNumberInfo CarrierNumberInfo `json:"carrierNumberInfo"`
12+
LookupSource string `json:"lookupSource"`
13+
LookupTime time.Time `json:"lookupTime"`
14+
LookupResponse map[string]interface{} `json:"lookupResponse"`
15+
}
16+
17+
type LookupSet struct {
18+
LookupMap map[string]Lookup `json:"lookupMap"`
19+
}
20+
21+
func NewLookupSet() LookupSet {
22+
return LookupSet{
23+
LookupMap: map[string]Lookup{}}
24+
}
25+
26+
func (set LookupSet) Add(lookup Lookup) {
27+
if set.LookupMap == nil {
28+
set.LookupMap = map[string]Lookup{}
29+
}
30+
set.LookupMap[lookup.LookupTime.Format(time.RFC3339)] = lookup
31+
}
32+
33+
func (set LookupSet) Latest(source string) (Lookup, error) {
34+
if set.LookupMap == nil || len(set.LookupMap) == 0 {
35+
return Lookup{}, errors.New("no latest lookup. lookupSet is empty.")
36+
}
37+
times := []string{}
38+
for dtKey, lookup := range set.LookupMap {
39+
if len(source) > 0 && lookup.LookupSource != source {
40+
continue
41+
}
42+
times = append(times, dtKey)
43+
}
44+
if len(times) == 0 {
45+
return Lookup{}, fmt.Errorf("no lookup for source [%s]", string(source))
46+
}
47+
if len(times) == 1 {
48+
return set.LookupMap[times[0]], nil
49+
}
50+
sort.Strings(times)
51+
return set.LookupMap[times[len(times)-1]], nil
52+
}
53+
54+
func (set LookupSet) Validate() {
55+
if set.LookupMap == nil || len(set.LookupMap) == 0 {
56+
return
57+
}
58+
for dtKey, lookup := range set.LookupMap {
59+
_, err := time.Parse(time.RFC3339, dtKey)
60+
if err != nil {
61+
delete(set.LookupMap, dtKey)
62+
set.LookupMap[lookup.LookupTime.Format(time.RFC3339)] = lookup
63+
}
64+
}
65+
}

number.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package gophonenumbers
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
type Number struct {
9+
E164Number string `json:"e164Number"`
10+
CountryCode string `json:"countryCode"`
11+
CarrierNumberInfo CarrierNumberInfo `json:"carrier"`
12+
Lookups LookupSet `json:"lookups"`
13+
}
14+
15+
func NewNumber() Number {
16+
return Number{
17+
Lookups: NewLookupSet()}
18+
}
19+
20+
func (num *Number) SetLatest(source string) error {
21+
latest, err := num.Lookups.Latest(source)
22+
if err != nil {
23+
return err
24+
}
25+
latest.CarrierNumberInfo.E164Number = strings.TrimSpace(latest.CarrierNumberInfo.E164Number)
26+
num.E164Number = strings.TrimSpace(num.E164Number)
27+
if num.E164Number != latest.CarrierNumberInfo.E164Number {
28+
return fmt.Errorf("lookup number mismatch: number [%s] lookup [%s]",
29+
num.E164Number,
30+
latest.CarrierNumberInfo.E164Number)
31+
}
32+
num.CarrierNumberInfo = latest.CarrierNumberInfo
33+
return nil
34+
}

number_set.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package gophonenumbers
2+
3+
import (
4+
"errors"
5+
"io/fs"
6+
7+
"github.com/grokify/simplego/encoding/jsonutil"
8+
)
9+
10+
type NumberSet struct {
11+
Numbers map[string]Number `json:"numbers"`
12+
}
13+
14+
func NewNumberSet() NumberSet {
15+
return NumberSet{Numbers: map[string]Number{}}
16+
}
17+
18+
func (set *NumberSet) Add(num Number) error {
19+
if len(num.E164Number) == 0 {
20+
return errors.New("no E.164 number")
21+
}
22+
set.Numbers[num.E164Number] = num
23+
return nil
24+
}
25+
26+
func (set *NumberSet) MapNumberCarrierName() map[string]string {
27+
mss := map[string]string{}
28+
for _, num := range set.Numbers {
29+
mss[num.E164Number] = num.CarrierNumberInfo.Name
30+
}
31+
return mss
32+
}
33+
34+
func (set *NumberSet) MapNumberLineType() map[string]string {
35+
mss := map[string]string{}
36+
for _, num := range set.Numbers {
37+
mss[num.E164Number] = num.CarrierNumberInfo.LineType
38+
}
39+
return mss
40+
}
41+
42+
func (set *NumberSet) WriteFileJSON(filename, prefix, indent string, perm fs.FileMode) error {
43+
return jsonutil.WriteFile(filename, set, prefix, indent, perm)
44+
}
45+
46+
type MapStringString map[string]string

0 commit comments

Comments
 (0)