diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 27a80647..d958fac8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,26 +1,32 @@ version: 2 updates: -- package-ecosystem: bundler - directory: "/" - schedule: - interval: daily - time: "04:00" - timezone: Asia/Calcutta -- package-ecosystem: bundler - directory: "/scraper" - schedule: - interval: daily - time: "04:00" - timezone: Asia/Calcutta -- package-ecosystem: mix - directory: "/" - schedule: - interval: daily - time: "04:00" - timezone: Asia/Calcutta -- package-ecosystem: composer - directory: "/" - schedule: - interval: daily - time: "04:00" - timezone: Asia/Calcutta + - package-ecosystem: bundler + directory: "/" + schedule: + interval: weekly + time: "04:00" + timezone: Asia/Calcutta + - package-ecosystem: bundler + directory: "/scraper" + schedule: + interval: weekly + time: "04:00" + timezone: Asia/Calcutta + - package-ecosystem: mix + directory: "/" + schedule: + interval: weekly + time: "04:00" + timezone: Asia/Calcutta + - package-ecosystem: composer + directory: "/" + schedule: + interval: weekly + time: "04:00" + timezone: Asia/Calcutta + - package-ecosystem: gomod + directory: "/" + schedule: + interval: weekly + time: "04:00" + timezone: Asia/Calcutta diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cd645a81..522f2b0f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -43,3 +43,16 @@ jobs: bundler-cache: true - run: bundle install - run: bundle exec rake + go: + strategy: + matrix: + go: ["^1.16.7", "^1.15.15"] + name: Go + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go }} + bundler-cache: true + - run: make go-test diff --git a/.gitignore b/.gitignore index 0fbf8056..0760a1a3 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,9 @@ InstalledFiles # One-off scripts *.php + +# IDE files +.idea/* + +# Temp files +.*~ diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4970a816 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +go-test: + go test -tags=unit -timeout 2m -coverprofile=coverage.cov -v ./... + +generate-constants: + go run ./src/go/generator/main.go diff --git a/README.md b/README.md index 66b6b036..44b2d964 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,16 @@ The minimum [PHP version supported is 7.2](https://endoflife.date/php). `$ npm install ifsc` +## Go + +`$ go get -u github.com/razorpay/ifsc` + +To use the go pkg, the import path should be: + +```go +import "github.com/razorpay/ifsc/src/go" +``` + ## Support Matrix Only the latest version of each SDK is considered. @@ -91,10 +101,11 @@ Only the latest version of each SDK is considered. | PHP | ✅ | ✅ | ✅ (✅) | ✅ | | Ruby | ✅ | ✅ | ✅ (✅) | ✅ | | Node.js | ✅ | ✅ | ❎ (❎) | ✅ | +| Go | ✅ | ✅ | ✅ (✅) | ✅ | ## API Documentation -This repository also hosts the source code for 3 modules: PHP/Node.js/Ruby as of now. +This repository also hosts the source code for 5 modules: PHP/Node.js/Ruby/Go as of now. The API is documented below: ### PHP @@ -277,6 +288,72 @@ Razorpay::IFSC::IFSC.find '...' code = Razorpay::IFSC::IFSC.new '...'; code.get ``` +### Go + +```go +package main + +import ( + ifsc "github.com/razorpay/ifsc/src/go" +) + +// todo: change funcs not required to lower case. + +func main() { + + ifsc.Validate("KKBK0000261") // Returns true + ifsc.Validate("BOTM0XEEMRA") // Returns false + + ifsc.ValidateBankCode("PUNB") // Returns true + ifsc.ValidateBankCode("ABCD") // Returns false + + ifsc.GetBankName("PUNB") // Returns "Punjab National Bank", nil + ifsc.GetBankName("ABCD") // Returns "", errors.New(invalid bank code) + ifsc.GetBankName(ifsc.HDFC) // Returns "HDFC Bank", nil + + + ifsc.GetBankDetails("PUNB") + // or + ifsc.GetBankDetails(ifsc.PUNB) + + /* Returns + (*ifsc.Bank){ + Name : "Punjab National Bank", + BankCode : "024", + Code : "PUNB", + Type : "PSB", + IFSC : "PUNB0244200", + MICR : "110024001", + IIN : "508568", + APBS : true, + AchCredit : true, + AchDebit : true, + NachDebit : true, + Upi : true + }), nil + */ + + ifsc.LookUP("KKBK0000261") + + /* + Returns + (*ifsc.IFSCResponse)({ + Bank : "Kotak Mahindra Bank", + Branch : "GURGAON", + Address : "JMD REGENT SQUARE,MEHRAULI GURGAON ROAD,OPPOSITE BRISTOL HOTEL,", + Contact : "4131000", + City : "GURGAON", + District : "GURGAON", + State : "HARYANA", + IFSC : "KKBK0000261", + BankCode : "KKBK" + }), nil + */ +} + +``` + +>>>>>>> 5ff434d (update readme & make bank code constants.) ### Code Notes Both the packages ship with a 300kb JSON file, that diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..d1b1dc47 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/razorpay/ifsc + +go 1.15 + +require github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..acb88a48 --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/src/go/bank.go b/src/go/bank.go new file mode 100644 index 00000000..27b1e84d --- /dev/null +++ b/src/go/bank.go @@ -0,0 +1,47 @@ +package ifsc + +import ( + "fmt" + "log" +) + +type Bank struct { + Name string `json:"name"` + BankCode string `json:"bank_code"` + Code string `json:"code"` + Type string `json:"type"` + IFSC string `json:"ifsc"` + MICR string `json:"micr"` + IIN string `json:"iin"` + APBS bool `json:"apbs"` + AchCredit bool `json:"ach_credit"` + AchDebit bool `json:"ach_debit"` + NachDebit bool `json:"nach_debit"` + Upi bool `json:"upi",omitempty` +} + +var bankData map[string]Bank + +func LoadBankData() { + if bankData == nil { + if err := LoadFile("banks.json", &bankData, ""); err != nil { + log.Panic(fmt.Sprintf("there is some error in banknames.json file: %v", err)) + } + } +} + +func GetBankDetails(bankCode string) *Bank { + data, ok := bankData[bankCode] + if !ok { + return nil + } + var err error + data.Name, err = GetBankName(bankCode) + if err != nil { + return nil + } + if data.MICR != "" { + data.BankCode = data.MICR[3:6] + } + return &data +} diff --git a/src/go/bank_test.go b/src/go/bank_test.go new file mode 100644 index 00000000..1d186624 --- /dev/null +++ b/src/go/bank_test.go @@ -0,0 +1,47 @@ +package ifsc + +import ( + "testing" + // TODO: change + "github.com/stretchr/testify/assert" +) + +func Test_BankGetDetails(t *testing.T) { + assert := assert.New(t) + result := GetBankDetails("FINO") + assert.Equal(getFINOfixture(), *result) + result = GetBankDetails("PUNB") + assert.Equal(getPUNBFixture(), *result) +} +func getPUNBFixture() Bank { + return Bank{ + Name: "Punjab National Bank", + BankCode: "024", + Code: "PUNB", + Type: "PSB", + IFSC: "PUNB0244200", + MICR: "110024001", + IIN: "508568", + APBS: true, + AchCredit: true, + AchDebit: true, + NachDebit: true, + Upi: true, + } +} +func getFINOfixture() Bank { + return Bank{ + Name: "Fino Payments Bank", + BankCode: "", + Code: "FINO", + Type: "PB", + IFSC: "FINO0000001", + MICR: "", + IIN: "608001", + APBS: true, + AchCredit: true, + AchDebit: false, + NachDebit: false, + Upi: true, + } +} diff --git a/src/go/client.go b/src/go/client.go new file mode 100644 index 00000000..5dc144a6 --- /dev/null +++ b/src/go/client.go @@ -0,0 +1,120 @@ +package ifsc + +import ( + "encoding/json" + "io/ioutil" + "log" + "net/http" +) + +type httpClient interface { + Get(url string) (resp *http.Response, err error) +} + +var client httpClient + +func init() { + client = http.DefaultClient +} + +const API_BASE = "https://ifsc.razorpay.com" + +type IFSCResponse struct { + Bank *string `json:"bank"` + Branch *string `json:"branch"` + Centre *string `json:"center"` + District *string `json:"district"` + State *string `json:"state"` + Address *string `json:"address"` + Contact *string `json:"contact"` + City *string `json:"city"` + IFSC *string `json:"ifsc"` + UPI *bool `json:"upi"` + RTGS *bool `json:"rtgs"` + MICR *string `json:"micr"` + NEFT *bool `json:"neft"` + SWIFT *string `json:"swift"` + IMPS *bool `json:"imps"` + BankCode *string `json:"bank_code"` +} + +// LookUP fetches the response from ifsc api for +func LookUP(ifsc string) (*IFSCResponse, error) { + var responseStruct *IFSCResponse + resp, err := client.Get(API_BASE + "/" + ifsc) + if err != nil { + return nil, err + } + status := resp.StatusCode + if status == http.StatusOK { + responseBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + responseMap := make(map[string]interface{}) + err = json.Unmarshal(responseBytes, &responseMap) + if err != nil { + return nil, err + } + responseStruct = NewIFSCResponse(responseMap) + responseStruct.setBankCode() + + } else if status == http.StatusNotFound { + return nil, ErrInvalidCode + } else { + return nil, ErrInvalidResponse + } + return responseStruct, nil +} +func NewIFSCResponse(input map[string]interface{}) *IFSCResponse { + response := &IFSCResponse{} + + setVal(input["BANK"], &response.Bank) + setVal(input["BRANCH"], &response.Branch) + setVal(input["CENTRE"], &response.Centre) + setVal(input["DISTRICT"], &response.District) + setVal(input["STATE"], &response.State) + setVal(input["ADDRESS"], &response.Address) + setVal(input["CONTACT"], &response.Contact) + setVal(input["CITY"], &response.City) + setVal(input["IFSC"], &response.IFSC) + setVal(input["UPI"], &response.UPI) + setVal(input["RTGS"], &response.RTGS) + setVal(input["MICR"], &response.MICR) + setVal(input["NEFT"], &response.NEFT) + setVal(input["SWIFT"], &response.SWIFT) + setVal(input["IMPS"], &response.IMPS) + + return response +} + +func setVal(input interface{}, output interface{}) { + bytes, err := json.Marshal(input) + if err != nil { + log.Printf("error marshalling input. input:%v, error:%v. Setting bytes as null", input, err) + bytes = []byte(`null`) + } + if err = json.Unmarshal(bytes, &output); err != nil { + log.Printf("error unmarshalling to output. bytes:%v, error:%v. Setting output as nil", string(bytes), err) + output = nil + } + +} + +func (ifsc *IFSCResponse) setBankCode() { + ifscCode := ifsc.GetBankCode() + ifsc.BankCode = &ifscCode +} + +func (ifsc *IFSCResponse) GetBankCode() string { + ifscCode := *ifsc.IFSC + return ifscCode[0:4] +} + +func (ifsc *IFSCResponse) GetBankName() string { + bankName, err := GetBankName(ifsc.GetBankCode()) + if err != nil { + return "" + } + return bankName +} diff --git a/src/go/client_test.go b/src/go/client_test.go new file mode 100644 index 00000000..709591c1 --- /dev/null +++ b/src/go/client_test.go @@ -0,0 +1,221 @@ +package ifsc + +import ( + "bytes" + "encoding/json" + "errors" + "io/ioutil" + "net/http" + "testing" + + "github.com/razorpay/ifsc/src/go/mocks" + "github.com/stretchr/testify/assert" +) + +func getIfscResponse() *IFSCResponse { + bytes := []byte(`{"bank": "Kotak Mahindra Bank", + "branch": "GURGAON", + "center": "GURGAON", + "district": "GURGAON", + "state": "HARYANA", + "address": "JMD REGENT SQUARE,MEHRAULI GURGAON ROAD,OPPOSITE BRISTOL HOTEL,", + "contact": "4131000", + "city": null, + "ifsc": "KKBK0000261", + "upi": true, + "rtgs": true, + "micr": "110485003", + "neft": null, + "swift": "", + "imps": true, + "bank_code": "KKBK"}`) + var response IFSCResponse + if err := json.Unmarshal(bytes, &response); err != nil { + return nil + } + return &response +} +func TestLookUP(t *testing.T) { + client = &mocks.Client{} + type args struct { + ifsc string + } + tests := []struct { + name string + args args + IfscResponse *IFSCResponse + mockedClient func() + wantErr bool + err error + }{ + { + "success", + args{"KKBK0000261"}, + getIfscResponse(), + GetSuccessMockResponse, + false, + nil, + }, + { + "failure, invalid code", + args{"KKB0000abc1"}, + nil, + GetInvalidCodeMockResponse, + true, + ErrInvalidCode, + }, + { + "failure, invalid Response", + args{"AIRP0000001"}, + nil, + GetFailureMockResponse, + true, + ErrInvalidResponse, + }, + { + "timeout error", + args{"abcd"}, + nil, + GetUrlError, + true, + http.ErrHandlerTimeout, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var err error + var got *IFSCResponse + tt.mockedClient() + got, err = LookUP(tt.args.ifsc) + if (err != nil) != tt.wantErr { + t.Errorf("LookUP() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr { + if !errors.Is(tt.err, err) { + t.Errorf("error is not equal. want()=%v, got()=%v", tt.err, err) + } + } else { + assert.Equal(t, tt.IfscResponse.Bank, got.Bank) + assert.Equal(t, tt.IfscResponse.Branch, got.Branch) + assert.Equal(t, tt.IfscResponse.Centre, got.Centre) + assert.Equal(t, tt.IfscResponse.District, got.District) + assert.Equal(t, tt.IfscResponse.State, got.State) + assert.Equal(t, tt.IfscResponse.Address, got.Address) + assert.Equal(t, tt.IfscResponse.Contact, got.Contact) + assert.Equal(t, tt.IfscResponse.City, got.City) + assert.Equal(t, tt.IfscResponse.IFSC, got.IFSC) + assert.Equal(t, tt.IfscResponse.UPI, got.UPI) + assert.Equal(t, tt.IfscResponse.RTGS, got.RTGS) + assert.Equal(t, tt.IfscResponse.MICR, got.MICR) + assert.Equal(t, tt.IfscResponse.NEFT, got.NEFT) + assert.Equal(t, tt.IfscResponse.SWIFT, got.SWIFT) + assert.Equal(t, tt.IfscResponse.IMPS, got.IMPS) + assert.Equal(t, tt.IfscResponse.BankCode, got.BankCode) + } + }) + } +} +func GetFailureMockResponse() { + mocks.GetFuncVar = func(url string) (resp *http.Response, err error) { + return &http.Response{ + StatusCode: http.StatusInternalServerError, + }, nil + } + +} +func GetInvalidCodeMockResponse() { + mocks.GetFuncVar = func(url string) (resp *http.Response, err error) { + return &http.Response{ + StatusCode: http.StatusNotFound, + }, nil + } + +} + +func GetUrlError() { + mocks.GetFuncVar = func(url string) (resp *http.Response, err error) { + return nil, http.ErrHandlerTimeout + } +} + +func GetSuccessMockResponse() { + mocks.GetFuncVar = func(url string) (resp *http.Response, err error) { + var successJson = `{ + "BRANCH": "GURGAON", + "CENTRE": "GURGAON", + "DISTRICT": "GURGAON", + "STATE": "HARYANA", + "ADDRESS": "JMD REGENT SQUARE,MEHRAULI GURGAON ROAD,OPPOSITE BRISTOL HOTEL,", + "CONTACT": "4131000", + "UPI": true, + "RTGS": true, + "CITY": null, + "MICR": "110485003", + "NEFT": null, + "IMPS": true, + "SWIFT": "", + "BANK": "Kotak Mahindra Bank", + "BANKCODE": "KKBK", + "IFSC": "KKBK0000261" + }` + r := ioutil.NopCloser(bytes.NewReader([]byte(successJson))) + return &http.Response{ + StatusCode: http.StatusOK, + Body: r, + }, nil + } + +} + +func TestIFSCResponse_GetBankName(t *testing.T) { + type fields struct { + Bank string + Branch string + Address string + Contact string + City string + District string + State string + BankCode string + IFSC string + } + tests := []struct { + name string + fields fields + want string + }{ + { + "success", + fields{ + IFSC: "HDFC0CADARS", + }, + "HDFC Bank", + }, + { + "failure", + fields{ + IFSC: "12 B", + }, + "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + expected := &IFSCResponse{ + Bank: &tt.fields.Bank, + Branch: &tt.fields.Branch, + Address: &tt.fields.Address, + Contact: &tt.fields.Contact, + City: &tt.fields.City, + District: &tt.fields.District, + State: &tt.fields.State, + BankCode: &tt.fields.BankCode, + IFSC: &tt.fields.IFSC, + } + if got := expected.GetBankName(); got != tt.want { + t.Errorf("IFSCResponse.GetBankName() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/src/go/constants.go b/src/go/constants.go new file mode 100644 index 00000000..b3c8c640 --- /dev/null +++ b/src/go/constants.go @@ -0,0 +1,2927 @@ +/* +This file is autogenerated from generator/main.go parsing the php constants from +Bank.php +*/ +package ifsc + +const ( + AACX = "AACX" + ABBL = "ABBL" + ABCX = "ABCX" + ABDX = "ABDX" + ABEX = "ABEX" + ABHY = "ABHY" + ABNA = "ABNA" + ABPB = "ABPB" + ABSB = "ABSB" + ABUX = "ABUX" + ACAX = "ACAX" + ACBX = "ACBX" + ACCX = "ACCX" + ACKX = "ACKX" + ACOX = "ACOX" + ACUB = "ACUB" + ACUX = "ACUX" + ADBX = "ADBX" + ADCB = "ADCB" + ADCC = "ADCC" + ADCX = "ADCX" + ADDX = "ADDX" + AGCX = "AGCX" + AGDX = "AGDX" + AGRX = "AGRX" + AGSX = "AGSX" + AGUX = "AGUX" + AGVX = "AGVX" + AHMX = "AHMX" + AHUX = "AHUX" + AIRP = "AIRP" + AJAR = "AJAR" + AJHC = "AJHC" + AJKB = "AJKB" + AJMX = "AJMX" + AJNX = "AJNX" + AJPX = "AJPX" + AJSX = "AJSX" + AJUX = "AJUX" + AKJB = "AKJB" + AKKB = "AKKB" + AKMX = "AKMX" + AKOX = "AKOX" + ALAX = "ALAX" + ALIX = "ALIX" + ALLA = "ALLA" + ALLX = "ALLX" + ALWX = "ALWX" + AMAX = "AMAX" + AMBX = "AMBX" + AMCB = "AMCB" + AMCX = "AMCX" + AMDN = "AMDN" + AMMX = "AMMX" + AMNX = "AMNX" + AMRX = "AMRX" + AMSB = "AMSB" + AMSX = "AMSX" + ANBX = "ANBX" + ANDB = "ANDB" + ANDX = "ANDX" + ANMX = "ANMX" + ANSX = "ANSX" + ANUX = "ANUX" + ANZB = "ANZB" + APBL = "APBL" + APCX = "APCX" + APGB = "APGB" + APGV = "APGV" + APGX = "APGX" + APJX = "APJX" + APMC = "APMC" + APMX = "APMX" + APNX = "APNX" + APRX = "APRX" + APSX = "APSX" + ARBL = "ARBL" + ARCX = "ARCX" + ARMX = "ARMX" + ARYX = "ARYX" + ASBL = "ASBL" + ASBX = "ASBX" + ASHX = "ASHX" + ASKX = "ASKX" + ASNX = "ASNX" + ASOX = "ASOX" + ASSX = "ASSX" + AUBL = "AUBL" + AUBX = "AUBX" + AUCB = "AUCB" + AUCX = "AUCX" + AUGX = "AUGX" + AURX = "AURX" + AVDX = "AVDX" + AWCX = "AWCX" + AWUX = "AWUX" + AZAX = "AZAX" + AZPX = "AZPX" + AZSX = "AZSX" + AZUX = "AZUX" + BACB = "BACB" + BACX = "BACX" + BADX = "BADX" + BALX = "BALX" + BANX = "BANX" + BARA = "BARA" + BARB = "BARB" + BARC = "BARC" + BARX = "BARX" + BASX = "BASX" + BAUX = "BAUX" + BAVX = "BAVX" + BBKM = "BBKM" + BBLX = "BBLX" + BBRX = "BBRX" + BBSX = "BBSX" + BBUX = "BBUX" + BBVX = "BBVX" + BCBM = "BCBM" + BCBX = "BCBX" + BCCB = "BCCB" + BCCX = "BCCX" + BCEX = "BCEX" + BCEY = "BCEY" + BCOX = "BCOX" + BCUB = "BCUB" + BCUX = "BCUX" + BDBB = "BDBB" + BDBL = "BDBL" + BDBX = "BDBX" + BDCX = "BDCX" + BDDX = "BDDX" + BDIX = "BDIX" + BDNX = "BDNX" + BDOX = "BDOX" + BDUX = "BDUX" + BEDX = "BEDX" + BELX = "BELX" + BFUX = "BFUX" + BGBX = "BGBX" + BGCX = "BGCX" + BGGX = "BGGX" + BGUX = "BGUX" + BGVX = "BGVX" + BHAX = "BHAX" + BHBX = "BHBX" + BHCX = "BHCX" + BHDX = "BHDX" + BHEX = "BHEX" + BHGX = "BHGX" + BHIX = "BHIX" + BHJX = "BHJX" + BHMX = "BHMX" + BHOX = "BHOX" + BHRX = "BHRX" + BHSX = "BHSX" + BHTX = "BHTX" + BHUX = "BHUX" + BHWX = "BHWX" + BJUX = "BJUX" + BKCX = "BKCX" + BKDN = "BKDN" + BKDX = "BKDX" + BKID = "BKID" + BKSX = "BKSX" + BLGX = "BLGX" + BMBL = "BMBL" + BMCB = "BMCB" + BMCX = "BMCX" + BMPX = "BMPX" + BMSX = "BMSX" + BNBX = "BNBX" + BNCX = "BNCX" + BNPA = "BNPA" + BNSB = "BNSB" + BNSX = "BNSX" + BODX = "BODX" + BOFA = "BOFA" + BORX = "BORX" + BOTM = "BOTM" + BOTX = "BOTX" + BPCX = "BPCX" + BPSX = "BPSX" + BRCX = "BRCX" + BRDX = "BRDX" + BRGX = "BRGX" + BRMX = "BRMX" + BRSX = "BRSX" + BRUX = "BRUX" + BSBX = "BSBX" + BSCX = "BSCX" + BTCX = "BTCX" + BTUX = "BTUX" + BUBX = "BUBX" + BUCL = "BUCL" + BUCX = "BUCX" + BUGX = "BUGX" + BUNX = "BUNX" + BURX = "BURX" + BUSX = "BUSX" + BUZX = "BUZX" + BVNX = "BVNX" + BVSX = "BVSX" + BWCX = "BWCX" + CALX = "CALX" + CBHX = "CBHX" + CBIN = "CBIN" + CCBL = "CCBL" + CCBX = "CCBX" + CCCX = "CCCX" + CCMX = "CCMX" + CCOB = "CCOB" + CCUX = "CCUX" + CDCX = "CDCX" + CEBX = "CEBX" + CGBX = "CGBX" + CGGX = "CGGX" + CHAS = "CHAS" + CHAX = "CHAX" + CHBX = "CHBX" + CHCX = "CHCX" + CHDX = "CHDX" + CHIX = "CHIX" + CHKX = "CHKX" + CHPX = "CHPX" + CHRX = "CHRX" + CHSX = "CHSX" + CHTX = "CHTX" + CIDX = "CIDX" + CITI = "CITI" + CITX = "CITX" + CIUB = "CIUB" + CJAX = "CJAX" + CJMX = "CJMX" + CLBL = "CLBL" + CMCB = "CMCB" + CMCX = "CMCX" + CMDX = "CMDX" + CMLX = "CMLX" + CMPX = "CMPX" + CNRB = "CNRB" + CNSX = "CNSX" + COAS = "COAS" + COCX = "COCX" + COLX = "COLX" + COMX = "COMX" + CONX = "CONX" + CORP = "CORP" + COSB = "COSB" + CPDX = "CPDX" + CPSN = "CPSN" + CRBX = "CRBX" + CRES = "CRES" + CRLY = "CRLY" + CRSX = "CRSX" + CRUB = "CRUB" + CSBK = "CSBK" + CSBX = "CSBX" + CTBA = "CTBA" + CTBX = "CTBX" + CTCB = "CTCB" + CTOX = "CTOX" + CTUX = "CTUX" + CUBX = "CUBX" + CUCX = "CUCX" + CURX = "CURX" + CZCX = "CZCX" + CZUX = "CZUX" + DAAX = "DAAX" + DAHX = "DAHX" + DAUX = "DAUX" + DBAX = "DBAX" + DBSS = "DBSS" + DCBL = "DCBL" + DCBX = "DCBX" + DCCX = "DCCX" + DCDX = "DCDX" + DCEX = "DCEX" + DCKX = "DCKX" + DCMX = "DCMX" + DCNX = "DCNX" + DCPX = "DCPX" + DCSX = "DCSX" + DCTX = "DCTX" + DCUX = "DCUX" + DDBX = "DDBX" + DDCX = "DDCX" + DDDX = "DDDX" + DDHX = "DDHX" + DEGX = "DEGX" + DENS = "DENS" + DEOB = "DEOB" + DEOX = "DEOX" + DEUT = "DEUT" + DEUX = "DEUX" + DEVX = "DEVX" + DGBX = "DGBX" + DHBX = "DHBX" + DHKX = "DHKX" + DHUX = "DHUX" + DIBX = "DIBX" + DICG = "DICG" + DICX = "DICX" + DIUX = "DIUX" + DJCX = "DJCX" + DKCL = "DKCL" + DKSX = "DKSX" + DLSC = "DLSC" + DLXB = "DLXB" + DMCB = "DMCB" + DMCX = "DMCX" + DMKB = "DMKB" + DMKJ = "DMKJ" + DNDC = "DNDC" + DNSB = "DNSB" + DNSX = "DNSX" + DOBX = "DOBX" + DOHB = "DOHB" + DRGX = "DRGX" + DSBX = "DSBX" + DSCB = "DSCB" + DSHX = "DSHX" + DSPX = "DSPX" + DSUX = "DSUX" + DTCX = "DTCX" + DTPX = "DTPX" + DUCX = "DUCX" + DUMX = "DUMX" + DUNX = "DUNX" + DURG = "DURG" + DVDX = "DVDX" + DYPX = "DYPX" + EBIL = "EBIL" + ECBL = "ECBL" + EDBX = "EDBX" + EDCX = "EDCX" + EDSX = "EDSX" + EIBI = "EIBI" + ESAF = "ESAF" + ESFB = "ESFB" + ESMF = "ESMF" + ETCX = "ETCX" + ETDX = "ETDX" + EUCX = "EUCX" + EWCX = "EWCX" + FCBX = "FCBX" + FCCX = "FCCX" + FCOX = "FCOX" + FDFX = "FDFX" + FDRL = "FDRL" + FEKX = "FEKX" + FGCB = "FGCB" + FINF = "FINF" + FINO = "FINO" + FINX = "FINX" + FIRN = "FIRN" + FIRX = "FIRX" + FMCX = "FMCX" + FRIX = "FRIX" + FSCX = "FSCX" + FSFB = "FSFB" + FZCX = "FZCX" + FZSX = "FZSX" + GACX = "GACX" + GADX = "GADX" + GANX = "GANX" + GBCB = "GBCB" + GCBX = "GCBX" + GCCX = "GCCX" + GCUL = "GCUL" + GCUX = "GCUX" + GDCB = "GDCB" + GDCX = "GDCX" + GDDX = "GDDX" + GDUX = "GDUX" + GGBK = "GGBK" + GGCX = "GGCX" + GHPX = "GHPX" + GKNX = "GKNX" + GMBX = "GMBX" + GMCX = "GMCX" + GMUX = "GMUX" + GNCX = "GNCX" + GNSX = "GNSX" + GODX = "GODX" + GOSX = "GOSX" + GPCX = "GPCX" + GPOX = "GPOX" + GRAX = "GRAX" + GSBL = "GSBL" + GSBX = "GSBX" + GSCB = "GSCB" + GSCX = "GSCX" + GSSX = "GSSX" + GTCX = "GTCX" + GUBX = "GUBX" + GUCX = "GUCX" + GUNX = "GUNX" + GUOX = "GUOX" + HAMX = "HAMX" + HANX = "HANX" + HARC = "HARC" + HCBL = "HCBL" + HCBX = "HCBX" + HCCX = "HCCX" + HCLX = "HCLX" + HDCL = "HDCL" + HDCX = "HDCX" + HDFC = "HDFC" + HGBX = "HGBX" + HINX = "HINX" + HISX = "HISX" + HMBX = "HMBX" + HMNX = "HMNX" + HOCX = "HOCX" + HOOX = "HOOX" + HPCX = "HPCX" + HPSC = "HPSC" + HPSX = "HPSX" + HSBC = "HSBC" + HSBM = "HSBM" + HSBX = "HSBX" + HSCX = "HSCX" + HSDX = "HSDX" + HSSX = "HSSX" + HUBX = "HUBX" + HUCB = "HUCB" + HUCH = "HUCH" + HUCX = "HUCX" + HUTX = "HUTX" + HVBK = "HVBK" + IBBK = "IBBK" + IBKL = "IBKL" + IBKO = "IBKO" + ICBK = "ICBK" + ICBL = "ICBL" + ICHX = "ICHX" + ICIC = "ICIC" + ICLL = "ICLL" + ICMX = "ICMX" + IDFB = "IDFB" + IDIB = "IDIB" + IDUK = "IDUK" + IDUX = "IDUX" + ILCB = "ILCB" + IMCX = "IMCX" + IMPX = "IMPX" + INCX = "INCX" + INDB = "INDB" + INDX = "INDX" + IOBA = "IOBA" + IPCX = "IPCX" + IPOS = "IPOS" + IPPB = "IPPB" + IPSX = "IPSX" + ISBX = "ISBX" + ISMX = "ISMX" + ITBL = "ITBL" + ITCX = "ITCX" + ITDX = "ITDX" + IUCB = "IUCB" + IUCX = "IUCX" + JACX = "JACX" + JAKA = "JAKA" + JALX = "JALX" + JAMX = "JAMX" + JANA = "JANA" + JANX = "JANX" + JASB = "JASB" + JASX = "JASX" + JAUX = "JAUX" + JBHX = "JBHX" + JBIX = "JBIX" + JBMX = "JBMX" + JCBX = "JCBX" + JCCB = "JCCB" + JCCX = "JCCX" + JCDX = "JCDX" + JCHX = "JCHX" + JCPX = "JCPX" + JCUX = "JCUX" + JDCX = "JDCX" + JDEX = "JDEX" + JGBX = "JGBX" + JGCX = "JGCX" + JGWX = "JGWX" + JHAX = "JHAX" + JHSX = "JHSX" + JHUX = "JHUX" + JIBX = "JIBX" + JICX = "JICX" + JIDX = "JIDX" + JIGX = "JIGX" + JIKX = "JIKX" + JIMX = "JIMX" + JINX = "JINX" + JIOP = "JIOP" + JIOX = "JIOX" + JIRX = "JIRX" + JISX = "JISX" + JIVX = "JIVX" + JJCX = "JJCX" + JJHX = "JJHX" + JJSB = "JJSB" + JKAX = "JKAX" + JKCX = "JKCX" + JKDX = "JKDX" + JKEX = "JKEX" + JKHX = "JKHX" + JKMX = "JKMX" + JKRX = "JKRX" + JKSX = "JKSX" + JLCX = "JLCX" + JLDX = "JLDX" + JLNX = "JLNX" + JLSX = "JLSX" + JLWX = "JLWX" + JMAX = "JMAX" + JMBX = "JMBX" + JMCX = "JMCX" + JMDX = "JMDX" + JMHX = "JMHX" + JMMX = "JMMX" + JMOX = "JMOX" + JMPX = "JMPX" + JMSX = "JMSX" + JMYX = "JMYX" + JNAX = "JNAX" + JNDX = "JNDX" + JNSX = "JNSX" + JODX = "JODX" + JONX = "JONX" + JOWX = "JOWX" + JPAX = "JPAX" + JPCB = "JPCB" + JPCX = "JPCX" + JRAX = "JRAX" + JRKX = "JRKX" + JRNX = "JRNX" + JRSX = "JRSX" + JSAB = "JSAB" + JSAX = "JSAX" + JSBL = "JSBL" + JSBP = "JSBP" + JSBX = "JSBX" + JSCX = "JSCX" + JSDX = "JSDX" + JSEX = "JSEX" + JSFB = "JSFB" + JSHX = "JSHX" + JSKX = "JSKX" + JSMX = "JSMX" + JSOX = "JSOX" + JSRX = "JSRX" + JSTX = "JSTX" + JSVX = "JSVX" + JSWX = "JSWX" + JTIX = "JTIX" + JTSC = "JTSC" + JTSX = "JTSX" + JUCX = "JUCX" + JUSX = "JUSX" + JVCX = "JVCX" + KAAX = "KAAX" + KACE = "KACE" + KACX = "KACX" + KADX = "KADX" + KAGX = "KAGX" + KAIJ = "KAIJ" + KALX = "KALX" + KAMX = "KAMX" + KANG = "KANG" + KANX = "KANX" + KARB = "KARB" + KARX = "KARX" + KASX = "KASX" + KATX = "KATX" + KAYX = "KAYX" + KBCX = "KBCX" + KBKB = "KBKB" + KBNX = "KBNX" + KBSX = "KBSX" + KCBL = "KCBL" + KCBX = "KCBX" + KCCB = "KCCB" + KCCX = "KCCX" + KCDX = "KCDX" + KCEX = "KCEX" + KCOB = "KCOB" + KCUB = "KCUB" + KCUX = "KCUX" + KDBX = "KDBX" + KDCB = "KDCB" + KDCX = "KDCX" + KDIX = "KDIX" + KDNX = "KDNX" + KDUX = "KDUX" + KEMX = "KEMX" + KESX = "KESX" + KGBX = "KGBX" + KGDX = "KGDX" + KGRB = "KGRB" + KGSX = "KGSX" + KHAX = "KHAX" + KHCX = "KHCX" + KHDX = "KHDX" + KHNX = "KHNX" + KHUX = "KHUX" + KICX = "KICX" + KJSB = "KJSB" + KJSX = "KJSX" + KKBK = "KKBK" + KKMX = "KKMX" + KKSX = "KKSX" + KLGB = "KLGB" + KLMX = "KLMX" + KMCB = "KMCB" + KMCX = "KMCX" + KMNX = "KMNX" + KMSX = "KMSX" + KNBX = "KNBX" + KNCX = "KNCX" + KNNX = "KNNX" + KNPX = "KNPX" + KNSB = "KNSB" + KNSX = "KNSX" + KOBX = "KOBX" + KOCX = "KOCX" + KODX = "KODX" + KOEX = "KOEX" + KOLH = "KOLH" + KOSX = "KOSX" + KOTX = "KOTX" + KOYX = "KOYX" + KPCX = "KPCX" + KRCX = "KRCX" + KRDX = "KRDX" + KRIX = "KRIX" + KRMX = "KRMX" + KRNX = "KRNX" + KRTH = "KRTH" + KRTX = "KRTX" + KSBK = "KSBK" + KSBX = "KSBX" + KSCB = "KSCB" + KSCX = "KSCX" + KSMX = "KSMX" + KSNX = "KSNX" + KSTX = "KSTX" + KSUX = "KSUX" + KTBX = "KTBX" + KTCX = "KTCX" + KTDX = "KTDX" + KTTX = "KTTX" + KUBX = "KUBX" + KUCB = "KUCB" + KUCX = "KUCX" + KUKX = "KUKX" + KULX = "KULX" + KUMX = "KUMX" + KUNS = "KUNS" + KURX = "KURX" + KVBL = "KVBL" + KVCX = "KVCX" + KVGB = "KVGB" + KYDX = "KYDX" + LACX = "LACX" + LATX = "LATX" + LAVB = "LAVB" + LBMX = "LBMX" + LCBX = "LCBX" + LCCX = "LCCX" + LDCX = "LDCX" + LDPX = "LDPX" + LDRX = "LDRX" + LECX = "LECX" + LICB = "LICB" + LKBL = "LKBL" + LKCX = "LKCX" + LKHX = "LKHX" + LKMX = "LKMX" + LMNX = "LMNX" + LNSX = "LNSX" + LOKX = "LOKX" + LONX = "LONX" + LUCX = "LUCX" + LULX = "LULX" + MABL = "MABL" + MACX = "MACX" + MADX = "MADX" + MAGX = "MAGX" + MAHB = "MAHB" + MAHG = "MAHG" + MAHX = "MAHX" + MAJX = "MAJX" + MAKX = "MAKX" + MALX = "MALX" + MAMX = "MAMX" + MANX = "MANX" + MAPX = "MAPX" + MASX = "MASX" + MAUX = "MAUX" + MAVX = "MAVX" + MAWX = "MAWX" + MAYX = "MAYX" + MBCX = "MBCX" + MBGX = "MBGX" + MBLX = "MBLX" + MCAX = "MCAX" + MCBL = "MCBL" + MCBX = "MCBX" + MCCX = "MCCX" + MCDX = "MCDX" + MCLX = "MCLX" + MCOX = "MCOX" + MCSX = "MCSX" + MCUX = "MCUX" + MDBK = "MDBK" + MDCB = "MDCB" + MDCX = "MDCX" + MDEX = "MDEX" + MDGX = "MDGX" + MDIX = "MDIX" + MDMX = "MDMX" + MDPX = "MDPX" + MEDX = "MEDX" + MERX = "MERX" + MEUX = "MEUX" + MFCX = "MFCX" + MFUX = "MFUX" + MGBX = "MGBX" + MGCB = "MGCB" + MGCX = "MGCX" + MGDX = "MGDX" + MGRB = "MGRB" + MGSX = "MGSX" + MGUX = "MGUX" + MHCB = "MHCB" + MHCX = "MHCX" + MHEX = "MHEX" + MHLX = "MHLX" + MHMX = "MHMX" + MHNX = "MHNX" + MHSX = "MHSX" + MHUX = "MHUX" + MIZX = "MIZX" + MJCX = "MJCX" + MKPB = "MKPB" + MKUX = "MKUX" + MKYX = "MKYX" + MLCG = "MLCG" + MLDX = "MLDX" + MMCX = "MMCX" + MMMX = "MMMX" + MNBX = "MNBX" + MNCX = "MNCX" + MNSX = "MNSX" + MOGX = "MOGX" + MOUX = "MOUX" + MPCX = "MPCX" + MPDX = "MPDX" + MPRX = "MPRX" + MRBX = "MRBX" + MRTX = "MRTX" + MSAX = "MSAX" + MSBL = "MSBL" + MSBX = "MSBX" + MSCI = "MSCI" + MSCX = "MSCX" + MSHQ = "MSHQ" + MSLM = "MSLM" + MSNU = "MSNU" + MSNX = "MSNX" + MSOX = "MSOX" + MSSX = "MSSX" + MUBL = "MUBL" + MUBX = "MUBX" + MUCG = "MUCG" + MUCX = "MUCX" + MUDX = "MUDX" + MUNX = "MUNX" + MUPX = "MUPX" + MURX = "MURX" + MUSX = "MUSX" + MVCB = "MVCB" + MVCX = "MVCX" + MVIX = "MVIX" + MYAX = "MYAX" + MYSX = "MYSX" + MZCX = "MZCX" + MZRX = "MZRX" + NABX = "NABX" + NACX = "NACX" + NAGX = "NAGX" + NAIX = "NAIX" + NALX = "NALX" + NANX = "NANX" + NASX = "NASX" + NATA = "NATA" + NAUX = "NAUX" + NAVX = "NAVX" + NAWX = "NAWX" + NBAD = "NBAD" + NBBX = "NBBX" + NBCX = "NBCX" + NBMX = "NBMX" + NBRD = "NBRD" + NCBL = "NCBL" + NCBX = "NCBX" + NCCX = "NCCX" + NCOX = "NCOX" + NCUB = "NCUB" + NCUX = "NCUX" + NDCB = "NDCB" + NDCX = "NDCX" + NDDX = "NDDX" + NDGX = "NDGX" + NDIX = "NDIX" + NESF = "NESF" + NEYX = "NEYX" + NGBX = "NGBX" + NGKX = "NGKX" + NGNX = "NGNX" + NGRX = "NGRX" + NGSB = "NGSB" + NGSX = "NGSX" + NICB = "NICB" + NIDX = "NIDX" + NILX = "NILX" + NIRX = "NIRX" + NIUX = "NIUX" + NJBK = "NJBK" + NJCX = "NJCX" + NJGX = "NJGX" + NJMX = "NJMX" + NJSX = "NJSX" + NKGS = "NKGS" + NLGX = "NLGX" + NLUX = "NLUX" + NMCB = "NMCB" + NMCX = "NMCX" + NMGB = "NMGB" + NNCX = "NNCX" + NNSB = "NNSB" + NNSX = "NNSX" + NOBX = "NOBX" + NOIX = "NOIX" + NOSC = "NOSC" + NPCX = "NPCX" + NPKX = "NPKX" + NRDX = "NRDX" + NRMX = "NRMX" + NSBB = "NSBB" + NSBX = "NSBX" + NSCX = "NSCX" + NSGX = "NSGX" + NSIX = "NSIX" + NSJX = "NSJX" + NSMX = "NSMX" + NSPB = "NSPB" + NSPX = "NSPX" + NSRX = "NSRX" + NTBL = "NTBL" + NUBX = "NUBX" + NUCB = "NUCB" + NUCX = "NUCX" + NVCX = "NVCX" + NVNM = "NVNM" + NVSX = "NVSX" + NWCX = "NWCX" + ODCX = "ODCX" + ODGB = "ODGB" + OIBA = "OIBA" + OMCX = "OMCX" + ONSX = "ONSX" + ORBC = "ORBC" + ORCB = "ORCB" + OSMX = "OSMX" + PABX = "PABX" + PACX = "PACX" + PADX = "PADX" + PALX = "PALX" + PANX = "PANX" + PARX = "PARX" + PASX = "PASX" + PATX = "PATX" + PAYX = "PAYX" + PBGX = "PBGX" + PCBL = "PCBL" + PCBX = "PCBX" + PCCB = "PCCB" + PCCX = "PCCX" + PCLX = "PCLX" + PCMX = "PCMX" + PCOX = "PCOX" + PCPX = "PCPX" + PCSX = "PCSX" + PCTX = "PCTX" + PCUX = "PCUX" + PDBX = "PDBX" + PDCX = "PDCX" + PDNX = "PDNX" + PDSX = "PDSX" + PDUX = "PDUX" + PGBX = "PGBX" + PGCX = "PGCX" + PGRX = "PGRX" + PGTX = "PGTX" + PITX = "PITX" + PJSB = "PJSB" + PKBX = "PKBX" + PKDX = "PKDX" + PKGB = "PKGB" + PLOX = "PLOX" + PLUX = "PLUX" + PMCB = "PMCB" + PMCX = "PMCX" + PMEC = "PMEC" + PMNX = "PMNX" + PNCX = "PNCX" + PNMX = "PNMX" + PNPX = "PNPX" + PNSX = "PNSX" + PPBX = "PPBX" + PPCX = "PPCX" + PRCX = "PRCX" + PREX = "PREX" + PROX = "PROX" + PRPX = "PRPX" + PRSX = "PRSX" + PRTH = "PRTH" + PSBX = "PSBX" + PSCX = "PSCX" + PSIB = "PSIB" + PSRX = "PSRX" + PSSX = "PSSX" + PTCX = "PTCX" + PTNX = "PTNX" + PTSX = "PTSX" + PUBX = "PUBX" + PUCB = "PUCB" + PUCX = "PUCX" + PUDX = "PUDX" + PUGX = "PUGX" + PUNB = "PUNB" + PUNX = "PUNX" + PURX = "PURX" + PUSD = "PUSD" + PUSX = "PUSX" + PUUX = "PUUX" + PVAX = "PVAX" + PVCX = "PVCX" + PVVX = "PVVX" + PWUX = "PWUX" + PYCX = "PYCX" + PYTM = "PYTM" + QNBA = "QNBA" + QUCX = "QUCX" + RABO = "RABO" + RACX = "RACX" + RAEX = "RAEX" + RAJX = "RAJX" + RAKX = "RAKX" + RAMX = "RAMX" + RANX = "RANX" + RATN = "RATN" + RAUX = "RAUX" + RBBX = "RBBX" + RBCX = "RBCX" + RBIH = "RBIH" + RBIN = "RBIN" + RBIP = "RBIP" + RBIS = "RBIS" + RCBX = "RCBX" + RCCX = "RCCX" + RCDX = "RCDX" + RCMX = "RCMX" + RCUX = "RCUX" + RDCB = "RDCB" + RDCX = "RDCX" + RDNX = "RDNX" + REBX = "REBX" + RECX = "RECX" + REWX = "REWX" + RGCX = "RGCX" + RGSX = "RGSX" + RHMX = "RHMX" + RJCX = "RJCX" + RJJX = "RJJX" + RJNX = "RJNX" + RJTX = "RJTX" + RKCX = "RKCX" + RLUX = "RLUX" + RMGB = "RMGB" + RNBX = "RNBX" + RNDX = "RNDX" + RNGX = "RNGX" + RNSB = "RNSB" + RNSX = "RNSX" + ROCX = "ROCX" + ROHX = "ROHX" + RPUX = "RPUX" + RRBP = "RRBP" + RRSX = "RRSX" + RSBL = "RSBL" + RSBX = "RSBX" + RSCB = "RSCB" + RSSB = "RSSB" + RSSX = "RSSX" + RSUX = "RSUX" + RSVX = "RSVX" + RUCX = "RUCX" + RUKX = "RUKX" + RUMX = "RUMX" + RZSX = "RZSX" + SABR = "SABR" + SABX = "SABX" + SACB = "SACB" + SACX = "SACX" + SADX = "SADX" + SAGX = "SAGX" + SAHE = "SAHE" + SAHX = "SAHX" + SAIX = "SAIX" + SALX = "SALX" + SAMX = "SAMX" + SANT = "SANT" + SANX = "SANX" + SAPX = "SAPX" + SARX = "SARX" + SASA = "SASA" + SASX = "SASX" + SATX = "SATX" + SAUX = "SAUX" + SAVX = "SAVX" + SAWX = "SAWX" + SBBJ = "SBBJ" + SBCR = "SBCR" + SBCX = "SBCX" + SBHY = "SBHY" + SBIN = "SBIN" + SBKX = "SBKX" + SBLD = "SBLD" + SBLS = "SBLS" + SBLX = "SBLX" + SBMX = "SBMX" + SBMY = "SBMY" + SBNX = "SBNX" + SBPS = "SBPS" + SBPX = "SBPX" + SBSX = "SBSX" + SBTR = "SBTR" + SBUJ = "SBUJ" + SBUX = "SBUX" + SCBL = "SCBL" + SCBX = "SCBX" + SCCX = "SCCX" + SCDX = "SCDX" + SCIX = "SCIX" + SCNX = "SCNX" + SCOB = "SCOB" + SCOX = "SCOX" + SCPX = "SCPX" + SCSX = "SCSX" + SCUX = "SCUX" + SDBX = "SDBX" + SDCB = "SDCB" + SDCE = "SDCE" + SDCX = "SDCX" + SDHX = "SDHX" + SDSX = "SDSX" + SDTC = "SDTC" + SDTX = "SDTX" + SDUX = "SDUX" + SECB = "SECB" + SEMX = "SEMX" + SENX = "SENX" + SEUX = "SEUX" + SEWX = "SEWX" + SGCX = "SGCX" + SGLX = "SGLX" + SGSX = "SGSX" + SGUX = "SGUX" + SHAX = "SHAX" + SHBK = "SHBK" + SHBX = "SHBX" + SHCX = "SHCX" + SHEX = "SHEX" + SHGX = "SHGX" + SHIX = "SHIX" + SHKX = "SHKX" + SHMX = "SHMX" + SHNX = "SHNX" + SHOX = "SHOX" + SHRX = "SHRX" + SHSX = "SHSX" + SHUB = "SHUB" + SHUX = "SHUX" + SIBL = "SIBL" + SIBX = "SIBX" + SICX = "SICX" + SIDB = "SIDB" + SIDC = "SIDC" + SIDX = "SIDX" + SIGX = "SIGX" + SIHX = "SIHX" + SIKX = "SIKX" + SINX = "SINX" + SIRX = "SIRX" + SISX = "SISX" + SITX = "SITX" + SIWX = "SIWX" + SJGX = "SJGX" + SJSB = "SJSB" + SJSX = "SJSX" + SKCX = "SKCX" + SKKX = "SKKX" + SKNX = "SKNX" + SKSB = "SKSB" + SKUX = "SKUX" + SLAX = "SLAX" + SLCX = "SLCX" + SMBC = "SMBC" + SMBX = "SMBX" + SMCB = "SMCB" + SMCX = "SMCX" + SMEX = "SMEX" + SMMX = "SMMX" + SMNX = "SMNX" + SMPX = "SMPX" + SMSX = "SMSX" + SMTX = "SMTX" + SMUX = "SMUX" + SMVC = "SMVC" + SMWX = "SMWX" + SNAX = "SNAX" + SNBK = "SNBK" + SNBX = "SNBX" + SNCX = "SNCX" + SNDX = "SNDX" + SNGX = "SNGX" + SNKX = "SNKX" + SNLX = "SNLX" + SNPX = "SNPX" + SNSV = "SNSV" + SNSX = "SNSX" + SOBX = "SOBX" + SOGE = "SOGE" + SOLX = "SOLX" + SONX = "SONX" + SPBX = "SPBX" + SPCB = "SPCB" + SPCX = "SPCX" + SPNX = "SPNX" + SPSX = "SPSX" + SPTX = "SPTX" + SRCB = "SRCB" + SRCX = "SRCX" + SREX = "SREX" + SRGX = "SRGX" + SRHX = "SRHX" + SRSX = "SRSX" + SSBL = "SSBL" + SSBX = "SSBX" + SSDX = "SSDX" + SSHX = "SSHX" + SSKX = "SSKX" + SSLX = "SSLX" + SSNX = "SSNX" + SSOX = "SSOX" + SSSX = "SSSX" + SSWX = "SSWX" + STBP = "STBP" + STCB = "STCB" + STCX = "STCX" + STDX = "STDX" + STRX = "STRX" + SUBX = "SUBX" + SUCX = "SUCX" + SUDX = "SUDX" + SULX = "SULX" + SUMX = "SUMX" + SUNB = "SUNB" + SURX = "SURX" + SURY = "SURY" + SUSB = "SUSB" + SUSX = "SUSX" + SUTB = "SUTB" + SUVX = "SUVX" + SVAX = "SVAX" + SVBL = "SVBL" + SVCB = "SVCB" + SVCX = "SVCX" + SVGX = "SVGX" + SVMC = "SVMC" + SVNX = "SVNX" + SVOX = "SVOX" + SVRX = "SVRX" + SVSH = "SVSH" + SVSX = "SVSX" + SWMX = "SWMX" + SWSX = "SWSX" + SXXX = "SXXX" + SYNB = "SYNB" + TACX = "TACX" + TADX = "TADX" + TAMX = "TAMX" + TAPX = "TAPX" + TASX = "TASX" + TBCX = "TBCX" + TBDX = "TBDX" + TBHX = "TBHX" + TBMX = "TBMX" + TBNS = "TBNS" + TBPX = "TBPX" + TBSB = "TBSB" + TBSX = "TBSX" + TBTX = "TBTX" + TBUX = "TBUX" + TCBX = "TCBX" + TCCX = "TCCX" + TCHX = "TCHX" + TCPX = "TCPX" + TCUB = "TCUB" + TCUX = "TCUX" + TDBX = "TDBX" + TDCB = "TDCB" + TDCX = "TDCX" + TDIX = "TDIX" + TDMX = "TDMX" + TDPX = "TDPX" + TDSX = "TDSX" + TECX = "TECX" + TEHX = "TEHX" + TEMX = "TEMX" + TESX = "TESX" + TETX = "TETX" + TFCX = "TFCX" + TGBX = "TGBX" + TGCG = "TGCG" + TGCX = "TGCX" + TGDX = "TGDX" + TGMB = "TGMB" + TGNX = "TGNX" + TGUX = "TGUX" + THCX = "THCX" + THOX = "THOX" + THRS = "THRS" + THRX = "THRX" + THWX = "THWX" + TIDX = "TIDX" + TIRX = "TIRX" + TJAX = "TJAX" + TJBX = "TJBX" + TJCX = "TJCX" + TJDX = "TJDX" + TJMX = "TJMX" + TJNX = "TJNX" + TJSB = "TJSB" + TKAX = "TKAX" + TKCX = "TKCX" + TKDX = "TKDX" + TKTX = "TKTX" + TKUX = "TKUX" + TLPX = "TLPX" + TMAX = "TMAX" + TMBL = "TMBL" + TMBX = "TMBX" + TMCX = "TMCX" + TMNX = "TMNX" + TMPX = "TMPX" + TMSB = "TMSB" + TMSC = "TMSC" + TMSX = "TMSX" + TMTX = "TMTX" + TMUX = "TMUX" + TNBX = "TNBX" + TNCB = "TNCB" + TNCX = "TNCX" + TNDC = "TNDC" + TNEX = "TNEX" + TNHX = "TNHX" + TNIX = "TNIX" + TNKX = "TNKX" + TNMX = "TNMX" + TNSC = "TNSC" + TNUX = "TNUX" + TOCX = "TOCX" + TPCX = "TPCX" + TPDX = "TPDX" + TPSC = "TPSC" + TPSX = "TPSX" + TPUX = "TPUX" + TRAX = "TRAX" + TRDX = "TRDX" + TSAB = "TSAB" + TSAX = "TSAX" + TSBX = "TSBX" + TSCX = "TSCX" + TSDX = "TSDX" + TSIX = "TSIX" + TSMX = "TSMX" + TSNX = "TSNX" + TSPX = "TSPX" + TSSB = "TSSB" + TSSX = "TSSX" + TSUX = "TSUX" + TTBX = "TTBX" + TTCB = "TTCB" + TTCX = "TTCX" + TTGX = "TTGX" + TTLX = "TTLX" + TTUX = "TTUX" + TUBX = "TUBX" + TUCL = "TUCL" + TUCX = "TUCX" + TUDX = "TUDX" + TUMX = "TUMX" + TUNX = "TUNX" + TUOX = "TUOX" + TUPX = "TUPX" + TURX = "TURX" + TVBX = "TVBX" + TVDX = "TVDX" + TVPX = "TVPX" + TVUX = "TVUX" + TYCX = "TYCX" + UBBX = "UBBX" + UBGX = "UBGX" + UBIN = "UBIN" + UCBA = "UCBA" + UCBS = "UCBS" + UCBX = "UCBX" + UCCX = "UCCX" + UCDX = "UCDX" + UCUX = "UCUX" + UGBX = "UGBX" + UICX = "UICX" + UJSX = "UJSX" + UJVN = "UJVN" + UKGX = "UKGX" + UMAX = "UMAX" + UMCX = "UMCX" + UMSX = "UMSX" + UMUX = "UMUX" + UNAX = "UNAX" + UNIX = "UNIX" + UNMX = "UNMX" + UNSX = "UNSX" + UOVB = "UOVB" + UPCB = "UPCB" + UPCX = "UPCX" + URBN = "URBN" + URCX = "URCX" + URDX = "URDX" + URMX = "URMX" + UROX = "UROX" + USFB = "USFB" + USNX = "USNX" + UTBI = "UTBI" + UTBX = "UTBX" + UTCX = "UTCX" + UTGX = "UTGX" + UTIB = "UTIB" + UTKS = "UTKS" + UTKX = "UTKX" + UTZX = "UTZX" + UUCB = "UUCB" + UUCX = "UUCX" + VADX = "VADX" + VAIX = "VAIX" + VANX = "VANX" + VARA = "VARA" + VASJ = "VASJ" + VASX = "VASX" + VAUX = "VAUX" + VCAX = "VCAX" + VCBX = "VCBX" + VCCX = "VCCX" + VCNB = "VCNB" + VCOB = "VCOB" + VCOX = "VCOX" + VDCX = "VDCX" + VDYX = "VDYX" + VEDX = "VEDX" + VERX = "VERX" + VGBX = "VGBX" + VHDX = "VHDX" + VICX = "VICX" + VIDX = "VIDX" + VIJB = "VIJB" + VIJX = "VIJX" + VIKX = "VIKX" + VIMX = "VIMX" + VIRX = "VIRX" + VISX = "VISX" + VJSX = "VJSX" + VKCX = "VKCX" + VKSX = "VKSX" + VMCX = "VMCX" + VMMX = "VMMX" + VMUX = "VMUX" + VNSX = "VNSX" + VRDX = "VRDX" + VSBL = "VSBL" + VSBX = "VSBX" + VSCX = "VSCX" + VSSX = "VSSX" + VSVX = "VSVX" + VUCX = "VUCX" + VVCX = "VVCX" + VVSB = "VVSB" + VYAX = "VYAX" + VYPX = "VYPX" + VYSA = "VYSA" + WACX = "WACX" + WAIX = "WAIX" + WARX = "WARX" + WAUX = "WAUX" + WBSC = "WBSC" + WCBX = "WCBX" + WDCX = "WDCX" + WKGX = "WKGX" + WNBX = "WNBX" + WPAC = "WPAC" + WRCX = "WRCX" + WUCX = "WUCX" + WZUX = "WZUX" + XJKG = "XJKG" + YADX = "YADX" + YAVX = "YAVX" + YCBX = "YCBX" + YDCX = "YDCX" + YESB = "YESB" + YLNX = "YLNX" + YMSX = "YMSX" + YNCX = "YNCX" + YNSX = "YNSX" + ZBBX = "ZBBX" + ZBSX = "ZBSX" + ZCBL = "ZCBL" + ZIBX = "ZIBX" + ZILX = "ZILX" + ZIMX = "ZIMX" + ZISX = "ZISX" + ZLLX = "ZLLX" + ZMMX = "ZMMX" + ZRNB = "ZRNB" + ZSAX = "ZSAX" + ZSBG = "ZSBG" + ZSBL = "ZSBL" + ZSBX = "ZSBX" + ZSGX = "ZSGX" + ZSHX = "ZSHX" + ZSJX = "ZSJX" + ZSKX = "ZSKX" + ZSLX = "ZSLX" + ZSMX = "ZSMX" +) + +var bankCodes = map[string]bool{ + "AACX" : true, + "ABBL" : true, + "ABCX" : true, + "ABDX" : true, + "ABEX" : true, + "ABHY" : true, + "ABNA" : true, + "ABPB" : true, + "ABSB" : true, + "ABUX" : true, + "ACAX" : true, + "ACBX" : true, + "ACCX" : true, + "ACKX" : true, + "ACOX" : true, + "ACUB" : true, + "ACUX" : true, + "ADBX" : true, + "ADCB" : true, + "ADCC" : true, + "ADCX" : true, + "ADDX" : true, + "AGCX" : true, + "AGDX" : true, + "AGRX" : true, + "AGSX" : true, + "AGUX" : true, + "AGVX" : true, + "AHMX" : true, + "AHUX" : true, + "AIRP" : true, + "AJAR" : true, + "AJHC" : true, + "AJKB" : true, + "AJMX" : true, + "AJNX" : true, + "AJPX" : true, + "AJSX" : true, + "AJUX" : true, + "AKJB" : true, + "AKKB" : true, + "AKMX" : true, + "AKOX" : true, + "ALAX" : true, + "ALIX" : true, + "ALLA" : true, + "ALLX" : true, + "ALWX" : true, + "AMAX" : true, + "AMBX" : true, + "AMCB" : true, + "AMCX" : true, + "AMDN" : true, + "AMMX" : true, + "AMNX" : true, + "AMRX" : true, + "AMSB" : true, + "AMSX" : true, + "ANBX" : true, + "ANDB" : true, + "ANDX" : true, + "ANMX" : true, + "ANSX" : true, + "ANUX" : true, + "ANZB" : true, + "APBL" : true, + "APCX" : true, + "APGB" : true, + "APGV" : true, + "APGX" : true, + "APJX" : true, + "APMC" : true, + "APMX" : true, + "APNX" : true, + "APRX" : true, + "APSX" : true, + "ARBL" : true, + "ARCX" : true, + "ARMX" : true, + "ARYX" : true, + "ASBL" : true, + "ASBX" : true, + "ASHX" : true, + "ASKX" : true, + "ASNX" : true, + "ASOX" : true, + "ASSX" : true, + "AUBL" : true, + "AUBX" : true, + "AUCB" : true, + "AUCX" : true, + "AUGX" : true, + "AURX" : true, + "AVDX" : true, + "AWCX" : true, + "AWUX" : true, + "AZAX" : true, + "AZPX" : true, + "AZSX" : true, + "AZUX" : true, + "BACB" : true, + "BACX" : true, + "BADX" : true, + "BALX" : true, + "BANX" : true, + "BARA" : true, + "BARB" : true, + "BARC" : true, + "BARX" : true, + "BASX" : true, + "BAUX" : true, + "BAVX" : true, + "BBKM" : true, + "BBLX" : true, + "BBRX" : true, + "BBSX" : true, + "BBUX" : true, + "BBVX" : true, + "BCBM" : true, + "BCBX" : true, + "BCCB" : true, + "BCCX" : true, + "BCEX" : true, + "BCEY" : true, + "BCOX" : true, + "BCUB" : true, + "BCUX" : true, + "BDBB" : true, + "BDBL" : true, + "BDBX" : true, + "BDCX" : true, + "BDDX" : true, + "BDIX" : true, + "BDNX" : true, + "BDOX" : true, + "BDUX" : true, + "BEDX" : true, + "BELX" : true, + "BFUX" : true, + "BGBX" : true, + "BGCX" : true, + "BGGX" : true, + "BGUX" : true, + "BGVX" : true, + "BHAX" : true, + "BHBX" : true, + "BHCX" : true, + "BHDX" : true, + "BHEX" : true, + "BHGX" : true, + "BHIX" : true, + "BHJX" : true, + "BHMX" : true, + "BHOX" : true, + "BHRX" : true, + "BHSX" : true, + "BHTX" : true, + "BHUX" : true, + "BHWX" : true, + "BJUX" : true, + "BKCX" : true, + "BKDN" : true, + "BKDX" : true, + "BKID" : true, + "BKSX" : true, + "BLGX" : true, + "BMBL" : true, + "BMCB" : true, + "BMCX" : true, + "BMPX" : true, + "BMSX" : true, + "BNBX" : true, + "BNCX" : true, + "BNPA" : true, + "BNSB" : true, + "BNSX" : true, + "BODX" : true, + "BOFA" : true, + "BORX" : true, + "BOTM" : true, + "BOTX" : true, + "BPCX" : true, + "BPSX" : true, + "BRCX" : true, + "BRDX" : true, + "BRGX" : true, + "BRMX" : true, + "BRSX" : true, + "BRUX" : true, + "BSBX" : true, + "BSCX" : true, + "BTCX" : true, + "BTUX" : true, + "BUBX" : true, + "BUCL" : true, + "BUCX" : true, + "BUGX" : true, + "BUNX" : true, + "BURX" : true, + "BUSX" : true, + "BUZX" : true, + "BVNX" : true, + "BVSX" : true, + "BWCX" : true, + "CALX" : true, + "CBHX" : true, + "CBIN" : true, + "CCBL" : true, + "CCBX" : true, + "CCCX" : true, + "CCMX" : true, + "CCOB" : true, + "CCUX" : true, + "CDCX" : true, + "CEBX" : true, + "CGBX" : true, + "CGGX" : true, + "CHAS" : true, + "CHAX" : true, + "CHBX" : true, + "CHCX" : true, + "CHDX" : true, + "CHIX" : true, + "CHKX" : true, + "CHPX" : true, + "CHRX" : true, + "CHSX" : true, + "CHTX" : true, + "CIDX" : true, + "CITI" : true, + "CITX" : true, + "CIUB" : true, + "CJAX" : true, + "CJMX" : true, + "CLBL" : true, + "CMCB" : true, + "CMCX" : true, + "CMDX" : true, + "CMLX" : true, + "CMPX" : true, + "CNRB" : true, + "CNSX" : true, + "COAS" : true, + "COCX" : true, + "COLX" : true, + "COMX" : true, + "CONX" : true, + "CORP" : true, + "COSB" : true, + "CPDX" : true, + "CPSN" : true, + "CRBX" : true, + "CRES" : true, + "CRLY" : true, + "CRSX" : true, + "CRUB" : true, + "CSBK" : true, + "CSBX" : true, + "CTBA" : true, + "CTBX" : true, + "CTCB" : true, + "CTOX" : true, + "CTUX" : true, + "CUBX" : true, + "CUCX" : true, + "CURX" : true, + "CZCX" : true, + "CZUX" : true, + "DAAX" : true, + "DAHX" : true, + "DAUX" : true, + "DBAX" : true, + "DBSS" : true, + "DCBL" : true, + "DCBX" : true, + "DCCX" : true, + "DCDX" : true, + "DCEX" : true, + "DCKX" : true, + "DCMX" : true, + "DCNX" : true, + "DCPX" : true, + "DCSX" : true, + "DCTX" : true, + "DCUX" : true, + "DDBX" : true, + "DDCX" : true, + "DDDX" : true, + "DDHX" : true, + "DEGX" : true, + "DENS" : true, + "DEOB" : true, + "DEOX" : true, + "DEUT" : true, + "DEUX" : true, + "DEVX" : true, + "DGBX" : true, + "DHBX" : true, + "DHKX" : true, + "DHUX" : true, + "DIBX" : true, + "DICG" : true, + "DICX" : true, + "DIUX" : true, + "DJCX" : true, + "DKCL" : true, + "DKSX" : true, + "DLSC" : true, + "DLXB" : true, + "DMCB" : true, + "DMCX" : true, + "DMKB" : true, + "DMKJ" : true, + "DNDC" : true, + "DNSB" : true, + "DNSX" : true, + "DOBX" : true, + "DOHB" : true, + "DRGX" : true, + "DSBX" : true, + "DSCB" : true, + "DSHX" : true, + "DSPX" : true, + "DSUX" : true, + "DTCX" : true, + "DTPX" : true, + "DUCX" : true, + "DUMX" : true, + "DUNX" : true, + "DURG" : true, + "DVDX" : true, + "DYPX" : true, + "EBIL" : true, + "ECBL" : true, + "EDBX" : true, + "EDCX" : true, + "EDSX" : true, + "EIBI" : true, + "ESAF" : true, + "ESFB" : true, + "ESMF" : true, + "ETCX" : true, + "ETDX" : true, + "EUCX" : true, + "EWCX" : true, + "FCBX" : true, + "FCCX" : true, + "FCOX" : true, + "FDFX" : true, + "FDRL" : true, + "FEKX" : true, + "FGCB" : true, + "FINF" : true, + "FINO" : true, + "FINX" : true, + "FIRN" : true, + "FIRX" : true, + "FMCX" : true, + "FRIX" : true, + "FSCX" : true, + "FSFB" : true, + "FZCX" : true, + "FZSX" : true, + "GACX" : true, + "GADX" : true, + "GANX" : true, + "GBCB" : true, + "GCBX" : true, + "GCCX" : true, + "GCUL" : true, + "GCUX" : true, + "GDCB" : true, + "GDCX" : true, + "GDDX" : true, + "GDUX" : true, + "GGBK" : true, + "GGCX" : true, + "GHPX" : true, + "GKNX" : true, + "GMBX" : true, + "GMCX" : true, + "GMUX" : true, + "GNCX" : true, + "GNSX" : true, + "GODX" : true, + "GOSX" : true, + "GPCX" : true, + "GPOX" : true, + "GRAX" : true, + "GSBL" : true, + "GSBX" : true, + "GSCB" : true, + "GSCX" : true, + "GSSX" : true, + "GTCX" : true, + "GUBX" : true, + "GUCX" : true, + "GUNX" : true, + "GUOX" : true, + "HAMX" : true, + "HANX" : true, + "HARC" : true, + "HCBL" : true, + "HCBX" : true, + "HCCX" : true, + "HCLX" : true, + "HDCL" : true, + "HDCX" : true, + "HDFC" : true, + "HGBX" : true, + "HINX" : true, + "HISX" : true, + "HMBX" : true, + "HMNX" : true, + "HOCX" : true, + "HOOX" : true, + "HPCX" : true, + "HPSC" : true, + "HPSX" : true, + "HSBC" : true, + "HSBM" : true, + "HSBX" : true, + "HSCX" : true, + "HSDX" : true, + "HSSX" : true, + "HUBX" : true, + "HUCB" : true, + "HUCH" : true, + "HUCX" : true, + "HUTX" : true, + "HVBK" : true, + "IBBK" : true, + "IBKL" : true, + "IBKO" : true, + "ICBK" : true, + "ICBL" : true, + "ICHX" : true, + "ICIC" : true, + "ICLL" : true, + "ICMX" : true, + "IDFB" : true, + "IDIB" : true, + "IDUK" : true, + "IDUX" : true, + "ILCB" : true, + "IMCX" : true, + "IMPX" : true, + "INCX" : true, + "INDB" : true, + "INDX" : true, + "IOBA" : true, + "IPCX" : true, + "IPOS" : true, + "IPPB" : true, + "IPSX" : true, + "ISBX" : true, + "ISMX" : true, + "ITBL" : true, + "ITCX" : true, + "ITDX" : true, + "IUCB" : true, + "IUCX" : true, + "JACX" : true, + "JAKA" : true, + "JALX" : true, + "JAMX" : true, + "JANA" : true, + "JANX" : true, + "JASB" : true, + "JASX" : true, + "JAUX" : true, + "JBHX" : true, + "JBIX" : true, + "JBMX" : true, + "JCBX" : true, + "JCCB" : true, + "JCCX" : true, + "JCDX" : true, + "JCHX" : true, + "JCPX" : true, + "JCUX" : true, + "JDCX" : true, + "JDEX" : true, + "JGBX" : true, + "JGCX" : true, + "JGWX" : true, + "JHAX" : true, + "JHSX" : true, + "JHUX" : true, + "JIBX" : true, + "JICX" : true, + "JIDX" : true, + "JIGX" : true, + "JIKX" : true, + "JIMX" : true, + "JINX" : true, + "JIOP" : true, + "JIOX" : true, + "JIRX" : true, + "JISX" : true, + "JIVX" : true, + "JJCX" : true, + "JJHX" : true, + "JJSB" : true, + "JKAX" : true, + "JKCX" : true, + "JKDX" : true, + "JKEX" : true, + "JKHX" : true, + "JKMX" : true, + "JKRX" : true, + "JKSX" : true, + "JLCX" : true, + "JLDX" : true, + "JLNX" : true, + "JLSX" : true, + "JLWX" : true, + "JMAX" : true, + "JMBX" : true, + "JMCX" : true, + "JMDX" : true, + "JMHX" : true, + "JMMX" : true, + "JMOX" : true, + "JMPX" : true, + "JMSX" : true, + "JMYX" : true, + "JNAX" : true, + "JNDX" : true, + "JNSX" : true, + "JODX" : true, + "JONX" : true, + "JOWX" : true, + "JPAX" : true, + "JPCB" : true, + "JPCX" : true, + "JRAX" : true, + "JRKX" : true, + "JRNX" : true, + "JRSX" : true, + "JSAB" : true, + "JSAX" : true, + "JSBL" : true, + "JSBP" : true, + "JSBX" : true, + "JSCX" : true, + "JSDX" : true, + "JSEX" : true, + "JSFB" : true, + "JSHX" : true, + "JSKX" : true, + "JSMX" : true, + "JSOX" : true, + "JSRX" : true, + "JSTX" : true, + "JSVX" : true, + "JSWX" : true, + "JTIX" : true, + "JTSC" : true, + "JTSX" : true, + "JUCX" : true, + "JUSX" : true, + "JVCX" : true, + "KAAX" : true, + "KACE" : true, + "KACX" : true, + "KADX" : true, + "KAGX" : true, + "KAIJ" : true, + "KALX" : true, + "KAMX" : true, + "KANG" : true, + "KANX" : true, + "KARB" : true, + "KARX" : true, + "KASX" : true, + "KATX" : true, + "KAYX" : true, + "KBCX" : true, + "KBKB" : true, + "KBNX" : true, + "KBSX" : true, + "KCBL" : true, + "KCBX" : true, + "KCCB" : true, + "KCCX" : true, + "KCDX" : true, + "KCEX" : true, + "KCOB" : true, + "KCUB" : true, + "KCUX" : true, + "KDBX" : true, + "KDCB" : true, + "KDCX" : true, + "KDIX" : true, + "KDNX" : true, + "KDUX" : true, + "KEMX" : true, + "KESX" : true, + "KGBX" : true, + "KGDX" : true, + "KGRB" : true, + "KGSX" : true, + "KHAX" : true, + "KHCX" : true, + "KHDX" : true, + "KHNX" : true, + "KHUX" : true, + "KICX" : true, + "KJSB" : true, + "KJSX" : true, + "KKBK" : true, + "KKMX" : true, + "KKSX" : true, + "KLGB" : true, + "KLMX" : true, + "KMCB" : true, + "KMCX" : true, + "KMNX" : true, + "KMSX" : true, + "KNBX" : true, + "KNCX" : true, + "KNNX" : true, + "KNPX" : true, + "KNSB" : true, + "KNSX" : true, + "KOBX" : true, + "KOCX" : true, + "KODX" : true, + "KOEX" : true, + "KOLH" : true, + "KOSX" : true, + "KOTX" : true, + "KOYX" : true, + "KPCX" : true, + "KRCX" : true, + "KRDX" : true, + "KRIX" : true, + "KRMX" : true, + "KRNX" : true, + "KRTH" : true, + "KRTX" : true, + "KSBK" : true, + "KSBX" : true, + "KSCB" : true, + "KSCX" : true, + "KSMX" : true, + "KSNX" : true, + "KSTX" : true, + "KSUX" : true, + "KTBX" : true, + "KTCX" : true, + "KTDX" : true, + "KTTX" : true, + "KUBX" : true, + "KUCB" : true, + "KUCX" : true, + "KUKX" : true, + "KULX" : true, + "KUMX" : true, + "KUNS" : true, + "KURX" : true, + "KVBL" : true, + "KVCX" : true, + "KVGB" : true, + "KYDX" : true, + "LACX" : true, + "LATX" : true, + "LAVB" : true, + "LBMX" : true, + "LCBX" : true, + "LCCX" : true, + "LDCX" : true, + "LDPX" : true, + "LDRX" : true, + "LECX" : true, + "LICB" : true, + "LKBL" : true, + "LKCX" : true, + "LKHX" : true, + "LKMX" : true, + "LMNX" : true, + "LNSX" : true, + "LOKX" : true, + "LONX" : true, + "LUCX" : true, + "LULX" : true, + "MABL" : true, + "MACX" : true, + "MADX" : true, + "MAGX" : true, + "MAHB" : true, + "MAHG" : true, + "MAHX" : true, + "MAJX" : true, + "MAKX" : true, + "MALX" : true, + "MAMX" : true, + "MANX" : true, + "MAPX" : true, + "MASX" : true, + "MAUX" : true, + "MAVX" : true, + "MAWX" : true, + "MAYX" : true, + "MBCX" : true, + "MBGX" : true, + "MBLX" : true, + "MCAX" : true, + "MCBL" : true, + "MCBX" : true, + "MCCX" : true, + "MCDX" : true, + "MCLX" : true, + "MCOX" : true, + "MCSX" : true, + "MCUX" : true, + "MDBK" : true, + "MDCB" : true, + "MDCX" : true, + "MDEX" : true, + "MDGX" : true, + "MDIX" : true, + "MDMX" : true, + "MDPX" : true, + "MEDX" : true, + "MERX" : true, + "MEUX" : true, + "MFCX" : true, + "MFUX" : true, + "MGBX" : true, + "MGCB" : true, + "MGCX" : true, + "MGDX" : true, + "MGRB" : true, + "MGSX" : true, + "MGUX" : true, + "MHCB" : true, + "MHCX" : true, + "MHEX" : true, + "MHLX" : true, + "MHMX" : true, + "MHNX" : true, + "MHSX" : true, + "MHUX" : true, + "MIZX" : true, + "MJCX" : true, + "MKPB" : true, + "MKUX" : true, + "MKYX" : true, + "MLCG" : true, + "MLDX" : true, + "MMCX" : true, + "MMMX" : true, + "MNBX" : true, + "MNCX" : true, + "MNSX" : true, + "MOGX" : true, + "MOUX" : true, + "MPCX" : true, + "MPDX" : true, + "MPRX" : true, + "MRBX" : true, + "MRTX" : true, + "MSAX" : true, + "MSBL" : true, + "MSBX" : true, + "MSCI" : true, + "MSCX" : true, + "MSHQ" : true, + "MSLM" : true, + "MSNU" : true, + "MSNX" : true, + "MSOX" : true, + "MSSX" : true, + "MUBL" : true, + "MUBX" : true, + "MUCG" : true, + "MUCX" : true, + "MUDX" : true, + "MUNX" : true, + "MUPX" : true, + "MURX" : true, + "MUSX" : true, + "MVCB" : true, + "MVCX" : true, + "MVIX" : true, + "MYAX" : true, + "MYSX" : true, + "MZCX" : true, + "MZRX" : true, + "NABX" : true, + "NACX" : true, + "NAGX" : true, + "NAIX" : true, + "NALX" : true, + "NANX" : true, + "NASX" : true, + "NATA" : true, + "NAUX" : true, + "NAVX" : true, + "NAWX" : true, + "NBAD" : true, + "NBBX" : true, + "NBCX" : true, + "NBMX" : true, + "NBRD" : true, + "NCBL" : true, + "NCBX" : true, + "NCCX" : true, + "NCOX" : true, + "NCUB" : true, + "NCUX" : true, + "NDCB" : true, + "NDCX" : true, + "NDDX" : true, + "NDGX" : true, + "NDIX" : true, + "NESF" : true, + "NEYX" : true, + "NGBX" : true, + "NGKX" : true, + "NGNX" : true, + "NGRX" : true, + "NGSB" : true, + "NGSX" : true, + "NICB" : true, + "NIDX" : true, + "NILX" : true, + "NIRX" : true, + "NIUX" : true, + "NJBK" : true, + "NJCX" : true, + "NJGX" : true, + "NJMX" : true, + "NJSX" : true, + "NKGS" : true, + "NLGX" : true, + "NLUX" : true, + "NMCB" : true, + "NMCX" : true, + "NMGB" : true, + "NNCX" : true, + "NNSB" : true, + "NNSX" : true, + "NOBX" : true, + "NOIX" : true, + "NOSC" : true, + "NPCX" : true, + "NPKX" : true, + "NRDX" : true, + "NRMX" : true, + "NSBB" : true, + "NSBX" : true, + "NSCX" : true, + "NSGX" : true, + "NSIX" : true, + "NSJX" : true, + "NSMX" : true, + "NSPB" : true, + "NSPX" : true, + "NSRX" : true, + "NTBL" : true, + "NUBX" : true, + "NUCB" : true, + "NUCX" : true, + "NVCX" : true, + "NVNM" : true, + "NVSX" : true, + "NWCX" : true, + "ODCX" : true, + "ODGB" : true, + "OIBA" : true, + "OMCX" : true, + "ONSX" : true, + "ORBC" : true, + "ORCB" : true, + "OSMX" : true, + "PABX" : true, + "PACX" : true, + "PADX" : true, + "PALX" : true, + "PANX" : true, + "PARX" : true, + "PASX" : true, + "PATX" : true, + "PAYX" : true, + "PBGX" : true, + "PCBL" : true, + "PCBX" : true, + "PCCB" : true, + "PCCX" : true, + "PCLX" : true, + "PCMX" : true, + "PCOX" : true, + "PCPX" : true, + "PCSX" : true, + "PCTX" : true, + "PCUX" : true, + "PDBX" : true, + "PDCX" : true, + "PDNX" : true, + "PDSX" : true, + "PDUX" : true, + "PGBX" : true, + "PGCX" : true, + "PGRX" : true, + "PGTX" : true, + "PITX" : true, + "PJSB" : true, + "PKBX" : true, + "PKDX" : true, + "PKGB" : true, + "PLOX" : true, + "PLUX" : true, + "PMCB" : true, + "PMCX" : true, + "PMEC" : true, + "PMNX" : true, + "PNCX" : true, + "PNMX" : true, + "PNPX" : true, + "PNSX" : true, + "PPBX" : true, + "PPCX" : true, + "PRCX" : true, + "PREX" : true, + "PROX" : true, + "PRPX" : true, + "PRSX" : true, + "PRTH" : true, + "PSBX" : true, + "PSCX" : true, + "PSIB" : true, + "PSRX" : true, + "PSSX" : true, + "PTCX" : true, + "PTNX" : true, + "PTSX" : true, + "PUBX" : true, + "PUCB" : true, + "PUCX" : true, + "PUDX" : true, + "PUGX" : true, + "PUNB" : true, + "PUNX" : true, + "PURX" : true, + "PUSD" : true, + "PUSX" : true, + "PUUX" : true, + "PVAX" : true, + "PVCX" : true, + "PVVX" : true, + "PWUX" : true, + "PYCX" : true, + "PYTM" : true, + "QNBA" : true, + "QUCX" : true, + "RABO" : true, + "RACX" : true, + "RAEX" : true, + "RAJX" : true, + "RAKX" : true, + "RAMX" : true, + "RANX" : true, + "RATN" : true, + "RAUX" : true, + "RBBX" : true, + "RBCX" : true, + "RBIH" : true, + "RBIN" : true, + "RBIP" : true, + "RBIS" : true, + "RCBX" : true, + "RCCX" : true, + "RCDX" : true, + "RCMX" : true, + "RCUX" : true, + "RDCB" : true, + "RDCX" : true, + "RDNX" : true, + "REBX" : true, + "RECX" : true, + "REWX" : true, + "RGCX" : true, + "RGSX" : true, + "RHMX" : true, + "RJCX" : true, + "RJJX" : true, + "RJNX" : true, + "RJTX" : true, + "RKCX" : true, + "RLUX" : true, + "RMGB" : true, + "RNBX" : true, + "RNDX" : true, + "RNGX" : true, + "RNSB" : true, + "RNSX" : true, + "ROCX" : true, + "ROHX" : true, + "RPUX" : true, + "RRBP" : true, + "RRSX" : true, + "RSBL" : true, + "RSBX" : true, + "RSCB" : true, + "RSSB" : true, + "RSSX" : true, + "RSUX" : true, + "RSVX" : true, + "RUCX" : true, + "RUKX" : true, + "RUMX" : true, + "RZSX" : true, + "SABR" : true, + "SABX" : true, + "SACB" : true, + "SACX" : true, + "SADX" : true, + "SAGX" : true, + "SAHE" : true, + "SAHX" : true, + "SAIX" : true, + "SALX" : true, + "SAMX" : true, + "SANT" : true, + "SANX" : true, + "SAPX" : true, + "SARX" : true, + "SASA" : true, + "SASX" : true, + "SATX" : true, + "SAUX" : true, + "SAVX" : true, + "SAWX" : true, + "SBBJ" : true, + "SBCR" : true, + "SBCX" : true, + "SBHY" : true, + "SBIN" : true, + "SBKX" : true, + "SBLD" : true, + "SBLS" : true, + "SBLX" : true, + "SBMX" : true, + "SBMY" : true, + "SBNX" : true, + "SBPS" : true, + "SBPX" : true, + "SBSX" : true, + "SBTR" : true, + "SBUJ" : true, + "SBUX" : true, + "SCBL" : true, + "SCBX" : true, + "SCCX" : true, + "SCDX" : true, + "SCIX" : true, + "SCNX" : true, + "SCOB" : true, + "SCOX" : true, + "SCPX" : true, + "SCSX" : true, + "SCUX" : true, + "SDBX" : true, + "SDCB" : true, + "SDCE" : true, + "SDCX" : true, + "SDHX" : true, + "SDSX" : true, + "SDTC" : true, + "SDTX" : true, + "SDUX" : true, + "SECB" : true, + "SEMX" : true, + "SENX" : true, + "SEUX" : true, + "SEWX" : true, + "SGCX" : true, + "SGLX" : true, + "SGSX" : true, + "SGUX" : true, + "SHAX" : true, + "SHBK" : true, + "SHBX" : true, + "SHCX" : true, + "SHEX" : true, + "SHGX" : true, + "SHIX" : true, + "SHKX" : true, + "SHMX" : true, + "SHNX" : true, + "SHOX" : true, + "SHRX" : true, + "SHSX" : true, + "SHUB" : true, + "SHUX" : true, + "SIBL" : true, + "SIBX" : true, + "SICX" : true, + "SIDB" : true, + "SIDC" : true, + "SIDX" : true, + "SIGX" : true, + "SIHX" : true, + "SIKX" : true, + "SINX" : true, + "SIRX" : true, + "SISX" : true, + "SITX" : true, + "SIWX" : true, + "SJGX" : true, + "SJSB" : true, + "SJSX" : true, + "SKCX" : true, + "SKKX" : true, + "SKNX" : true, + "SKSB" : true, + "SKUX" : true, + "SLAX" : true, + "SLCX" : true, + "SMBC" : true, + "SMBX" : true, + "SMCB" : true, + "SMCX" : true, + "SMEX" : true, + "SMMX" : true, + "SMNX" : true, + "SMPX" : true, + "SMSX" : true, + "SMTX" : true, + "SMUX" : true, + "SMVC" : true, + "SMWX" : true, + "SNAX" : true, + "SNBK" : true, + "SNBX" : true, + "SNCX" : true, + "SNDX" : true, + "SNGX" : true, + "SNKX" : true, + "SNLX" : true, + "SNPX" : true, + "SNSV" : true, + "SNSX" : true, + "SOBX" : true, + "SOGE" : true, + "SOLX" : true, + "SONX" : true, + "SPBX" : true, + "SPCB" : true, + "SPCX" : true, + "SPNX" : true, + "SPSX" : true, + "SPTX" : true, + "SRCB" : true, + "SRCX" : true, + "SREX" : true, + "SRGX" : true, + "SRHX" : true, + "SRSX" : true, + "SSBL" : true, + "SSBX" : true, + "SSDX" : true, + "SSHX" : true, + "SSKX" : true, + "SSLX" : true, + "SSNX" : true, + "SSOX" : true, + "SSSX" : true, + "SSWX" : true, + "STBP" : true, + "STCB" : true, + "STCX" : true, + "STDX" : true, + "STRX" : true, + "SUBX" : true, + "SUCX" : true, + "SUDX" : true, + "SULX" : true, + "SUMX" : true, + "SUNB" : true, + "SURX" : true, + "SURY" : true, + "SUSB" : true, + "SUSX" : true, + "SUTB" : true, + "SUVX" : true, + "SVAX" : true, + "SVBL" : true, + "SVCB" : true, + "SVCX" : true, + "SVGX" : true, + "SVMC" : true, + "SVNX" : true, + "SVOX" : true, + "SVRX" : true, + "SVSH" : true, + "SVSX" : true, + "SWMX" : true, + "SWSX" : true, + "SXXX" : true, + "SYNB" : true, + "TACX" : true, + "TADX" : true, + "TAMX" : true, + "TAPX" : true, + "TASX" : true, + "TBCX" : true, + "TBDX" : true, + "TBHX" : true, + "TBMX" : true, + "TBNS" : true, + "TBPX" : true, + "TBSB" : true, + "TBSX" : true, + "TBTX" : true, + "TBUX" : true, + "TCBX" : true, + "TCCX" : true, + "TCHX" : true, + "TCPX" : true, + "TCUB" : true, + "TCUX" : true, + "TDBX" : true, + "TDCB" : true, + "TDCX" : true, + "TDIX" : true, + "TDMX" : true, + "TDPX" : true, + "TDSX" : true, + "TECX" : true, + "TEHX" : true, + "TEMX" : true, + "TESX" : true, + "TETX" : true, + "TFCX" : true, + "TGBX" : true, + "TGCG" : true, + "TGCX" : true, + "TGDX" : true, + "TGMB" : true, + "TGNX" : true, + "TGUX" : true, + "THCX" : true, + "THOX" : true, + "THRS" : true, + "THRX" : true, + "THWX" : true, + "TIDX" : true, + "TIRX" : true, + "TJAX" : true, + "TJBX" : true, + "TJCX" : true, + "TJDX" : true, + "TJMX" : true, + "TJNX" : true, + "TJSB" : true, + "TKAX" : true, + "TKCX" : true, + "TKDX" : true, + "TKTX" : true, + "TKUX" : true, + "TLPX" : true, + "TMAX" : true, + "TMBL" : true, + "TMBX" : true, + "TMCX" : true, + "TMNX" : true, + "TMPX" : true, + "TMSB" : true, + "TMSC" : true, + "TMSX" : true, + "TMTX" : true, + "TMUX" : true, + "TNBX" : true, + "TNCB" : true, + "TNCX" : true, + "TNDC" : true, + "TNEX" : true, + "TNHX" : true, + "TNIX" : true, + "TNKX" : true, + "TNMX" : true, + "TNSC" : true, + "TNUX" : true, + "TOCX" : true, + "TPCX" : true, + "TPDX" : true, + "TPSC" : true, + "TPSX" : true, + "TPUX" : true, + "TRAX" : true, + "TRDX" : true, + "TSAB" : true, + "TSAX" : true, + "TSBX" : true, + "TSCX" : true, + "TSDX" : true, + "TSIX" : true, + "TSMX" : true, + "TSNX" : true, + "TSPX" : true, + "TSSB" : true, + "TSSX" : true, + "TSUX" : true, + "TTBX" : true, + "TTCB" : true, + "TTCX" : true, + "TTGX" : true, + "TTLX" : true, + "TTUX" : true, + "TUBX" : true, + "TUCL" : true, + "TUCX" : true, + "TUDX" : true, + "TUMX" : true, + "TUNX" : true, + "TUOX" : true, + "TUPX" : true, + "TURX" : true, + "TVBX" : true, + "TVDX" : true, + "TVPX" : true, + "TVUX" : true, + "TYCX" : true, + "UBBX" : true, + "UBGX" : true, + "UBIN" : true, + "UCBA" : true, + "UCBS" : true, + "UCBX" : true, + "UCCX" : true, + "UCDX" : true, + "UCUX" : true, + "UGBX" : true, + "UICX" : true, + "UJSX" : true, + "UJVN" : true, + "UKGX" : true, + "UMAX" : true, + "UMCX" : true, + "UMSX" : true, + "UMUX" : true, + "UNAX" : true, + "UNIX" : true, + "UNMX" : true, + "UNSX" : true, + "UOVB" : true, + "UPCB" : true, + "UPCX" : true, + "URBN" : true, + "URCX" : true, + "URDX" : true, + "URMX" : true, + "UROX" : true, + "USFB" : true, + "USNX" : true, + "UTBI" : true, + "UTBX" : true, + "UTCX" : true, + "UTGX" : true, + "UTIB" : true, + "UTKS" : true, + "UTKX" : true, + "UTZX" : true, + "UUCB" : true, + "UUCX" : true, + "VADX" : true, + "VAIX" : true, + "VANX" : true, + "VARA" : true, + "VASJ" : true, + "VASX" : true, + "VAUX" : true, + "VCAX" : true, + "VCBX" : true, + "VCCX" : true, + "VCNB" : true, + "VCOB" : true, + "VCOX" : true, + "VDCX" : true, + "VDYX" : true, + "VEDX" : true, + "VERX" : true, + "VGBX" : true, + "VHDX" : true, + "VICX" : true, + "VIDX" : true, + "VIJB" : true, + "VIJX" : true, + "VIKX" : true, + "VIMX" : true, + "VIRX" : true, + "VISX" : true, + "VJSX" : true, + "VKCX" : true, + "VKSX" : true, + "VMCX" : true, + "VMMX" : true, + "VMUX" : true, + "VNSX" : true, + "VRDX" : true, + "VSBL" : true, + "VSBX" : true, + "VSCX" : true, + "VSSX" : true, + "VSVX" : true, + "VUCX" : true, + "VVCX" : true, + "VVSB" : true, + "VYAX" : true, + "VYPX" : true, + "VYSA" : true, + "WACX" : true, + "WAIX" : true, + "WARX" : true, + "WAUX" : true, + "WBSC" : true, + "WCBX" : true, + "WDCX" : true, + "WKGX" : true, + "WNBX" : true, + "WPAC" : true, + "WRCX" : true, + "WUCX" : true, + "WZUX" : true, + "XJKG" : true, + "YADX" : true, + "YAVX" : true, + "YCBX" : true, + "YDCX" : true, + "YESB" : true, + "YLNX" : true, + "YMSX" : true, + "YNCX" : true, + "YNSX" : true, + "ZBBX" : true, + "ZBSX" : true, + "ZCBL" : true, + "ZIBX" : true, + "ZILX" : true, + "ZIMX" : true, + "ZISX" : true, + "ZLLX" : true, + "ZMMX" : true, + "ZRNB" : true, + "ZSAX" : true, + "ZSBG" : true, + "ZSBL" : true, + "ZSBX" : true, + "ZSGX" : true, + "ZSHX" : true, + "ZSJX" : true, + "ZSKX" : true, + "ZSLX" : true, + "ZSMX" : true, +} \ No newline at end of file diff --git a/src/go/errors.go b/src/go/errors.go new file mode 100644 index 00000000..0374652c --- /dev/null +++ b/src/go/errors.go @@ -0,0 +1,8 @@ +package ifsc + +import "errors" + +var ( + ErrInvalidCode = errors.New("InvalidCode") + ErrInvalidResponse = errors.New("IFSC API returned invalid response") +) diff --git a/src/go/generator/constants.template b/src/go/generator/constants.template new file mode 100644 index 00000000..b4bf3267 --- /dev/null +++ b/src/go/generator/constants.template @@ -0,0 +1,13 @@ +/* +This file is autogenerated from generator/main.go parsing the php constants from +Bank.php +*/ +package ifsc + +const ({{ range .Value }} + {{ . }} = "{{ . }}"{{ end }} +) + +var bankCodes = map[string]bool{ {{ range .Value }} + "{{ . }}" : true, {{ end }} +} \ No newline at end of file diff --git a/src/go/generator/main.go b/src/go/generator/main.go new file mode 100644 index 00000000..d13b58d3 --- /dev/null +++ b/src/go/generator/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "errors" + "fmt" + "html/template" + "io" + "io/ioutil" + "log" + "os" + "path" + "regexp" + "runtime" +) + +type bankConstants struct { + Value []string +} + +func ExtractPhpConstant() (*bankConstants, error) { + re := regexp.MustCompile(`(?P[A-Z]{4}) =`) + _, fileN, _, ok := runtime.Caller(0) + if !ok { + return nil, errors.New("it was not possible to recover the information. Caller function error") + } + dir, _ := path.Split(fileN) + phpFilePath := path.Join(dir, "..", "..", "php", "Bank.php") + bytes, err := ioutil.ReadFile(phpFilePath) + if err != nil { + return nil, err + } + matches := re.FindAllStringSubmatch(string(bytes), -1) + var result bankConstants + for _, match := range matches { + result.Value = append(result.Value, match[1]) + } + return &result, nil +} + +func GenerateConstantsFile(writer io.Writer) error { + cwd, err := getCwd() + if err != nil { + log.Fatal(err) + } + templateFilePath := path.Join(cwd, "constants.template") + fileBytes, err := ioutil.ReadFile(templateFilePath) + if err != nil { + return err + } + constantsArr, err := ExtractPhpConstant() + if err != nil { + return err + } + t := template.Must(template.New("constants.template").Parse(string(fileBytes))) + t.Execute(writer, constantsArr) + fmt.Printf("added %d constants ", len(constantsArr.Value)) + return nil +} +func getCwd() (string, error) { + _, fileN, _, ok := runtime.Caller(0) + if !ok { + return "", errors.New("it was not possible to recover the information. Caller function error") + } + dir, _ := path.Split(fileN) + return dir, nil +} +func main() { + cwd, err := getCwd() + if err != nil { + log.Fatal(err) + } + constantsFilePath := path.Join(cwd, "..", "constants.go") + os.Remove(constantsFilePath) + file, err := os.Create(constantsFilePath) + if err != nil { + log.Fatal(err) + } + if err := GenerateConstantsFile(file); err != nil { + log.Printf("error generation constants file, err:%v", err) + } + fmt.Printf("saved to %v \n", constantsFilePath) +} diff --git a/src/go/generator/main_test.go b/src/go/generator/main_test.go new file mode 100644 index 00000000..7ef95907 --- /dev/null +++ b/src/go/generator/main_test.go @@ -0,0 +1,29 @@ +package main + +import ( + "bytes" + "io/ioutil" + "path" + "testing" +) + +func Test_verifyConstantFile(t *testing.T) { + var buff bytes.Buffer + if err := GenerateConstantsFile(&buff); err != nil { + t.Errorf("error generating constants file. error:%v", err) + } + dir, err := getCwd() + if err != nil { + t.Fatal(err) + } + constantsFilePath := path.Join(dir, "..", "constants.go") + fileData, err := ioutil.ReadFile(constantsFilePath) + if err != nil { + t.Errorf("error reading constants file, err:%v", err) + } + if buff.String() != string(fileData) { + t.Fatalf(`There is difference in constants file. Please regenerate the constants.go file and commit it.\n + Use make generate-constants`) + } + +} diff --git a/src/go/ifsc.go b/src/go/ifsc.go new file mode 100644 index 00000000..952569aa --- /dev/null +++ b/src/go/ifsc.go @@ -0,0 +1,167 @@ +package ifsc + +import ( + "encoding/json" + "errors" + "fmt" + "io/ioutil" + "log" + "path" + "runtime" + "strconv" + "strings" +) + +var ifscMap map[string][]Data +var bankNames map[string]string +var sublet map[string]string +var customSublets map[string]string + +type Data struct { + Value string +} + +func (d Data) MarshalJSON() ([]byte, error) { + return json.Marshal(d.Value) +} + +func (d *Data) UnmarshalJSON(input []byte) error { + var value int + if err := json.Unmarshal(input, &value); err != nil { + var value string + if err := json.Unmarshal(input, &value); err != nil { + return err + } + d.Value = value + return nil + } + d.Value = strconv.Itoa(value) + return nil +} + +func init() { + LoadBankData() + if ifscMap == nil { + if err := LoadFile("IFSC.json", &ifscMap, ""); err != nil { + log.Panic(fmt.Sprintf("there is some error in IFSC.json file: %v", err)) + } + } + if sublet == nil { + if err := LoadFile("sublet.json", &sublet, ""); err != nil { + log.Panic(fmt.Sprintf("there is some error in sublet.json file: %v", err)) + } + } + if customSublets == nil { + if err := LoadFile("custom-sublets.json", &customSublets, ""); err != nil { + log.Panic(fmt.Sprintf("there is some error in custom-sublets.json file: %v", err)) + } + } + if bankNames == nil { + if err := LoadFile("banknames.json", &bankNames, ""); err != nil { + log.Panic(fmt.Sprintf("there is some error in banknames.json file: %v", err)) + } + } +} + +func LoadFile(fileName string, result interface{}, fullDirPath string) error { + _, fileN, _, ok := runtime.Caller(0) + if !ok { + return errors.New("it was not possible to recover the information. Caller function error") + } + dir, _ := path.Split(fileN) + jsonDir := path.Join(dir, "..") + var completePath string + if fullDirPath != "" { + completePath = path.Join(fullDirPath, fileName) + } else { + completePath = path.Join(jsonDir, fileName) + + } + bytes, err := ioutil.ReadFile(completePath) + if err != nil { + return err + } + if err := json.Unmarshal(bytes, &result); err != nil { + return err + } + return nil +} + +func Validate(code string) bool { + if len(code) != 11 || string(code[4]) != "0" { + return false + } + bankCode := strings.ToUpper(code[0:4]) + branchCode := strings.ToUpper(code[5:]) + list, ok := ifscMap[bankCode] + if !ok { + return false + } + branchData, err := getData(branchCode) + if err != nil { + return false + } + for _, data := range list { + if data == *branchData { + return true + } + } + return false +} + +func getData(input string) (*Data, error) { + var inputBytes []byte + var err error + intValue, err := strconv.ParseInt(input, 10, 32) + if err == nil { + input = strconv.Itoa(int(intValue)) + } + if inputBytes, err = json.Marshal(input); err != nil { + return nil, err + } + var output Data + if err := json.Unmarshal(inputBytes, &output); err != nil { + return nil, err + } + return &output, nil +} + +func GetBankName(code string) (string, error) { + bankName, ok := bankNames[code] + if !ok { + if Validate(code) { + bankCode, ok := sublet[code] + if !ok { + bankName, err := getCustomSubletName(code) + if err != nil { + bankName, _ := bankNames[code[0:4]] + return bankName, nil + } else { + return bankName, nil + } + } + return bankNames[bankCode], nil + + } else { + return "", errors.New("invalid bank code") + } + } + return bankName, nil +} +func getCustomSubletName(code string) (string, error) { + for key, value := range customSublets { + if len(code) >= len(key) && code[0:len(key)] == key { + bankName, ok := bankNames[value] + if !ok { + return value, nil + } + return bankName, nil + } + } + return "", errors.New("custom sublet name not found") +} + +func ValidateBankCode(bankCodeInput string) bool { + _, ok := bankCodes[bankCodeInput] + return ok +} diff --git a/src/go/ifsc_test.go b/src/go/ifsc_test.go new file mode 100644 index 00000000..95858037 --- /dev/null +++ b/src/go/ifsc_test.go @@ -0,0 +1,119 @@ +package ifsc + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetBankName_BankName(t *testing.T) { + assert := assert.New(t) + actual, ierr := GetBankName("WBSC0DJCB01") + assert.Nil(ierr) + assert.Equal("Darjeeling District Central Co-operative Bank", actual) +} + +func TestGetBankName_Sublet(t *testing.T) { + assert := assert.New(t) + fixtureData := getSubletFixture() + for input, _ := range fixtureData { + ownerBankCode := input[0:4] + actual, err := GetBankName(input) + assert.Nil(err) + expected, err := GetBankName(ownerBankCode) + assert.Nil(err) + assert.Equal(expected, actual) + } +} +func TestGetBankName_CustomSublet_Success(t *testing.T) { + assert := assert.New(t) + fixtureData := getCustomSubletFixture() + for input, expected := range fixtureData { + actual, err := GetBankName(input) + assert.Nil(err) + assert.Equal(expected, actual) + } + +} + +func getCustomSubletFixture() map[string]string { + return map[string]string{ + "KSCB0006001": "Tumkur District Central Bank", + "WBSC0KPCB01": "Kolkata Police Co-operative Bank", + "YESB0ADB002": "Amravati District Central Co-operative Bank", + } +} + +func getSubletFixture() map[string]string { + return map[string]string{ + "SKUX": "IBKL0116SBK", + "SPTX": "IBKL0116SSB", + "VCOX": "IBKL0116VMC", + "AURX": "IBKL01192AC", + "NMCX": "IBKL0123NMC", + "MSSX": "IBKL01241MB", + "TNCX": "IBKL01248NC", + "URDX": "IBKL01263UC", + } +} + +func TestValidateBankCode(t *testing.T) { + type args struct { + bankCodeInput string + } + tests := []struct { + name string + args args + want bool + }{ + { + "success", + args{"ABCX"}, + true, + }, + { + "failure", + args{"Aaaa"}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ValidateBankCode(tt.args.bankCodeInput); got != tt.want { + t.Errorf("ValidateBankCode() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestValidate(t *testing.T) { + var result interface{} + err := LoadFile("validator_asserts.json", &result, "../../tests") + if err != nil { + t.Fatalf("error: %+v", err) + } + type args struct { + code string + } + tests := []struct { + name string + args args + want bool + }{} + for key, value := range result.(map[string]interface{}) { + for inp_key, expected_val := range value.(map[string]interface{}) { + tests = append(tests, struct { + name string + args args + want bool + }{key + ":" + inp_key, args{inp_key}, expected_val.(bool)}) + } + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Validate(tt.args.code); got != tt.want { + t.Errorf("Validate() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/src/go/mocks/client.go b/src/go/mocks/client.go new file mode 100644 index 00000000..60a989de --- /dev/null +++ b/src/go/mocks/client.go @@ -0,0 +1,16 @@ +package mocks + +import "net/http" + +// Client is the mock client +type Client struct { + GetFunc func(url string) (resp *http.Response, err error) +} + +var ( + GetFuncVar func(url string) (resp *http.Response, err error) +) + +func (m *Client) Get(url string) (resp *http.Response, err error) { + return GetFuncVar(url) +}