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