Initial Commit
This commit is contained in:
7
README.md
Normal file
7
README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# go-invoicetxt
|
||||
|
||||
A Go library for working with invoice.txt files.
|
||||
|
||||
A invoice.txt file is a plain text file for invoicing purposes. It stems from the same motivations as todo.txt (http://todotxt.org/ ).
|
||||
|
||||
This library is very similar to https://github.com/br0xen/go-todotxt, which is a fork of https://github.com/JamesClonk/go-todotxt.
|
||||
3
go.mod
Normal file
3
go.mod
Normal file
@@ -0,0 +1,3 @@
|
||||
module git.bullercodeworks.com/brian/go-invoicetxt
|
||||
|
||||
go 1.25.5
|
||||
75
invoice.go
Normal file
75
invoice.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user