From a0e42acd5711bb700e5073928e54eab6748c9d48 Mon Sep 17 00:00:00 2001 From: GorthMohogany Date: Mon, 1 Oct 2018 17:06:40 -0400 Subject: [PATCH] #5 - adds support for properties with multiple values --- card_test.go | 60 +++++++++++++++++++++++++++++++++++++++++++++++++ decoder.go | 37 +++++++++++++++++++++++++----- decoder_test.go | 38 ++++++++++++++++++++++++++++--- 3 files changed, 127 insertions(+), 8 deletions(-) diff --git a/card_test.go b/card_test.go index 4d20c67..fccb089 100644 --- a/card_test.go +++ b/card_test.go @@ -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: "me@joebloggs.com", + 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: "_$!!$_", 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: "me@joebloggs.com", + 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: "_$!!$_", Group: "item1"}, + {Value: "Twitter", Group: "item2"}, + }, +} + var testCardApple = Card{ "VERSION": []*Field{{Value: "3.0"}}, "N": []*Field{{Value: "Bloggs;Joe;;;"}}, diff --git a/decoder.go b/decoder.go index 1d2153d..abe75ec 100644 --- a/decoder.go +++ b/decoder.go @@ -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" { @@ -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 { @@ -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 = "💩" +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 { @@ -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 } diff --git a/decoder_test.go b/decoder_test.go index 4af77e1..cdf1ccb 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -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:me@joebloggs.com, 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:_$!!$_ +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:me@joebloggs.com, 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:_$!!$_ +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 @@ -81,6 +111,8 @@ var decoderTests = []struct { {testCardGoogleString, testCardGoogle}, {testCardAppleString, testCardApple}, {testCardLineFoldingString, testCardLineFolding}, + {testCardGoogleMultiValueString, testCardGoogleMultiEmail}, + {testCardGoogleMultiValueWithCommaString, testCardGoogleMultiEmailComma}, } func TestDecoder(t *testing.T) { @@ -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) } }