Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#5 - adds support for properties with multiple values #11

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions card_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,66 @@ var testCardGoogle = Card{
},
}

var testCardGoogleMultiEmail = Card{
"VERSION": []*Field{{Value: "3.0"}},
"N": []*Field{{Value: "Bloggs;Joe;;;"}},
"FN": []*Field{{Value: "Joe Bloggs"}},
"EMAIL": []*Field{{
Value: "[email protected]",
Params: Params{"TYPE": {"INTERNET", "HOME"}},
},{
Value: "[email protected]",
Params: Params{"TYPE": {"INTERNET", "HOME"}},
}},
"TEL": []*Field{{
Value: "+44 20 1234 5678",
Params: Params{"TYPE": {"CELL"}},
}},
"ADR": []*Field{{
Value: ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
Params: Params{"TYPE": {"HOME"}},
}},
"URL": []*Field{
{Value: "http\\://joebloggs.com", Group: "item1"},
{Value: "http\\://twitter.com/test", Group: "item2"},
},
"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
"X-ABLABEL": []*Field{
{Value: "_$!<HomePage>!$_", Group: "item1"},
{Value: "Twitter", Group: "item2"},
},
}

var testCardGoogleMultiEmailComma = Card{
"VERSION": []*Field{{Value: "3.0"}},
"N": []*Field{{Value: "Bloggs;Joe;;;"}},
"FN": []*Field{{Value: "Joe Bloggs"}},
"EMAIL": []*Field{{
Value: "[email protected]",
Params: Params{"TYPE": {"INTERNET", "HOME"}},
},{
Value: `joe@joebloggs,com`,
Params: Params{"TYPE": {"INTERNET", "HOME"}},
}},
"TEL": []*Field{{
Value: "+44 20 1234 5678",
Params: Params{"TYPE": {"CELL"}},
}},
"ADR": []*Field{{
Value: ";;1 Trafalgar Square;London;;WC2N;United Kingdom",
Params: Params{"TYPE": {"HOME"}},
}},
"URL": []*Field{
{Value: "http\\://joebloggs.com", Group: "item1"},
{Value: "http\\://twitter.com/test", Group: "item2"},
},
"X-SKYPE": []*Field{{Value: "joe.bloggs"}},
"X-ABLABEL": []*Field{
{Value: "_$!<HomePage>!$_", Group: "item1"},
{Value: "Twitter", Group: "item2"},
},
}

var testCardApple = Card{
"VERSION": []*Field{{Value: "3.0"}},
"N": []*Field{{Value: "Bloggs;Joe;;;"}},
Expand Down
37 changes: 32 additions & 5 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ func (dec *Decoder) Decode() (Card, error) {
return card, err
}

k, f, err := parseLine(l)
k, fields, err := parseLine(l)
if err != nil {
continue
}

f := fields[0]

if !hasBegin {
if k == "BEGIN" {
if strings.ToUpper(f.Value) != "VCARD" {
Expand All @@ -89,7 +91,7 @@ func (dec *Decoder) Decode() (Card, error) {
break
}

card[k] = append(card[k], f)
card[k] = append(card[k], fields...)
}

if !hasEnd {
Expand All @@ -101,8 +103,13 @@ func (dec *Decoder) Decode() (Card, error) {
return card, nil
}

func parseLine(l string) (key string, field *Field, err error) {
field = new(Field)
const placeholder = "\uFFFD"
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using a placeholder (which could be in the input), can we replace this logic with a proper char-by-char parser?

const escapedComma = `\,`
const comma = `,`

func parseLine(l string) (key string, fields []*Field, err error) {
fields = []*Field{}
field := new(Field)
field.Group, l = parseGroup(l)
key, hasParams, l, err := parseKey(l)
if err != nil {
Expand All @@ -116,7 +123,27 @@ func parseLine(l string) (key string, field *Field, err error) {
}
}

field.Value = parseValue(l)
v := strings.Replace(l, escapedComma, placeholder, -1)

originalValue := parseValue(v)

values := strings.Split(originalValue, ",")

if len(values) > 1 {
for _, value := range values {
f := new(Field)
value = strings.Replace(value, placeholder, comma, -1)
f.Value = strings.TrimSpace(value)
f.Group = field.Group
f.Params = field.Params
fields = append(fields, f)
}
} else {
originalValue = strings.Replace(originalValue, placeholder, comma, -1)
field.Value = originalValue
fields = append(fields, field)
}

return
}

Expand Down
38 changes: 35 additions & 3 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,36 @@ item2.URL:http\://twitter.com/test
item2.X-ABLabel:Twitter
END:VCARD`

// Google Contacts (15 November 2012)
var testCardGoogleMultiValueString = `BEGIN:VCARD
VERSION:3.0
N:Bloggs;Joe;;;
FN:Joe Bloggs
EMAIL;TYPE=INTERNET;TYPE=HOME:[email protected], [email protected]
TEL;TYPE=CELL:+44 20 1234 5678
ADR;TYPE=HOME:;;1 Trafalgar Square;London;;WC2N;United Kingdom
item1.URL:http\://joebloggs.com
item1.X-ABLabel:_$!<HomePage>!$_
X-SKYPE:joe.bloggs
item2.URL:http\://twitter.com/test
item2.X-ABLabel:Twitter
END:VCARD`

var testCardGoogleMultiValueWithCommaString = `BEGIN:VCARD
VERSION:3.0
N:Bloggs;Joe;;;
FN:Joe Bloggs
EMAIL;TYPE=INTERNET;TYPE=HOME:[email protected], joe@joebloggs\,com
TEL;TYPE=CELL:+44 20 1234 5678
ADR;TYPE=HOME:;;1 Trafalgar Square;London;;WC2N;United Kingdom
item1.URL:http\://joebloggs.com
item1.X-ABLabel:_$!<HomePage>!$_
X-SKYPE:joe.bloggs
item2.URL:http\://twitter.com/test
item2.X-ABLabel:Twitter
END:VCARD`


// Apple Contacts (version 7.1)
var testCardAppleString = `BEGIN:VCARD
VERSION:3.0
Expand Down Expand Up @@ -81,6 +111,8 @@ var decoderTests = []struct {
{testCardGoogleString, testCardGoogle},
{testCardAppleString, testCardApple},
{testCardLineFoldingString, testCardLineFolding},
{testCardGoogleMultiValueString, testCardGoogleMultiEmail},
{testCardGoogleMultiValueWithCommaString, testCardGoogleMultiEmailComma},
}

func TestDecoder(t *testing.T) {
Expand Down Expand Up @@ -134,9 +166,9 @@ func TestParseLine_escaped(t *testing.T) {
expectedKey := "NOTE"
expectedValue := "Mythical Manager\nHyjinx Software Division\nBabsCo, Inc.\n"

if key, field, err := parseLine(l); err != nil {
if key, fields, err := parseLine(l); err != nil {
t.Fatal("Expected no error while parsing line, got:", err)
} else if key != expectedKey || field.Value != expectedValue {
t.Errorf("parseLine(%q): expected (%q, %q), got (%q, %q)", l, expectedKey, expectedValue, key, field.Value)
} else if key != expectedKey || fields[0].Value != expectedValue {
t.Errorf("parseLine(%q): expected (%q, %q), got (%q, %q)", l, expectedKey, expectedValue, key, fields[0].Value)
}
}