Sean E. Russell: 1 Refactoring 8 files changed, 232 insertions(+), 248 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~psic4t/public-inbox/patches/36035/mbox | git am -3Learn more about email & git
Removes a lot of dead code; localizes variables that didn't need to be global; consistent variable names; remove redundant naming (e.g. type thingStruct struct); generalizes some functions; and general clean-ups. --- README.md | 20 ++++--- caldavserver.go | 135 +++++++++++++++++++++--------------------------- defines.go | 51 +++++++----------- directory.go | 85 +++++++++++------------------- go.mod | 2 +- helpers.go | 64 +++++++++++------------ main.go | 117 ++++++++++++++++++++++++++--------------- parse.go | 6 +-- 8 files changed, 232 insertions(+), 248 deletions(-) diff --git a/README.md b/README.md index e5fcca8..87db3fa 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,20 @@ servers / addressbooks in parallel what makes it quite fast. Its main purpose is displaying addressbook data. Nevertheless it supports basic creation and editing of entries. +qcard was originally written by ~psic4t on SourceHut, but this fork diverges +significantly. + ## Features -- Easy search for contacts -- Parallel fetching of multiple addressbooks -- Easy to use filters -- Create, modify and delete contacts -- Import VCF files -- Display VCF files -- Easy setup -- Supports ICS directories, as made by vdirsyncer +- Easy search for contacts. +- Concerrent fetching of multiple addressbooks . +- Easy to use filters. +- Create, modify and delete contacts . +- Import VCF files. +- Display VCF files. +- Easy setup. +- Supports ICS directories, as made by vdirsyncer as well as caldav servers +- Supports arbitrary number of remote and local accounts. ## Installation / Configuration diff --git a/caldavserver.go b/caldavserver.go index 9732c40..3eaa675 100644 --- a/caldavserver.go +++ b/caldavserver.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/xml" "fmt" + "io" "io/ioutil" "net/http" "os" @@ -18,9 +19,7 @@ type caldavserver struct { calNo int } -func (c caldavserver) getAbProps(receiver chan calProps, errChan chan error) { - defer close(receiver) - defer close(errChan) +func (c caldavserver) GetProps() (Properties, error) { req, err := http.NewRequest("PROPFIND", c.Url, nil) req.SetBasicAuth(c.Username, c.Password) @@ -28,32 +27,74 @@ func (c caldavserver) getAbProps(receiver chan calProps, errChan chan error) { resp, err := cli.Do(req) if err != nil { - errChan <- err - return + return Properties{}, err } xmlContent, _ := ioutil.ReadAll(resp.Body) defer resp.Body.Close() - xmlProps := xmlProps{} + xmlProps := XmlProps{} err = xml.Unmarshal(xmlContent, &xmlProps) if err != nil { - errChan <- err - return + return Properties{}, err } displayName := xmlProps.DisplayName - thisCal := calProps{ + return Properties{ calNo: c.calNo, displayName: displayName, source: c.Url, + }, nil +} + +func (c caldavserver) FetchContacts(receiver chan Contact, errChan chan error) { + defer close(receiver) + defer close(errChan) + var xmlBody string + + xmlBody = `<c:addressbook-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav"><d:prop> + <d:getetag /><c:address-data /> + </d:prop></c:addressbook-query>` + + //fmt.Println(xmlBody) + req, err := http.NewRequest("REPORT", c.Url, strings.NewReader(xmlBody)) + req.SetBasicAuth(c.Username, c.Password) + req.Header.Add("Content-Type", "application/xml; charset=utf-8") + req.Header.Add("Depth", "1") // needed for SabreDAV + req.Header.Add("Prefer", "return-minimal") + + cli := &http.Client{} + resp, err := cli.Do(req) + if err != nil { + errChan <- err + return + } + + xmlContent, _ := ioutil.ReadAll(resp.Body) + defer resp.Body.Close() + + //fmt.Println(string(xmlContent)) + xmlData := XmlData{} + err = xml.Unmarshal(xmlContent, &xmlData) + if err != nil { + errChan <- err + return + } + + for i := range xmlData.Elements { + contactData := xmlData.Elements[i].Data + contactHref := xmlData.Elements[i].Href + ABColor := Colors[c.calNo%len(Colors)] + rv := parseMain(contactData, contactHref, ABColor) + if rv.fullName != "" { + receiver <- rv + } } - receiver <- thisCal } -func (c caldavserver) deleteContact(contactFilename string) error { +func (c caldavserver) DeleteContact(contactFilename string) error { if contactFilename == "" { - fmt.Errorf("No contact filename given") + return fmt.Errorf("No contact filename given") } req, _ := http.NewRequest("DELETE", c.Url+contactFilename, nil) @@ -61,40 +102,28 @@ func (c caldavserver) deleteContact(contactFilename string) error { cli := &http.Client{} resp, err := cli.Do(req) - defer resp.Body.Close() if err != nil { return err } + defer resp.Body.Close() return nil } -func (c caldavserver) dumpContact(contactFilename string, toFile bool) error { +func (c caldavserver) DumpContact(contactFilename string, out io.Writer) error { req, _ := http.NewRequest("GET", c.Url+contactFilename, nil) req.SetBasicAuth(c.Username, c.Password) cli := &http.Client{} resp, err := cli.Do(req) - defer resp.Body.Close() if err != nil { return err } - //fmt.Println(resp.Status) - xmlContent, _ := ioutil.ReadAll(resp.Body) - - if toFile { - // create cache dir if not exists - os.MkdirAll(cacheLocation, os.ModePerm) - err := ioutil.WriteFile(cacheLocation+"/"+contactFilename, xmlContent, 0644) - if err != nil { - return err - } - } else { - fmt.Println(string(xmlContent)) - } + defer resp.Body.Close() + io.Copy(out, resp.Body) return nil } -func (c caldavserver) uploadVCF(contactFilePath string, contactEdit bool) error { +func (c caldavserver) UploadVCF(contactFilePath string, contactEdit bool) error { var vcfData string var contactVCF string var contactFileName string @@ -109,7 +138,6 @@ func (c caldavserver) uploadVCF(contactFilePath string, contactEdit bool) error contactVCF = vcfData contactFileName = genUUID() + `.ics` fmt.Println(contactVCF) - } else { //eventICS, err := ioutil.ReadFile(cacheLocation + "/" + eventFilename) contactVCFByte, err := ioutil.ReadFile(contactFilePath) @@ -139,52 +167,7 @@ func (c caldavserver) uploadVCF(contactFilePath string, contactEdit bool) error return nil } -func (c caldavserver) fetchAbData(receiver chan contactStruct, errChan chan error) { - defer close(receiver) - defer close(errChan) - var xmlBody string - - xmlBody = `<c:addressbook-query xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:carddav"><d:prop> - <d:getetag /><c:address-data /> - </d:prop></c:addressbook-query>` - - //fmt.Println(xmlBody) - req, err := http.NewRequest("REPORT", c.Url, strings.NewReader(xmlBody)) - req.SetBasicAuth(c.Username, c.Password) - req.Header.Add("Content-Type", "application/xml; charset=utf-8") - req.Header.Add("Depth", "1") // needed for SabreDAV - req.Header.Add("Prefer", "return-minimal") - - cli := &http.Client{} - resp, err := cli.Do(req) - if err != nil { - errChan <- err - return - } - - xmlContent, _ := ioutil.ReadAll(resp.Body) - defer resp.Body.Close() - - //fmt.Println(string(xmlContent)) - xmlData := XmlDataStruct{} - err = xml.Unmarshal(xmlContent, &xmlData) - if err != nil { - errChan <- err - return - } - - for i := range xmlData.Elements { - contactData := xmlData.Elements[i].Data - contactHref := xmlData.Elements[i].Href - ABColor := Colors[c.calNo%len(Colors)] - rv := parseMain(contactData, contactHref, ABColor) - if rv.fullName != "" { - receiver <- rv - } - } -} - -func (c caldavserver) createContact(contactData string) error { +func (c caldavserver) CreateContact(contactData string) error { str, err := createContact(contactData) if err != nil { return err diff --git a/defines.go b/defines.go index 865b31f..d28abea 100644 --- a/defines.go +++ b/defines.go @@ -3,31 +3,19 @@ package main import ( _ "embed" "encoding/xml" - "os" - "time" + "io" ) -var err string -var homedir string = os.Getenv("HOME") -var editor string = os.Getenv("EDITOR") -var configLocation string = (homedir + "/" + ConfigDir + "/config.json") -var cacheLocation string = (homedir + "/" + CacheDir) -var versionLocation string = (cacheLocation + "/version.json") -var timezone, _ = time.Now().Zone() -var xmlContent []byte +var Version string = "0.6.0" + var showDetails bool var showFilename bool var showEmailOnly *bool -var displayFlag bool -var toFile bool var filter string -var searchterm string -// var colorBlock string = "█" var colorBlock string = "|" var Colors = [10]string{"\033[0;31m", "\033[0;32m", "\033[1;33m", "\033[1;34m", "\033[1;35m", "\033[1;36m", "\033[1;37m", "\033[1;38m", "\033[1;39m", "\033[1;40m"} var showColor bool = true -var qcardversion string = "0.6.0" const ( ConfigDir = ".config/qcard" @@ -40,22 +28,23 @@ const ( ColBlue = "\033[1;34m" ) -type addressBook interface { - getAbProps(chan calProps, chan error) - fetchAbData(chan contactStruct, chan error) - deleteContact(string) error - dumpContact(string, bool) error - uploadVCF(string, bool) error - createContact(string) error +type AddressBook interface { + // GetProps fetches properties of the address book + GetProps() (Properties, error) + FetchContacts(chan Contact, chan error) + DeleteContact(string) error + DumpContact(string, io.Writer) error + UploadVCF(string, bool) error + CreateContact(string) error } -type configStruct struct { - Addressbooks []addressBook +type Config struct { + Addressbooks []AddressBook DetailThreshold int SortByLastname bool } -type contactStruct struct { +type Contact struct { Href string Color string fullName string @@ -75,7 +64,7 @@ type contactStruct struct { note string } -type xmlProps struct { +type XmlProps struct { calNo string Url string XMLName xml.Name `xml:"multistatus"` @@ -87,19 +76,19 @@ type xmlProps struct { LastModified string `xml:"response>propstat>prop>getlastmodified"` } -type calProps struct { +type Properties struct { calNo int displayName string source string color string } -type XmlDataStruct struct { +type XmlData struct { XMLName xml.Name `xml:"multistatus"` - Elements []xmlDataElements `xml:"response"` + Elements []XmlDataElements `xml:"response"` } -type xmlDataElements struct { +type XmlDataElements struct { XMLName xml.Name `xml:"response"` Href string `xml:"href"` ETag string `xml:"propstat>prop>getetag"` @@ -107,4 +96,4 @@ type xmlDataElements struct { } //go:embed "vcard.templ" -var contactSkel string +var ContactSkel string diff --git a/directory.go b/directory.go index a131704..264cb72 100644 --- a/directory.go +++ b/directory.go @@ -9,31 +9,43 @@ import ( "path/filepath" ) -type directory struct { +type Directory struct { Path string calNo int } -func (c directory) getAbProps(receiver chan calProps, errChan chan error) { +func (c Directory) GetProps() (Properties, error) { + return Properties{ + calNo: c.calNo, + displayName: filepath.Base(c.Path), + source: c.Path, + }, nil +} + +func (c Directory) FetchContacts(receiver chan Contact, errChan chan error) { defer close(receiver) defer close(errChan) ds, err := os.ReadDir(c.Path) - fmt.Printf("getAbProps have %d contacts\n", len(ds)) if err != nil { errChan <- err return } - for i, d := range ds { - xp := calProps{ - calNo: i, - displayName: d.Name(), - source: d.Name(), + for _, file := range ds { + contactData, err := ioutil.ReadFile(filepath.Join(c.Path, file.Name())) + if err != nil { + errChan <- err + return + } + contactHref := file.Name() + ABColor := Colors[c.calNo%len(Colors)] + rv := parseMain(string(contactData), contactHref, ABColor) + if rv.fullName != "" { + receiver <- rv } - receiver <- xp } } -func (c directory) deleteContact(contactFilename string) error { +func (c Directory) DeleteContact(contactFilename string) error { if contactFilename == "" { return fmt.Errorf("No contact filename given") } @@ -43,42 +55,30 @@ func (c directory) deleteContact(contactFilename string) error { return nil } -func (c directory) dumpContact(contactFilename string, toFile bool) error { - xmlContent, err := ioutil.ReadFile(contactFilename) +func (c Directory) DumpContact(contactFilename string, out io.Writer) error { + file, err := os.Open(contactFilename) if err != nil { return err } - - if toFile { - // create cache dir if not exists - os.MkdirAll(cacheLocation, os.ModePerm) - err := ioutil.WriteFile(cacheLocation+"/"+contactFilename, xmlContent, 0644) - if err != nil { - return err - } - } else { - fmt.Println(string(xmlContent)) - } + defer file.Close() + io.Copy(out, file) return nil } -func (c directory) uploadVCF(contactFilePath string, contactEdit bool) error { +func (c Directory) UploadVCF(contactFilePath string, contactEdit bool) error { var contactFileName string var fin io.Reader + contactFileName = genUUID() + `.ics` // no edit, so new filename if contactFilePath == "-" { fin = os.Stdin - contactFileName = genUUID() + `.ics` // no edit, so new filename } else { var err error - fin, err = os.Open(contactFilePath) - if err != nil { + if fin, err = os.Open(contactFilePath); err != nil { return err } - if contactEdit == true { + if contactEdit { contactFileName = path.Base(contactFilePath) // use old filename again - } else { - contactFileName = genUUID() + `.ics` // no edit, so new filename } } fout, err := os.Create(filepath.Join(c.Path, contactFileName)) @@ -89,30 +89,7 @@ func (c directory) uploadVCF(contactFilePath string, contactEdit bool) error { return err } -func (c directory) fetchAbData(receiver chan contactStruct, errChan chan error) { - defer close(receiver) - defer close(errChan) - ds, err := os.ReadDir(c.Path) - if err != nil { - errChan <- err - return - } - for _, file := range ds { - contactData, err := ioutil.ReadFile(filepath.Join(c.Path, file.Name())) - if err != nil { - errChan <- err - return - } - contactHref := file.Name() - ABColor := Colors[c.calNo%len(Colors)] - rv := parseMain(string(contactData), contactHref, ABColor) - if rv.fullName != "" { - receiver <- rv - } - } -} - -func (c directory) createContact(contactData string) error { +func (c Directory) CreateContact(contactData string) error { str, err := createContact(contactData) if err != nil { return err diff --git a/go.mod b/go.mod index 6b48968..841ee61 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module data.haus/qcard +module ser1.net/qcard go 1.17 diff --git a/helpers.go b/helpers.go index c22484b..83c2816 100644 --- a/helpers.go +++ b/helpers.go @@ -9,18 +9,20 @@ import ( "log" "os" "path" + "path/filepath" "regexp" - "sort" "strconv" "strings" + "sync" "text/template" "time" ) -func getConf() configStruct { +func getConf() Config { + configLocation := filepath.Join(os.Getenv("HOME"), ConfigDir, "config.json") configData, err := ioutil.ReadFile(configLocation) if err != nil { - fmt.Print("Config not found. \n\nPlease copy config-sample.json to ~/.config/qcard/config.json and modify it accordingly.\n\n") + fmt.Printf("Config not found.\n\nPlease copy config-sample.json to %s and modify it accordingly.\n\n", configLocation) log.Fatal(err) } configMap := make(map[string]interface{}) @@ -29,8 +31,8 @@ func getConf() configStruct { if err != nil { log.Fatalf("error loading config from %s: %s", configLocation, err) } - conf := configStruct{ - Addressbooks: make([]addressBook, 0), + conf := Config{ + Addressbooks: make([]AddressBook, 0), } if dt, ok := configMap["DetailThreshold"].(float64); ok { conf.DetailThreshold = int(dt) @@ -50,7 +52,7 @@ func getConf() configStruct { calNo: ctr, }) } else if m["Path"] != "" { - conf.Addressbooks = append(conf.Addressbooks, directory{ + conf.Addressbooks = append(conf.Addressbooks, Directory{ Path: m["Path"].(string), calNo: ctr, }) @@ -64,27 +66,24 @@ func getConf() configStruct { } func getAbList() { - props := make(chan calProps, 10) - errs := make(chan error, 3) - for _, a := range config.Addressbooks { - go a.getAbProps(props, errs) - } - - p := []calProps{} - for prop := range props { - p = append(p, prop) - } - for err := range errs { - fmt.Printf("error getting ab list: %v\n", err) + wg := sync.WaitGroup{} + printChan := make(chan string, 10) + for _, ab := range config.Addressbooks { + wg.Add(1) + go func(a AddressBook) { + p, err := a.GetProps() + if err != nil { + fmt.Printf("error getting ab list: %v\n", err) + return + } + printChan <- fmt.Sprintf("[%v] - %s%s%s %s (%s)", p.calNo, Colors[p.calNo], colorBlock, ColDefault, p.displayName, p.source) + wg.Done() + }(ab) } - - sort.Slice(p, func(i, j int) bool { - return p[i].calNo < (p[j].calNo) - }) - - for i, pi := range p { - fmt.Printf("[%v] - %s%s%s %s (%s)\n", i, Colors[i], colorBlock, ColDefault, pi.displayName, pi.source) + for s := range printChan { + fmt.Println(s) } + wg.Wait() } var keyMap map[string]string = map[string]string{ @@ -157,10 +156,7 @@ func splitAfter(s string, re *regexp.Regexp) (r []string) { return } -func (e contactStruct) fancyOutput() { - //fmt.Println(e.name) - //fmt.Printf(`%5s`, ` `) - //fmt.Println(` M: ` + e.phoneCell) +func (e Contact) fancyOutput() { if *showEmailOnly { showDetails = false @@ -234,7 +230,7 @@ func (e contactStruct) fancyOutput() { } //fmt.Println() } -func (e contactStruct) vcfOutput() { +func (e Contact) vcfOutput() { // whole day or greater fmt.Println(`Contact =======`) @@ -299,13 +295,13 @@ func createContact(contactData string) (string, error) { data["uuid"] = genUUID() fullName := data["fullName"] - lastInd := strings.LastIndex(fullName, " ") // split name at last space - name := fullName[lastInd+1:] + ";" + fullName[0:lastInd] + ";;;" // lastname, givenname1 givenname2 - data["name"] = name + names := strings.Split(fullName, " ") + namesN := append([]string{names[len(names)-1]}, names[0:len(names)-1]...) + data["name"] = strings.Join(namesN, ";") // lastname, givenname1 givenname2 var out strings.Builder t := template.New("vcard") - te, err := t.Parse(contactSkel) + te, err := t.Parse(ContactSkel) if err != nil { return "", err } diff --git a/main.go b/main.go index 69a7311..cf8312c 100644 --- a/main.go +++ b/main.go @@ -1,25 +1,31 @@ package main import ( - // "bytes" "flag" "fmt" - "log" + "io" "os" "os/exec" + "path/filepath" "sort" - //"strconv" + "sync" ) -var config configStruct +// TODO Fix the documentatiotn, esp installation +// TODO normalize variable naming and structure +// TODO clean up the rampant global state +// TODO add API documentation +// TODO add unit tests +// TODO the AUR package should copy the conf files to the right plances +// TODO rename package and add communication channels + +var config Config func main() { config = getConf() toFile := false + var displayFlag bool - if len(os.Args[1:]) > 0 { - searchterm = os.Args[1] - } flag.StringVar(&filter, "s", "", "Search term") //flag.BoolVar(&showInfo, "i", false, "Show additional info like description and location for contacts") flag.BoolVar(&showFilename, "f", false, "Show contact filename for editing or deletion") @@ -38,6 +44,9 @@ func main() { flagset := make(map[string]bool) // map for flag.Visit. get bools to determine set flags flag.Visit(func(f *flag.Flag) { flagset[f.Name] = true }) + cacheLocation := filepath.Join(os.Getenv("HOME"), CacheDir) + os.MkdirAll(cacheLocation, os.ModePerm) + // TODO if *showAddressbooks { } @@ -52,23 +61,39 @@ func main() { } ab := config.Addressbooks[*abNo] if flagset["delete"] { - ab.deleteContact(*contactDelete) + ab.DeleteContact(*contactDelete) } else if flagset["d"] { - ab.dumpContact(*contactDump, toFile) + // make file here + // create cache dir if not exists + var out io.Writer + if toFile { + // create cache dir if not exists + os.MkdirAll(cacheLocation, os.ModePerm) + fname := filepath.Join(cacheLocation, *contactDump) + file, err := os.Create(fname) + if err != nil { + fmt.Printf("error creating contact file %s: %s\n", fname, err) + os.Exit(1) + } + out = file + } else { + out = os.Stdout + } + ab.DumpContact(*contactDump, out) } else if flagset["p"] { displayVCF() } else if flagset["n"] { - ab.createContact(*contactNew) + ab.CreateContact(*contactNew) } else if flagset["u"] { contactEdit := false - ab.uploadVCF(*contactFile, contactEdit) + ab.UploadVCF(*contactFile, contactEdit) } else if flagset["edit"] { editContact(ab, *contactEdit) } } else if flagset["l"] { getAbList() } else if *version { - fmt.Println("qcard " + qcardversion) + fmt.Println("qcard " + Version) } else if flagset["a"] { showAddresses(*abNo) } else { @@ -77,55 +102,65 @@ func main() { } func showAddresses(abn int) { - abs := make(chan contactStruct, 10) - ech := make(chan error, 3) + abs := make(chan Contact, 10) + ech := make(chan error, 10) if abn < 0 { for _, ab := range config.Addressbooks { - go ab.fetchAbData(abs, ech) + go ab.FetchContacts(abs, ech) } } else { ab := config.Addressbooks[abn] - go ab.fetchAbData(abs, ech) + go ab.FetchContacts(abs, ech) } - contactsSlice := make([]contactStruct, 0) - for cs := range abs { - contactsSlice = append(contactsSlice, cs) - } - for e := range ech { - fmt.Printf("error processing: %s\n", e) - } + contacts := make([]Contact, 0) + wg := sync.WaitGroup{} + wg.Add(2) + go func() { + for c := range abs { + contacts = append(contacts, c) + } + wg.Done() + }() + go func() { + for e := range ech { + fmt.Printf("error processing: %s\n", e) + } + wg.Done() + }() + wg.Wait() - if len(contactsSlice) == 0 { - log.Fatal("no contacts") // get out if nothing found + if len(contacts) == 0 { + return } // TODO: Allow sort by first and last name - sort.Slice(contactsSlice, func(i, j int) bool { + sort.Slice(contacts, func(i, j int) bool { if config.SortByLastname { - return contactsSlice[i].name < contactsSlice[j].name + return contacts[i].name < contacts[j].name } else { - return contactsSlice[i].fullName < contactsSlice[j].fullName + return contacts[i].fullName < contacts[j].fullName } }) - if len(contactsSlice) <= config.DetailThreshold { - showDetails = true - } - - if *showEmailOnly { - fmt.Println("Searching...") - } + showDetails = len(contacts) <= config.DetailThreshold - for _, e := range contactsSlice { + for _, e := range contacts { e.fancyOutput() // pretty print } } -func editContact(c addressBook, contactFilename string) error { - toFile = true +func editContact(c AddressBook, contactFilename string) error { + cacheLocation := filepath.Join(os.Getenv("HOME"), CacheDir) + // create cache dir if not exists + fname := filepath.Join(cacheLocation, contactFilename) + toFile, err := os.Create(fname) + if err != nil { + fmt.Printf("error creating contact file %s: %s\n", fname, err) + os.Exit(1) + } contactEdit := true - c.dumpContact(contactFilename, toFile) + c.DumpContact(contactFilename, toFile) //fmt.Println(appointmentEdit) filePath := cacheLocation + "/" + contactFilename fileInfo, err := os.Stat(filePath) @@ -134,7 +169,7 @@ func editContact(c addressBook, contactFilename string) error { } beforeMTime := fileInfo.ModTime() - shell := exec.Command(editor, filePath) + shell := exec.Command(os.Getenv("EDITOR"), filePath) shell.Stdout = os.Stdin shell.Stdin = os.Stdin shell.Stderr = os.Stderr @@ -147,7 +182,7 @@ func editContact(c addressBook, contactFilename string) error { afterMTime := fileInfo.ModTime() if beforeMTime.Before(afterMTime) { - c.uploadVCF(filePath, contactEdit) + c.UploadVCF(filePath, contactEdit) } else { return err } diff --git a/parse.go b/parse.go index f80b97d..695ff31 100644 --- a/parse.go +++ b/parse.go @@ -104,12 +104,12 @@ func parseContactNickname(contactData string) string { return trimField(result, "(?i)NICKNAME:") } -func parseMain(contactData, href, color string) contactStruct { +func parseMain(contactData, href, color string) Contact { //fmt.Println(parseContactName(contactData)) fullName := parseContactFullName(contactData) if (filter == "") || (filterMatch(fullName) == true) { - data := contactStruct{ + data := Contact{ Href: href, Color: color, fullName: fullName, @@ -130,5 +130,5 @@ func parseMain(contactData, href, color string) contactStruct { } return data } - return contactStruct{} + return Contact{} } -- 2.37.2