diff --git a/dlidparser/dlidlicense.go b/dlidparser/dlidlicense.go index 9887031..c7bc3d4 100644 --- a/dlidparser/dlidlicense.go +++ b/dlidparser/dlidlicense.go @@ -1,197 +1,47 @@ package dlidparser import ( + "encoding/json" "time" ) +//DriverSex - states record a sex type DriverSex int +//Possibilities for different sex const ( DriverSexNone DriverSex = iota DriverSexMale DriverSexFemale ) +//DLIDLicense holds extracted driver's license info type DLIDLicense struct { - firstName string - middleNames []string - lastName string - nameSuffix string - street string - city string - state string - country string - postal string - sex DriverSex - socialSecurityNumber string - dateOfBirth time.Time - issuerId string - issuerName string - expiryDate time.Time - issueDate time.Time - vehicleClass string - restrictionCodes string - endorsementCodes string - customerId string - documentDiscriminator string -} - -func (d *DLIDLicense) SetFirstName(s string) { - d.firstName = s -} - -func (d *DLIDLicense) FirstName() string { - return d.firstName -} - -func (d *DLIDLicense) SetMiddleNames(s []string) { - d.middleNames = s -} - -func (d *DLIDLicense) MiddleNames() []string { - return d.middleNames -} - -func (d *DLIDLicense) SetLastName(s string) { - d.lastName = s -} - -func (d *DLIDLicense) LastName() string { - return d.lastName -} - -func (d *DLIDLicense) SetNameSuffix(s string) { - d.nameSuffix = s -} - -func (d *DLIDLicense) NameSuffix() string { - return d.nameSuffix -} - -func (d *DLIDLicense) SetStreet(s string) { - d.street = s -} - -func (d *DLIDLicense) Street() string { - return d.street -} - -func (d *DLIDLicense) SetCity(s string) { - d.city = s -} - -func (d *DLIDLicense) City() string { - return d.city -} - -func (d *DLIDLicense) SetState(s string) { - d.state = s -} - -func (d *DLIDLicense) State() string { - return d.state -} - -func (d *DLIDLicense) SetCountry(s string) { - d.country = s -} - -func (d *DLIDLicense) Country() string { - return d.country -} - -func (d *DLIDLicense) SetPostal(s string) { - d.postal = s -} - -func (d *DLIDLicense) Postal() string { - return d.postal -} - -func (d *DLIDLicense) SetSex(s DriverSex) { - d.sex = s -} - -func (d *DLIDLicense) Sex() DriverSex { - return d.sex -} - -func (d *DLIDLicense) SetSocialSecurityNumber(s string) { - d.socialSecurityNumber = s -} - -func (d *DLIDLicense) SocialSecurityNumber() string { - return d.socialSecurityNumber -} - -func (d *DLIDLicense) SetDateOfBirth(t time.Time) { - d.dateOfBirth = t -} - -func (d *DLIDLicense) DateOfBirth() time.Time { - return d.dateOfBirth -} - -func (d *DLIDLicense) IssuerId() string { - return d.issuerId -} - -func (d *DLIDLicense) SetIssuerId(s string) { - d.issuerId = s -} - -func (d *DLIDLicense) IssuerName() string { - return d.issuerName -} - -func (d *DLIDLicense) SetIssuerName(s string) { - d.issuerName = s -} - -func (d *DLIDLicense) VehicleClass() string { - return d.vehicleClass -} - -func (d *DLIDLicense) SetVehicleClass(s string) { - d.vehicleClass = s -} - -func (d *DLIDLicense) RestrictionCodes() string { - return d.restrictionCodes -} - -func (d *DLIDLicense) SetRestrictionCodes(s string) { - d.restrictionCodes = s -} - -func (d *DLIDLicense) EndorsementCodes() string { - return d.endorsementCodes -} - -func (d *DLIDLicense) SetEndorsementCodes(s string) { - d.endorsementCodes = s -} - -func (d *DLIDLicense) CustomerId() string { - return d.customerId -} - -func (d *DLIDLicense) SetCustomerId(s string) { - d.customerId = s -} - -func (d *DLIDLicense) SetExpiryDate(t time.Time) { - d.expiryDate = t -} - -func (d *DLIDLicense) ExpiryDate() time.Time { - return d.expiryDate -} - -func (d *DLIDLicense) SetIssueDate(t time.Time) { - d.issueDate = t -} - -func (d *DLIDLicense) IssueDate() time.Time { - return d.issueDate + FirstName string `json:"first_name,omitempty"` + MiddleNames []string `json:"middle_names,omitempty"` + LastName string `json:"last_name,omitempty"` + NameSuffix string `json:"name_suffix,omitempty"` + Street string `json:"street,omitempty"` + City string `json:"city,omitempty"` + State string `json:"state,omitempty"` + Country string `json:"country,omitempty"` + Postal string `json:"postal,omitempty"` + Sex DriverSex `json:"sex,omitempty"` + SocialSecurityNumber string `json:"social_security_number,omitempty"` + DateOfBirth time.Time `json:"date_of_birth,omitempty"` + IssuerID string `json:"issuer_id,omitempty"` + IssuerName string `json:"issuer_name,omitempty"` + ExpiryDate time.Time `json:"expiry_date,omitempty"` + IssueDate time.Time `json:"issue_date,omitempty"` + VehicleClass string `json:"vehicle_class,omitempty"` + RestrictionCodes string `json:"restriction_codes,omitempty"` + EndorsementCodes string `json:"endorsement_codes,omitempty"` + CustomerID string `json:"customer_id,omitempty"` + DocumentDiscriminator string `json:"document_discriminator,omitempty"` + AAMVAVersion int `json:"aamva_version,omitempty"` +} + +func (d DLIDLicense) String() string { + b, _ := json.Marshal(d) + return string(b) } diff --git a/dlidparser/dlidparser_test.go b/dlidparser/dlidparser_test.go index db2f836..0ec8ffe 100644 --- a/dlidparser/dlidparser_test.go +++ b/dlidparser/dlidparser_test.go @@ -31,15 +31,15 @@ func TestV1IllinoisParser(t *testing.T) { t.Error("V1 Illinois parser failed") } - if s.FirstName() != "SAMPLE" { + if s.FirstName != "SAMPLE" { t.Error("V1 Illinois parser extracted wrong first name") } - if s.MiddleNames()[0] != "CARD" { + if s.MiddleNames[0] != "CARD" { t.Error("V1 Illinois parser extracted wrong middle name") } - if s.LastName() != "CDL" { + if s.LastName != "CDL" { t.Error("V1 Illinois parser extracted wrong last name") } } @@ -51,15 +51,15 @@ func TestV1ConnecticutParser(t *testing.T) { t.Error("V1 Connecticut parser failed") } - if s.FirstName() != "JOHN" { + if s.FirstName != "JOHN" { t.Error("V1 Connecticut parser extracted wrong first name") } - if s.MiddleNames()[0] != "Q" { + if s.MiddleNames[0] != "Q" { t.Error("V1 Connecticut parser extracted wrong middle name") } - if s.LastName() != "PUBLIC" { + if s.LastName != "PUBLIC" { t.Error("V1 Connecticut parser extracted wrong last name") } } @@ -71,15 +71,15 @@ func TestV1MassachusettsParser(t *testing.T) { t.Error("V1 Massachusetts parser failed") } - if s.FirstName() != "JOHN" { + if s.FirstName != "JOHN" { t.Error("V1 Massachusetts parser extracted wrong first name") } - if s.MiddleNames()[0] != "Q" { + if s.MiddleNames[0] != "Q" { t.Error("V1 Massachusetts parser extracted wrong middle name") } - if s.LastName() != "PUBLIC" { + if s.LastName != "PUBLIC" { t.Error("V1 Massachusetts parser extracted wrong last name") } } @@ -91,15 +91,15 @@ func TestV1PennsylvaniaParser(t *testing.T) { t.Error("V1 Pennsylvania parser failed") } - if s.FirstName() != "JOHN" { + if s.FirstName != "JOHN" { t.Error("V1 Pennsylvania parser extracted wrong first name") } - if s.MiddleNames()[0] != "Q" { + if s.MiddleNames[0] != "Q" { t.Error("V1 Pennsylvania parser extracted wrong middle name") } - if s.LastName() != "PUBLIC" { + if s.LastName != "PUBLIC" { t.Error("V1 Pennsylvania parser extracted wrong last name") } } @@ -111,15 +111,15 @@ func TestV1ColoradoParser(t *testing.T) { t.Error("V1 Colorado parser failed") } - if s.FirstName() != "JOHN" { + if s.FirstName != "JOHN" { t.Error("V1 Colorado parser extracted wrong first name") } - if s.MiddleNames()[0] != "Q" { + if s.MiddleNames[0] != "Q" { t.Error("V1 Colorado parser extracted wrong middle name") } - if s.LastName() != "PUBLIC" { + if s.LastName != "PUBLIC" { t.Error("V1 Colorado parser extracted wrong last name") } } @@ -131,95 +131,95 @@ func TestV1Parser(t *testing.T) { t.Error("V1 parser failed") } - if s.IssuerName() != "Virginia" { + if s.IssuerName != "Virginia" { t.Error("V1 parser extracted wrong issuer") } - if s.FirstName() != "JOHN" { + if s.FirstName != "JOHN" { t.Error("V1 parser extracted wrong first name") } - if s.MiddleNames()[0] != "Q" { + if s.MiddleNames[0] != "Q" { t.Error("V1 parser extracted wrong middle name") } - if s.LastName() != "PUBLIC" { + if s.LastName != "PUBLIC" { t.Error("V1 parser extracted wrong last name") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V1 parser got wrong country") } - if s.Street() != "123 MAIN STREET" { + if s.Street != "123 MAIN STREET" { t.Error("V1 parser got wrong street") } - if s.City() != "ANYTOWN" { + if s.City != "ANYTOWN" { t.Error("V1 parser got wrong city") } - if s.State() != "VA" { + if s.State != "VA" { t.Error("V1 parser got wrong state") } - if s.Postal() != "123459999" { + if s.Postal != "123459999" { t.Error("V1 parser got wrong postal code") } - if s.DateOfBirth().Day() != 23 { + if s.DateOfBirth.Day() != 23 { t.Error("V1 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 11 { + if s.DateOfBirth.Month() != 11 { t.Error("V1 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1976 { + if s.DateOfBirth.Year() != 1976 { t.Error("V1 parser got wrong date of birth year") } - if s.CustomerId() != "0123456789ABC" { + if s.CustomerID != "0123456789ABC" { t.Error("V1 parser got wrong customer id") } - if s.EndorsementCodes() != "" { + if s.EndorsementCodes != "" { t.Error("V1 parser got wrong endorsement codes") } - if s.VehicleClass() != "DM" { + if s.VehicleClass != "DM" { t.Error("V1 parser got wrong vehicle class") } - if s.RestrictionCodes() != "" { + if s.RestrictionCodes != "" { t.Error("V1 parser got wrong restriction codes") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V1 parser got wrong sex") } - if s.ExpiryDate().Day() != 1 { + if s.ExpiryDate.Day() != 1 { t.Error("V1 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V1 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2001 { + if s.ExpiryDate.Year() != 2001 { t.Error("V1 parser got wrong expiry year") } - if s.IssueDate().Day() != 1 { + if s.IssueDate.Day() != 1 { t.Error("V1 parser got wrong issue day") } - if s.IssueDate().Month() != 12 { + if s.IssueDate.Month() != 12 { t.Error("V1 parser got wrong issue month") } - if s.IssueDate().Year() != 1996 { + if s.IssueDate.Year() != 1996 { t.Error("V1 parser got wrong issue year") } } @@ -235,99 +235,99 @@ func TestV3Parser(t *testing.T) { t.Error("V3 parser failed") } - if s.IssuerName() != "Texas" { + if s.IssuerName != "Texas" { t.Error("V3 parser extracted wrong issuer") } - if s.FirstName() != "JAMES" { + if s.FirstName != "JAMES" { t.Error("V3 parser extracted wrong first name") } - if len(s.MiddleNames()) != 2 { + if len(s.MiddleNames) != 2 { t.Error("V3 parser failed to extract middle names") } - if s.MiddleNames()[0] != "ROBERT" || s.MiddleNames()[1] != "R" { + if s.MiddleNames[0] != "ROBERT" || s.MiddleNames[1] != "R" { t.Error("V3 parser extracted wrong middle names") } - if s.LastName() != "JONES" { + if s.LastName != "JONES" { t.Error("V3 parser extracted wrong last name") } - if s.DateOfBirth().Day() != 11 { + if s.DateOfBirth.Day() != 11 { t.Error("V3 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 10 { + if s.DateOfBirth.Month() != 10 { t.Error("V3 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1978 { + if s.DateOfBirth.Year() != 1978 { t.Error("V3 parser got wrong date of birth year") } - if s.CustomerId() != "22334455" { + if s.CustomerID != "22334455" { t.Error("V3 parser got wrong customer id") } - if s.EndorsementCodes() != "P" { + if s.EndorsementCodes != "P" { t.Error("V3 parser got wrong endorsement codes") } - if s.VehicleClass() != "B" { + if s.VehicleClass != "B" { t.Error("V3 parser got wrong vehicle class") } - if s.RestrictionCodes() != "LP" { + if s.RestrictionCodes != "LP" { t.Error("V3 parser got wrong restriction codes") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V3 parser got wrong country") } - if s.Street() != "123 SOME STREET" { + if s.Street != "123 SOME STREET" { t.Error("V3 parser got wrong street") } - if s.City() != "CITY 12" { + if s.City != "CITY 12" { t.Error("V3 parser got wrong city") } - if s.State() != "TX" { + if s.State != "TX" { t.Error("V3 parser got wrong state") } - if s.Postal() != "90210" { + if s.Postal != "90210" { t.Error("V3 parser got wrong postal code") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V3 parser got wrong sex") } - if s.ExpiryDate().Day() != 5 { + if s.ExpiryDate.Day() != 5 { t.Error("V4 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 4 { + if s.ExpiryDate.Month() != 4 { t.Error("V3 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2018 { + if s.ExpiryDate.Year() != 2018 { t.Error("V3 parser got wrong expiry year") } - if s.IssueDate().Day() != 8 { + if s.IssueDate.Day() != 8 { t.Error("V3 parser got wrong issue day") } - if s.IssueDate().Month() != 7 { + if s.IssueDate.Month() != 7 { t.Error("V3 parser got wrong issue month") } - if s.IssueDate().Year() != 2012 { + if s.IssueDate.Year() != 2012 { t.Error("V3 parser got wrong issue year") } } @@ -339,103 +339,103 @@ func TestV4Parser(t *testing.T) { t.Error("V4 parser failed") } - if s.IssuerName() != "Virginia" { + if s.IssuerName != "Virginia" { t.Error("V4 parser extracted wrong issuer") } - if s.FirstName() != "MICHAEL" { + if s.FirstName != "MICHAEL" { t.Error("V4 parser extracted wrong first name") } - if len(s.MiddleNames()) != 2 { + if len(s.MiddleNames) != 2 { t.Error("V4 parser failed to extract middle names") } - if s.MiddleNames()[0] != "JOHN" || s.MiddleNames()[1] != "BOB" { + if s.MiddleNames[0] != "JOHN" || s.MiddleNames[1] != "BOB" { t.Error("V4 parser extracted wrong middle names") } - if s.LastName() != "SAMPLE" { + if s.LastName != "SAMPLE" { t.Error("V4 parser extracted wrong last name") } - if s.NameSuffix() != "JR" { + if s.NameSuffix != "JR" { t.Error("V4 parser extracted wrong name suffix") } - if s.DateOfBirth().Day() != 7 { + if s.DateOfBirth.Day() != 7 { t.Error("V4 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 6 { + if s.DateOfBirth.Month() != 6 { t.Error("V4 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1986 { + if s.DateOfBirth.Year() != 1986 { t.Error("V4 parser got wrong date of birth year") } - if s.CustomerId() != "T64235789" { + if s.CustomerID != "T64235789" { t.Error("V4 parser got wrong customer id") } - if s.EndorsementCodes() != "PH" { + if s.EndorsementCodes != "PH" { t.Error("V4 parser got wrong endorsement codes") } - if s.VehicleClass() != "D" { + if s.VehicleClass != "D" { t.Error("V4 parser got wrong vehicle class") } - if s.RestrictionCodes() != "K" { + if s.RestrictionCodes != "K" { t.Error("V4 parser got wrong restriction codes") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V4 parser got wrong country") } - if s.Street() != "2300 WEST BROAD STREET" { + if s.Street != "2300 WEST BROAD STREET" { t.Error("V4 parser got wrong street") } - if s.City() != "RICHMOND" { + if s.City != "RICHMOND" { t.Error("V4 parser got wrong city") } - if s.State() != "VA" { + if s.State != "VA" { t.Error("V4 parser got wrong state") } - if s.Postal() != "23269" { + if s.Postal != "23269" { t.Error("V4 parser got wrong postal code") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V4 parser got wrong sex") } - if s.ExpiryDate().Day() != 10 { + if s.ExpiryDate.Day() != 10 { t.Error("V4 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V4 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2012 { + if s.ExpiryDate.Year() != 2012 { t.Error("V4 parser got wrong expiry year") } - if s.IssueDate().Day() != 6 { + if s.IssueDate.Day() != 6 { t.Error("V4 parser got wrong issue day") } - if s.IssueDate().Month() != 6 { + if s.IssueDate.Month() != 6 { t.Error("V4 parser got wrong issue month") } - if s.IssueDate().Year() != 2008 { + if s.IssueDate.Year() != 2008 { t.Error("V4 parser got wrong issue year") } } @@ -447,103 +447,103 @@ func TestV5Parser(t *testing.T) { t.Error("V5 parser failed") } - if s.IssuerName() != "Virginia" { + if s.IssuerName != "Virginia" { t.Error("V5 parser extracted wrong issuer") } - if s.FirstName() != "MICHAEL" { + if s.FirstName != "MICHAEL" { t.Error("V5 parser extracted wrong first name") } - if len(s.MiddleNames()) != 2 { + if len(s.MiddleNames) != 2 { t.Error("V5 parser failed to extract middle names") } - if s.MiddleNames()[0] != "JOHN" || s.MiddleNames()[1] != "BOB" { + if s.MiddleNames[0] != "JOHN" || s.MiddleNames[1] != "BOB" { t.Error("V5 parser extracted wrong middle names") } - if s.LastName() != "SAMPLE" { + if s.LastName != "SAMPLE" { t.Error("V5 parser extracted wrong last name") } - if s.NameSuffix() != "JR" { + if s.NameSuffix != "JR" { t.Error("V4 parser extracted wrong name suffix") } - if s.DateOfBirth().Day() != 7 { + if s.DateOfBirth.Day() != 7 { t.Error("V5 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 6 { + if s.DateOfBirth.Month() != 6 { t.Error("V5 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1986 { + if s.DateOfBirth.Year() != 1986 { t.Error("V5 parser got wrong date of birth year") } - if s.CustomerId() != "T64235789" { + if s.CustomerID != "T64235789" { t.Error("V5 parser got wrong customer id") } - if s.EndorsementCodes() != "PH" { + if s.EndorsementCodes != "PH" { t.Error("V5 parser got wrong endorsement codes") } - if s.VehicleClass() != "D" { + if s.VehicleClass != "D" { t.Error("V5 parser got wrong vehicle class") } - if s.RestrictionCodes() != "K" { + if s.RestrictionCodes != "K" { t.Error("V5 parser got wrong restriction codes") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V5 parser got wrong country") } - if s.Street() != "2300 WEST BROAD STREET" { + if s.Street != "2300 WEST BROAD STREET" { t.Error("V5 parser got wrong street") } - if s.City() != "RICHMOND" { + if s.City != "RICHMOND" { t.Error("V5 parser got wrong city") } - if s.State() != "VA" { + if s.State != "VA" { t.Error("V5 parser got wrong state") } - if s.Postal() != "23269" { + if s.Postal != "23269" { t.Error("V5 parser got wrong postal code") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V5 parser got wrong sex") } - if s.ExpiryDate().Day() != 10 { + if s.ExpiryDate.Day() != 10 { t.Error("5V5 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V5 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2012 { + if s.ExpiryDate.Year() != 2012 { t.Error("V5 parser got wrong expiry year") } - if s.IssueDate().Day() != 6 { + if s.IssueDate.Day() != 6 { t.Error("V5 parser got wrong issue day") } - if s.IssueDate().Month() != 6 { + if s.IssueDate.Month() != 6 { t.Error("V5 parser got wrong issue month") } - if s.IssueDate().Year() != 2008 { + if s.IssueDate.Year() != 2008 { t.Error("V5 parser got wrong issue year") } } @@ -555,103 +555,103 @@ func TestV6Parser(t *testing.T) { t.Error("V6 parser failed") } - if s.IssuerName() != "Virginia" { + if s.IssuerName != "Virginia" { t.Error("V6 parser extracted wrong issuer") } - if s.FirstName() != "MICHAEL" { + if s.FirstName != "MICHAEL" { t.Error("V6 parser extracted wrong first name") } - if len(s.MiddleNames()) != 2 { + if len(s.MiddleNames) != 2 { t.Error("V6 parser failed to extract middle names") } - if s.MiddleNames()[0] != "JOHN" || s.MiddleNames()[1] != "BOB" { + if s.MiddleNames[0] != "JOHN" || s.MiddleNames[1] != "BOB" { t.Error("V6 parser extracted wrong middle names") } - if s.LastName() != "SAMPLE" { + if s.LastName != "SAMPLE" { t.Error("V6 parser extracted wrong last name") } - if s.NameSuffix() != "JR" { + if s.NameSuffix != "JR" { t.Error("V4 parser extracted wrong name suffix") } - if s.DateOfBirth().Day() != 7 { + if s.DateOfBirth.Day() != 7 { t.Error("V6 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 6 { + if s.DateOfBirth.Month() != 6 { t.Error("V6 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1986 { + if s.DateOfBirth.Year() != 1986 { t.Error("V6 parser got wrong date of birth year") } - if s.CustomerId() != "T64235789" { + if s.CustomerID != "T64235789" { t.Error("V6 parser got wrong customer id") } - if s.EndorsementCodes() != "PH" { + if s.EndorsementCodes != "PH" { t.Error("V6 parser got wrong endorsement codes") } - if s.VehicleClass() != "D" { + if s.VehicleClass != "D" { t.Error("V6 parser got wrong vehicle class") } - if s.RestrictionCodes() != "K" { + if s.RestrictionCodes != "K" { t.Error("V6 parser got wrong restriction codes") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V6 parser got wrong country") } - if s.Street() != "2300 WEST BROAD STREET" { + if s.Street != "2300 WEST BROAD STREET" { t.Error("V6 parser got wrong street") } - if s.City() != "RICHMOND" { + if s.City != "RICHMOND" { t.Error("V6 parser got wrong city") } - if s.State() != "VA" { + if s.State != "VA" { t.Error("V6 parser got wrong state") } - if s.Postal() != "23269" { + if s.Postal != "23269" { t.Error("V6 parser got wrong postal code") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V6 parser got wrong sex") } - if s.ExpiryDate().Day() != 10 { + if s.ExpiryDate.Day() != 10 { t.Error("V6 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V6 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2012 { + if s.ExpiryDate.Year() != 2012 { t.Error("V6 parser got wrong expiry year") } - if s.IssueDate().Day() != 6 { + if s.IssueDate.Day() != 6 { t.Error("V6 parser got wrong issue day") } - if s.IssueDate().Month() != 6 { + if s.IssueDate.Month() != 6 { t.Error("V6 parser got wrong issue month") } - if s.IssueDate().Year() != 2008 { + if s.IssueDate.Year() != 2008 { t.Error("V6 parser got wrong issue year") } } @@ -663,103 +663,103 @@ func TestV7Parser(t *testing.T) { t.Error("V7 parser failed") } - if s.IssuerName() != "Virginia" { + if s.IssuerName != "Virginia" { t.Error("V7 parser extracted wrong issuer") } - if s.FirstName() != "MICHAEL" { + if s.FirstName != "MICHAEL" { t.Error("V7 parser extracted wrong first name") } - if len(s.MiddleNames()) != 2 { + if len(s.MiddleNames) != 2 { t.Error("V7 parser failed to extract middle names") } - if s.MiddleNames()[0] != "JOHN" || s.MiddleNames()[1] != "BOB" { + if s.MiddleNames[0] != "JOHN" || s.MiddleNames[1] != "BOB" { t.Error("V7 parser extracted wrong middle names") } - if s.LastName() != "SAMPLE" { + if s.LastName != "SAMPLE" { t.Error("V7 parser extracted wrong last name") } - if s.NameSuffix() != "JR" { + if s.NameSuffix != "JR" { t.Error("V4 parser extracted wrong name suffix") } - if s.DateOfBirth().Day() != 7 { + if s.DateOfBirth.Day() != 7 { t.Error("V7 parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 6 { + if s.DateOfBirth.Month() != 6 { t.Error("V7 parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1986 { + if s.DateOfBirth.Year() != 1986 { t.Error("V7 parser got wrong date of birth year") } - if s.CustomerId() != "T64235789" { + if s.CustomerID != "T64235789" { t.Error("V7 parser got wrong customer id") } - if s.EndorsementCodes() != "PH" { + if s.EndorsementCodes != "PH" { t.Error("V7 parser got wrong endorsement codes") } - if s.VehicleClass() != "D" { + if s.VehicleClass != "D" { t.Error("V7 parser got wrong vehicle class") } - if s.RestrictionCodes() != "K" { + if s.RestrictionCodes != "K" { t.Error("V7 parser got wrong restriction codes") } - if s.Country() != "USA" { + if s.Country != "USA" { t.Error("V7 parser got wrong country") } - if s.Street() != "2300 WEST BROAD STREET" { + if s.Street != "2300 WEST BROAD STREET" { t.Error("V7 parser got wrong street") } - if s.City() != "RICHMOND" { + if s.City != "RICHMOND" { t.Error("V7 parser got wrong city") } - if s.State() != "VA" { + if s.State != "VA" { t.Error("V7 parser got wrong state") } - if s.Postal() != "23269" { + if s.Postal != "23269" { t.Error("V7 parser got wrong postal code") } - if s.Sex() != DriverSexMale { + if s.Sex != DriverSexMale { t.Error("V7 parser got wrong sex") } - if s.ExpiryDate().Day() != 10 { + if s.ExpiryDate.Day() != 10 { t.Error("V7 parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V7 parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2012 { + if s.ExpiryDate.Year() != 2012 { t.Error("V7 parser got wrong expiry year") } - if s.IssueDate().Day() != 6 { + if s.IssueDate.Day() != 6 { t.Error("V7 parser got wrong issue day") } - if s.IssueDate().Month() != 6 { + if s.IssueDate.Month() != 6 { t.Error("V7 parser got wrong issue month") } - if s.IssueDate().Year() != 2008 { + if s.IssueDate.Year() != 2008 { t.Error("V7 parser got wrong issue year") } } @@ -771,39 +771,39 @@ func TestV7CanadaParser(t *testing.T) { t.Error("V7 Canada parser failed") } - if s.DateOfBirth().Day() != 7 { + if s.DateOfBirth.Day() != 7 { t.Error("V7 Canada parser got wrong date of birth day") } - if s.DateOfBirth().Month() != 6 { + if s.DateOfBirth.Month() != 6 { t.Error("V7 Canada parser got wrong date of birth month") } - if s.DateOfBirth().Year() != 1986 { + if s.DateOfBirth.Year() != 1986 { t.Error("V7 Canada parser got wrong date of birth year") } - if s.ExpiryDate().Day() != 10 { + if s.ExpiryDate.Day() != 10 { t.Error("V7 Canada parser got wrong expiry day") } - if s.ExpiryDate().Month() != 12 { + if s.ExpiryDate.Month() != 12 { t.Error("V7 Canada parser got wrong expiry month") } - if s.ExpiryDate().Year() != 2012 { + if s.ExpiryDate.Year() != 2012 { t.Error("V7 Canada parser got wrong expiry year") } - if s.IssueDate().Day() != 6 { + if s.IssueDate.Day() != 6 { t.Error("V7 Canada parser got wrong issue day") } - if s.IssueDate().Month() != 6 { + if s.IssueDate.Month() != 6 { t.Error("V7 Canada parser got wrong issue month") } - if s.IssueDate().Year() != 2008 { + if s.IssueDate.Year() != 2008 { t.Error("V7 Canada parser got wrong issue year") } } diff --git a/dlidparser/parseV1.go b/dlidparser/parseV1.go index 697521a..b0887b6 100644 --- a/dlidparser/parseV1.go +++ b/dlidparser/parseV1.go @@ -2,73 +2,57 @@ package dlidparser import ( "errors" + "fmt" "strconv" "strings" "time" ) -const ColoradoIssuerId string = "636020" -const ConnecticutIssuerId string = "636006" -const IllinoisIssuerId string = "636035" -const MassachusettsIssuerId string = "636002" -const SouthCarolinaIssuerId string = "636005" -const TennesseeIssuerId string = "636053" - -func parseV1(data string, issuer string) (license *DLIDLicense, err error) { - - start, end, err := dataRangeV1(data) - - if issuer == IllinoisIssuerId { - - // Illinois are the worst offenders so far in terms of mangling the DLID - // spec. They store name, licence number, expiry date and date of birth - // as expected, but then go all-out crazy and encrypt everything else. - // This means that the data range exceeds the size of the licence data - // string. We have to treat Illinois as a special case. - end = len(data) - 1 +const coloradoIssuerID string = "636020" +const connecticutIssuerID string = "636006" +const illinoisIssuerID string = "636035" +const massachusettsIssuerID string = "636002" +const southCarolinaIssuerID string = "636005" +const tennesseeIssuerID string = "636053" +const minnIssuerID string = "636038" + +func parseV1(data string, issuer string) (*DLIDLicense, error) { + start, _, _ := dataRangeV1(data) + if start == -1 { + start, _, _ = dataRangeV1(data) } - - if end >= len(data) { - err = errors.New("Payload location does not exist in data") + //yeah this is a hack + if start > len(data) && strings.Index(data, "DAB") != -1 { + start = strings.Index(data, "DAB") } - - payload := data[start:end] - - if err != nil { - return + if start > len(data) { + return nil, fmt.Errorf("couldn't find start of payload") } + end := len(data) - 1 - license, err = parseDataV1(payload, issuer) - - if err != nil { - return - } - - return + payload := data[start:end] + return parseDataV1(payload, issuer) } -func dataRangeV1(data string) (start int, end int, err error) { +func dataRangeV1(data string) (int, int, error) { - start, err = strconv.Atoi(data[21:25]) + start, err := strconv.Atoi(data[21:25]) if err != nil { - err = errors.New("Data contains malformed payload location") - return + return 0, 0, errors.New("Data contains malformed payload location") } - end, err = strconv.Atoi(data[25:29]) + end, err := strconv.Atoi(data[25:29]) if err != nil { - err = errors.New("Data contains malformed payload length") - return + end = len(data) - 1 + } else { + end += start } - - end += start - - return + return start, end, nil } -func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err error) { +func parseDataV1(licenceData string, issuer string) (*DLIDLicense, error) { // Version 1 of the DLID card spec was published in 2000. As of 2012, it is // the version used in Colorado. @@ -101,13 +85,16 @@ func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err e components := strings.Split(licenceData, "\n") - license = new(DLIDLicense) + license := &DLIDLicense{} - license.SetIssuerId(issuer) - license.SetIssuerName(issuers[issuer]) + license.IssuerID = issuer + license.IssuerName = issuers[issuer] // Country is always USA for V1 licenses - license.SetCountry("USA") + license.Country = "USA" + expire := "" + issue := "" + dob := "" for component := range components { @@ -122,27 +109,19 @@ func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err e switch identifier { case "DAR": - license.SetVehicleClass(data) - + license.VehicleClass = data case "DAS": - license.SetRestrictionCodes(data) - + license.RestrictionCodes = data case "DAT": - license.SetEndorsementCodes(data) - + license.EndorsementCodes = data case "DAA": - // Early versions of the Colorado implementation screwed up the // delimiter - they use a space instead of the specified comma. - separator := " " - if strings.Index(data, separator) == -1 { separator = "," } - names := strings.Split(data, separator) - // According to the spec, names are ordered LAST,FIRST,MIDDLE. // However, the geniuses in the Colorado and Tennessee DMVs order it // FIRST,MIDDLE,LAST. We'll use the issuer ID number to @@ -151,66 +130,54 @@ func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err e // // http://www.aamva.org/IIN-and-RID/ - if issuer == ColoradoIssuerId || issuer == TennesseeIssuerId { - + if issuer == coloradoIssuerID || issuer == tennesseeIssuerID || issuer == minnIssuerID { // Colorado's backwards formatting style... - license.SetFirstName(names[0]) + license.FirstName = names[0] if len(names) > 2 { - license.SetMiddleNames(names[1 : len(names)-1]) - license.SetLastName(names[len(names)-1]) + license.MiddleNames = names[1 : len(names)-1] + license.LastName = names[len(names)-1] } else if len(names) > 1 { - license.SetLastName(names[1]) + license.LastName = names[1] } } else { // Everyone else, hopefully. - license.SetLastName(names[0]) + license.LastName = names[0] if len(names) > 1 { - license.SetFirstName(names[1]) + license.FirstName = names[1] if len(names) > 2 { - license.SetMiddleNames(names[2:]) + license.MiddleNames = names[2:] } } } case "DAE": - license.SetNameSuffix(data) + license.NameSuffix = data case "DAL": - // Colorado screws up again: they omit the *required* DAG field and // substitute the optional DAL field in older licences. fallthrough - case "DAG": - license.SetStreet(data) - + license.Street = data case "DAN": - // Again, old Colorado licences ignore the spec. fallthrough - case "DAI": - license.SetCity(data) - + license.City = data case "DAO": - // Colorado strikes again. Honestly, what is the point in having a // spec if you don't follow it? fallthrough - case "DAJ": - license.SetState(data) - + license.State = data case "DAP": // More Colorado shenanigans. fallthrough - case "DAK": - // Colorado uses the 5-digit zip code. South Carolina uses the // 5 digit zip code plus the +4 extension all smooshed together // into one long string. Massachusetts uses the 5 digit zip @@ -221,19 +188,14 @@ func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err e // defeat in trying to untangle the incredible mess implemented // in this single field; we'll just show the zip as it is // stored. - license.SetPostal(strings.Trim(data, " ")) - + license.Postal = strings.Trim(data, " ") case "DAQ": - license.SetCustomerId(data) - + license.CustomerID = data case "DBA": - license.SetExpiryDate(parseDateV1(data)) - + expire = data case "DBB": - license.SetDateOfBirth(parseDateV1(data)) - + dob = data case "DBC": - // Sex can be stored as M/F if it uses the DLID code. It could // also be stored as 0/1/2/9 if it uses the ANSI D-20 codes, // available here: @@ -244,49 +206,53 @@ func parseDataV1(licenceData string, issuer string) (license *DLIDLicense, err e case "M": fallthrough case "1": - license.SetSex(DriverSexMale) + license.Sex = DriverSexMale case "F": fallthrough case "2": - license.SetSex(DriverSexFemale) + license.Sex = DriverSexFemale default: - license.SetSex(DriverSexNone) + license.Sex = DriverSexNone } - case "DBD": - license.SetIssueDate(parseDateV1(data)) - + issue = data case "DBK": - // Optional and probably not available - license.SetSocialSecurityNumber(data) + license.SocialSecurityNumber = data + case "DAB": + license.LastName = data + case "DAC": + license.FirstName = data + case "DAD": + license.MiddleNames = []string{data} + /* + default: + fmt.Printf("Unknown: %v : %v\n", identifier, data) + */ } } - return + license.ExpiryDate = parseDateV1(expire) + license.DateOfBirth = parseDateV1(dob) + license.IssueDate = parseDateV1(issue) + + return license, nil } func parseDateV1(data string) time.Time { - - year, err := strconv.Atoi(data[:4]) - - if err != nil { - return time.Unix(0, 0) - } - - month, err := strconv.Atoi(data[4:6]) - - if err != nil { + var format string + switch len(data) { + case 8: + format = "20060102" + case 10: //odd NC vintage 2008 edition licenses + format = "01-02-2006" + default: return time.Unix(0, 0) } - day, err := strconv.Atoi(data[6:8]) - + d, err := time.Parse(format, data) if err != nil { return time.Unix(0, 0) } - - location, err := time.LoadLocation("UTC") - - return time.Date(year, time.Month(month), day, 0, 0, 0, 0, location) + return d } diff --git a/dlidparser/parseV2.go b/dlidparser/parseV2.go index 1bfe23f..5ea3f2d 100644 --- a/dlidparser/parseV2.go +++ b/dlidparser/parseV2.go @@ -2,72 +2,66 @@ package dlidparser import ( "errors" + "fmt" "strconv" "strings" "time" ) -func parseV2(data string, issuer string) (license *DLIDLicense, err error) { +func parseV2(data string, issuer string) (*DLIDLicense, error) { start, end, err := dataRangeV2(data) if end >= len(data) { - err = errors.New("Payload location does not exist in data") + return nil, errors.New("Payload location does not exist in data") } payload := data[start:end] if err != nil { - return + return nil, err } - - license, err = parseDataV2(payload, issuer) - - if err != nil { - return - } - - return + return parseDataV2(payload, issuer) } -func dataRangeV2(data string) (start int, end int, err error) { +func dataRangeV2(data string) (int, int, error) { - start, err = strconv.Atoi(data[23:27]) + if len(data) < 31 { + return 0, 0, errors.New("Data is short") + } + start, err := strconv.Atoi(data[23:27]) if err != nil { - err = errors.New("Data contains malformed payload location") - return + return 0, 0, errors.New("Data contains malformed payload location") } - end, err = strconv.Atoi(data[27:31]) + end, err := strconv.Atoi(data[27:31]) if err != nil { - err = errors.New("Data contains malformed payload length") - return + return 0, 0, errors.New(fmt.Sprintf("Data contains malformed payload length -%v %v", start, data[27:31])) } end += start - return + return start, end, nil } -func parseDataV2(licenceData string, issuer string) (license *DLIDLicense, err error) { +func parseDataV2(licenceData string, issuer string) (*DLIDLicense, error) { // Version 1 of the DLID card spec was published in 2003. if !strings.HasPrefix(licenceData, "DL") { - err = errors.New("Missing header in licence data chunk") - return + return nil, errors.New("Missing header in licence data chunk") } licenceData = licenceData[2:] components := strings.Split(licenceData, "\n") - license = new(DLIDLicense) + license := &DLIDLicense{} - license.SetIssuerId(issuer) - license.SetIssuerName(issuers[issuer]) + license.IssuerID = issuer + license.IssuerName = issuers[issuer] for component := range components { @@ -82,16 +76,16 @@ func parseDataV2(licenceData string, issuer string) (license *DLIDLicense, err e switch identifier { case "DCA": - license.SetVehicleClass(data) + license.VehicleClass = data case "DCB": - license.SetRestrictionCodes(data) + license.RestrictionCodes = data case "DCD": - license.SetEndorsementCodes(data) + license.EndorsementCodes = data case "DCS": - license.SetLastName(data) + license.LastName = data case "DCT": @@ -109,29 +103,29 @@ func parseDataV2(licenceData string, issuer string) (license *DLIDLicense, err e names := strings.Split(data, separator) - license.SetFirstName(names[0]) + license.FirstName = names[0] if len(names) > 1 { - license.SetMiddleNames(names[1:]) + license.MiddleNames = names[1:] } case "DAG": - license.SetStreet(data) + license.Street = data case "DAI": - license.SetCity(data) + license.City = data case "DAJ": - license.SetState(data) + license.State = data case "DAK": - license.SetPostal(data) + license.Postal = data case "DAQ": - license.SetCustomerId(data) + license.CustomerID = data case "DBB": - license.SetDateOfBirth(parseDateV2(data)) + license.DateOfBirth = parseDateV2(data) case "DBC": @@ -141,16 +135,16 @@ func parseDataV2(licenceData string, issuer string) (license *DLIDLicense, err e switch data { case "1": - license.SetSex(DriverSexMale) + license.Sex = DriverSexMale case "2": - license.SetSex(DriverSexFemale) + license.Sex = DriverSexFemale default: - license.SetSex(DriverSexNone) + license.Sex = DriverSexNone } } } - return + return license, nil } func parseDateV2(data string) time.Time { @@ -158,26 +152,12 @@ func parseDateV2(data string) time.Time { // Sooo, let me get this straight. They switched from a reasonably-standard // and universal date format (yyyyMMdd) to the bizarre US lumpy format // (MMddyyyy)? What were they thinking!? - - month, err := strconv.Atoi(data[:2]) - - if err != nil { + if len(data) != 8 { return time.Unix(0, 0) } - - day, err := strconv.Atoi(data[2:4]) - + t, err := time.Parse("01022006", data) if err != nil { return time.Unix(0, 0) } - - year, err := strconv.Atoi(data[4:8]) - - if err != nil { - return time.Unix(0, 0) - } - - location, err := time.LoadLocation("UTC") - - return time.Date(year, time.Month(month), day, 0, 0, 0, 0, location) + return t } diff --git a/dlidparser/parseV3.go b/dlidparser/parseV3.go index 6d055a2..387fc18 100644 --- a/dlidparser/parseV3.go +++ b/dlidparser/parseV3.go @@ -1,53 +1,53 @@ package dlidparser import ( - "errors" - "strconv" "strings" "time" ) -func parseV3(data string, issuer string) (license *DLIDLicense, err error) { +func parseV3(data string, issuer string) (*DLIDLicense, error) { start, end, err := dataRangeV2(data) + if err != nil { + return nil, err + } if end >= len(data) { - err = errors.New("Payload location does not exist in data") + //lots of states don't count correct - VA i'm looking at you + end = len(data) - 1 } payload := data[start:end] - if err != nil { - return - } - - license, err = parseDataV3(payload, issuer) + license, err := parseDataV3(payload, issuer) if err != nil { - return + return nil, err } - return + return license, nil } -func parseDataV3(licenceData string, issuer string) (license *DLIDLicense, err error) { +func parseDataV3(licenceData string, issuer string) (*DLIDLicense, error) { // Version 3 of the DLID card spec was published in 2005. It is currently // (as of 2012) used in Wisconsin. - if !strings.HasPrefix(licenceData, "DL") { - err = errors.New("Missing header in licence data chunk") - return - } + /* + if !strings.HasPrefix(licenceData, "DL") { + err := errors.New("Missing header in licence data chunk") + return nil, err + } + */ licenceData = licenceData[2:] components := strings.Split(licenceData, "\n") - license = new(DLIDLicense) + license := &DLIDLicense{} - license.SetIssuerId(issuer) - license.SetIssuerName(issuers[issuer]) + license.IssuerID = issuer + license.IssuerName = issuers[issuer] var dateOfBirth string var expiryDate string @@ -66,22 +66,16 @@ func parseDataV3(licenceData string, issuer string) (license *DLIDLicense, err e switch identifier { case "DCA": - license.SetVehicleClass(data) - + license.VehicleClass = data case "DCB": - license.SetRestrictionCodes(data) - + license.RestrictionCodes = data case "DCD": - license.SetEndorsementCodes(data) - + license.EndorsementCodes = data case "DCS": - license.SetLastName(data) - + license.LastName = data case "DCG": - license.SetCountry(data) - + license.Country = data case "DCT": - // This field contains all of the licencee's names except last // name. The V3 spec doc doesn't specify how the names are // separated and doesn't provide an example (unlike the 2000 @@ -96,41 +90,38 @@ func parseDataV3(licenceData string, issuer string) (license *DLIDLicense, err e names := strings.Split(data, separator) - license.SetFirstName(names[0]) + license.FirstName = names[0] if len(names) > 1 { - license.SetMiddleNames(names[1:]) + license.MiddleNames = names[1:] } case "DAG": - license.SetStreet(data) + license.Street = data case "DAI": - license.SetCity(data) + license.City = data case "DAJ": - license.SetState(data) + license.State = data case "DAK": - license.SetPostal(data) + license.Postal = data case "DAQ": - license.SetCustomerId(data) - + license.CustomerID = data case "DBA": expiryDate = data - case "DBB": dateOfBirth = data - case "DBC": switch data { case "1": - license.SetSex(DriverSexMale) + license.Sex = DriverSexMale case "2": - license.SetSex(DriverSexFemale) + license.Sex = DriverSexFemale default: - license.SetSex(DriverSexNone) + license.Sex = DriverSexNone } case "DBD": @@ -138,11 +129,17 @@ func parseDataV3(licenceData string, issuer string) (license *DLIDLicense, err e } } + //if empty default to USA - Michigan - doesn't set DCG - based on license issue 1.20.2017 + //without country - doesn't parse dates + if license.Country == "" { + license.Country = "USA" + } + // At this point we should know the country and the postal code (both are // mandatory fields) so we can undo the desperate mess the standards body // made of the postal code field. - if license.Country() == "USA" && len(license.Postal()) > 0 { + if strings.Contains(license.Country, "USA") && len(strings.TrimSpace(license.Postal)) == 9 { // For some reason known only to themselves, the standards guys took // the V1 and 2 postal code standards (code padded to 11 characters with @@ -159,26 +156,23 @@ func parseDataV3(licenceData string, issuer string) (license *DLIDLicense, err e // Naturally, some Texas licences ignore the spec and just use 5 // characters if they don't have a +4 section. - if len(license.Postal()) > 5 { - zip := license.Postal()[:5] - plus4 := license.Postal()[5:9] + zip := license.Postal[:5] + plus4 := license.Postal[5:9] - if plus4 == "0000" { - license.SetPostal(zip) - } else { - license.SetPostal(zip + "+" + plus4) - } + if plus4 == "0000" { + license.Postal = zip + } else { + license.Postal = zip + "+" + plus4 } } // Now we can parse the birth date, too. - if len(license.Country()) > 0 { - license.SetDateOfBirth(parseDateV3(dateOfBirth, license.Country())) - license.SetExpiryDate(parseDateV3(expiryDate, license.Country())) - license.SetIssueDate(parseDateV3(issueDate, license.Country())) + if len(license.Country) > 0 { + license.DateOfBirth = parseDateV3(dateOfBirth, license.Country) + license.ExpiryDate = parseDateV3(expiryDate, license.Country) + license.IssueDate = parseDateV3(issueDate, license.Country) } - - return + return license, nil } func parseDateV3(data string, country string) time.Time { @@ -191,51 +185,18 @@ func parseDateV3(data string, country string) time.Time { // implementations of a standard within a single field in a single version // of the standard. Breathtakingly stupid. - var day int - var month int - var year int - var err error - var location *time.Location - - if country == "USA" { - month, err = strconv.Atoi(data[:2]) - - if err != nil { - return time.Unix(0, 0) - } - - day, err = strconv.Atoi(data[2:4]) - - if err != nil { - return time.Unix(0, 0) - } - - year, err = strconv.Atoi(data[4:8]) - - if err != nil { - return time.Unix(0, 0) - } - } else { - year, err = strconv.Atoi(data[:4]) - - if err != nil { - return time.Unix(0, 0) - } - - month, err = strconv.Atoi(data[4:6]) - - if err != nil { - return time.Unix(0, 0) - } - - day, err = strconv.Atoi(data[6:8]) - - if err != nil { - return time.Unix(0, 0) + if len(data) != 8 { + return time.Unix(0, 0) + } + order := []string{"20060102", "01022006"} + if strings.Contains(country, "USA") { + order = []string{"01022006", "20060102"} + } + for _, format := range order { + t, err := time.Parse(format, data) + if err == nil { + return t } } - - location, err = time.LoadLocation("UTC") - - return time.Date(year, time.Month(month), day, 0, 0, 0, 0, location) + return time.Unix(0, 0) } diff --git a/dlidparser/parseV4.go b/dlidparser/parseV4.go index 7be2710..dbfbd51 100644 --- a/dlidparser/parseV4.go +++ b/dlidparser/parseV4.go @@ -1,50 +1,51 @@ package dlidparser import ( - "errors" "strings" ) -func parseV4(data string, issuer string) (license *DLIDLicense, err error) { +func parseV4(data string, issuer string) (*DLIDLicense, error) { start, end, err := dataRangeV2(data) - if end >= len(data) { - err = errors.New("Payload location does not exist in data") - } - - payload := data[start:end] - - if err != nil { - return - } + /* + if end > len(data) { + //lots of states don't count correct - VA i'm looking at you + end = len(data) - 1 + } + */ - license, err = parseDataV4(payload, issuer) + //license files don't really contain extra data + end = len(data) - 1 if err != nil { - return + return nil, err } - return + payload := data[start:end] + return parseDataV4(payload, issuer) } -func parseDataV4(licenceData string, issuer string) (license *DLIDLicense, err error) { +func parseDataV4(licenceData string, issuer string) (*DLIDLicense, error) { // Version 4 of the DLID card spec was published in 2009. - if !strings.HasPrefix(licenceData, "DL") { - err = errors.New("Missing header in licence data chunk") - return - } + /* + This would be nice but encdoing errors - SC 2014 issued - fail this + + if !strings.HasPrefix(licenceData, "DL") && !strings.HasPrefix(licenceData, "ID") { + return nil, errors.New("Missing header in licence data chunk") + } + */ licenceData = licenceData[2:] components := strings.Split(licenceData, "\n") - license = new(DLIDLicense) + license := &DLIDLicense{} - license.SetIssuerId(issuer) - license.SetIssuerName(issuers[issuer]) + license.IssuerID = issuer + license.IssuerName = issuers[issuer] var dateOfBirth string var expiryDate string @@ -63,44 +64,44 @@ func parseDataV4(licenceData string, issuer string) (license *DLIDLicense, err e switch identifier { case "DCA": - license.SetVehicleClass(data) + license.VehicleClass = data case "DCB": - license.SetRestrictionCodes(data) + license.RestrictionCodes = data case "DCD": - license.SetEndorsementCodes(data) + license.EndorsementCodes = data case "DCS": - license.SetLastName(data) + license.LastName = data case "DCU": - license.SetNameSuffix(data) + license.NameSuffix = data case "DAC": - license.SetFirstName(data) + license.FirstName = data case "DAD": names := strings.Split(data, ",") - license.SetMiddleNames(names) + license.MiddleNames = names case "DCG": - license.SetCountry(data) + license.Country = data case "DAG": - license.SetStreet(data) + license.Street = data case "DAI": - license.SetCity(data) + license.City = data case "DAJ": - license.SetState(data) + license.State = data case "DAK": - license.SetPostal(data) + license.Postal = data case "DAQ": - license.SetCustomerId(data) + license.CustomerID = data case "DBA": expiryDate = data @@ -111,11 +112,11 @@ func parseDataV4(licenceData string, issuer string) (license *DLIDLicense, err e case "DBC": switch data { case "1": - license.SetSex(DriverSexMale) + license.Sex = DriverSexMale case "2": - license.SetSex(DriverSexFemale) + license.Sex = DriverSexFemale default: - license.SetSex(DriverSexNone) + license.Sex = DriverSexNone } case "DBD": @@ -127,7 +128,7 @@ func parseDataV4(licenceData string, issuer string) (license *DLIDLicense, err e // mandatory fields) so we can undo the desperate mess the standards body // made of the postal code field. - if license.Country() == "USA" && len(license.Postal()) > 0 { + if license.Country == "USA" && len(strings.TrimSpace(license.Postal)) == 9 { // Another change to the postal code field! Surprise! This time the // standards guys trimmed the field down to 9 characters, which makes @@ -138,24 +139,26 @@ func parseDataV4(licenceData string, issuer string) (license *DLIDLicense, err e // We will extract the 5-digit zip and the +4 section. If the +4 is all // zeros we can discard it. - if len(license.Postal()) > 5 { - zip := license.Postal()[:5] - plus4 := license.Postal()[5:9] + zip := license.Postal[:5] + plus4 := license.Postal[5:9] - if plus4 == "0000" { - license.SetPostal(zip) - } else { - license.SetPostal(zip + "+" + plus4) - } + if plus4 == "0000" { + license.Postal = zip + } else { + license.Postal = zip + "+" + plus4 } } - // Now we can parse the dates, too. - if len(license.Country()) > 0 { - license.SetDateOfBirth(parseDateV3(dateOfBirth, license.Country())) - license.SetExpiryDate(parseDateV3(expiryDate, license.Country())) - license.SetIssueDate(parseDateV3(issueDate, license.Country())) + if license.IssuerName == "Wyoming" || (license.IssuerName == "West Virginia") { + license.DateOfBirth = parseDateV3(dateOfBirth, "CANADA") + license.ExpiryDate = parseDateV3(expiryDate, "CANADA") + license.IssueDate = parseDateV3(issueDate, "CANADA") + } else // Now we can parse the dates, too. + if len(license.Country) > 0 { + license.DateOfBirth = parseDateV3(dateOfBirth, license.Country) + license.ExpiryDate = parseDateV3(expiryDate, license.Country) + license.IssueDate = parseDateV3(issueDate, license.Country) } - return + return license, nil } diff --git a/dlidparser/parser.go b/dlidparser/parser.go index 11522e9..0357bae 100644 --- a/dlidparser/parser.go +++ b/dlidparser/parser.go @@ -2,10 +2,13 @@ package dlidparser import ( "errors" + "fmt" "strconv" + "strings" ) -func Parse(data string) (license *DLIDLicense, err error) { +//Parse the data string from a pdf417 driver's license barcode +func Parse(data string) (*DLIDLicense, error) { // This parser is based on standards from here: // @@ -21,30 +24,48 @@ func Parse(data string) (license *DLIDLicense, err error) { // PA and CT appear to have used old versions of the spec because they use // "AAMVA" instead of "ANSI " as part of the header. - if len(data) < 15 { - return license, errors.New("Data does not contain expected header") + if len(data) < 17 { + return nil, errors.New("Data does not contain expected header - ") + } + + if strings.HasPrefix(data, "@\n\u001e\rANSI6") { + data = strings.Replace(data, "ANSI6", "ANSI 6", 1) + } + //OREGON / AZ + if data[0:8] == "@\r\nANSI " || data[0:8] == "@\n\rANSI " /* for AZ circa 2009 licenses */ { + data = "@\n\u001e\rANSI " + data[8:] } if data[0:2] != "@\n" || data[3] != '\r' || - (data[4:9] != "ANSI " && data[4:9] != "AAMVA") { - return license, errors.New("Data does not contain expected header") + (data[4:9] != "ANSI " && data[4:9] != "AAMVA") { + return nil, fmt.Errorf("Data does not contain expected header %v", string(data[4:9])) } issuer := data[9:15] version, err := strconv.Atoi(data[15:17]) if err != nil { - return license, errors.New("Data does not contain a version number") + return nil, errors.New("Data does not contain a version number") } + var license *DLIDLicense switch version { case 1: license, err = parseV1(data, issuer) + if license != nil { + license.AAMVAVersion = 1 + } case 2: license, err = parseV2(data, issuer) + if license != nil { + license.AAMVAVersion = 2 + } case 3: license, err = parseV3(data, issuer) + if license != nil { + license.AAMVAVersion = 3 + } case 4: fallthrough case 5: @@ -53,9 +74,22 @@ func Parse(data string) (license *DLIDLicense, err error) { fallthrough case 7: license, err = parseV4(data, issuer) + if license != nil { + license.AAMVAVersion = 7 + } + case 8: + license, err = parseV4(data, issuer) + if license != nil { + license.AAMVAVersion = 8 + } + case 9: + //aamva 09 is 2016 the latest spec - compat w/ the v4 + license, err = parseV4(data, issuer) + if license != nil { + license.AAMVAVersion = 9 + } default: err = errors.New("Unsupported DLID version number") } - - return + return license, err } diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..aa9ff0b --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/derekg/DLID + +go 1.12 diff --git a/main.go b/main.go index b82ed59..cacff6d 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,12 @@ package main import ( - "github.com/ant512/DLID/dlidparser" + "fmt" + "io/ioutil" "log" + "os" + + "github.com/derekg/DLID/dlidparser" ) func main() { @@ -13,5 +17,18 @@ func main() { return } - log.Println(s) + fmt.Println() + fmt.Println(s) + if len(os.Args) > 1 { + data, err := ioutil.ReadFile(os.Args[1]) + if err != nil { + log.Fatal(err) + } + foo, err := dlidparser.Parse(string(data)) + if err != nil { + log.Fatal(err) + } + fmt.Println(foo) + } + }