76 lines
1.8 KiB
Go
76 lines
1.8 KiB
Go
// Package invoicetxt implements structs ad routines for working with invoice.txt files
|
|
package invoicetxt
|
|
|
|
import "strings"
|
|
|
|
type Invoice struct {
|
|
ID int `json:"id"` // Invoice id
|
|
Original string `json:"original"` // original raw invoice text
|
|
Hours float64 `json:"hours"`
|
|
Rate float64 `json:"rate"`
|
|
RetainerHours float64 `json:"retainerHours,omitempty"`
|
|
RetainerRate float64 `json:"retainerRate,omitempty"`
|
|
Contexts []string `json:"contexts"`
|
|
Projects []string `json:"projects"`
|
|
AdditionalTags map[string]string `json:"additionalTags"`
|
|
}
|
|
|
|
// ParseInvoice parses the input test string into an Invoice struct
|
|
func ParseInvoice(text string) (*Invoice, error) {
|
|
var err error
|
|
invoice := Invoice{
|
|
AdditionalTags: make(map[string]string),
|
|
}
|
|
invoice.Original = strings.Trim(text, "\t\n\r")
|
|
parts := getParts(invoice.Original)
|
|
var pIdx int
|
|
for pIdx = range parts {
|
|
if partIsSpecial(parts[pIdx]) {
|
|
break
|
|
}
|
|
}
|
|
|
|
return &invoice, err
|
|
}
|
|
|
|
func partIsSpecial(pt string) bool {
|
|
if len(pt) == 0 {
|
|
return false
|
|
}
|
|
return pt[0] == '#' || pt[0] == '@'
|
|
}
|
|
|
|
// getParts parses the text from 'text' pu
|
|
// Generally it just splits everything on spaces, except if we're in a double-quote
|
|
func getParts(text string) []string {
|
|
var ret []string
|
|
var wrk string
|
|
quoteStart := -1
|
|
for i := range text {
|
|
if quoteStart >= 0 {
|
|
if text[i] == '"' {
|
|
quoteStart = -1
|
|
ret = append(ret, wrk)
|
|
} else {
|
|
wrk = wrk + string(text[i])
|
|
}
|
|
} else {
|
|
switch text[i] {
|
|
case '"':
|
|
quoteStart = i
|
|
case ' ':
|
|
if len(wrk) > 0 {
|
|
ret = append(ret, wrk)
|
|
}
|
|
wrk = ""
|
|
default:
|
|
wrk = wrk + string(text[i])
|
|
}
|
|
}
|
|
}
|
|
if len(wrk) > 0 {
|
|
ret = append(ret, wrk)
|
|
}
|
|
return ret
|
|
}
|