Initial Commit

This commit is contained in:
2025-12-04 11:50:04 -06:00
commit 0da41b2326
3 changed files with 85 additions and 0 deletions

7
README.md Normal file
View 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
View File

@@ -0,0 +1,3 @@
module git.bullercodeworks.com/brian/go-invoicetxt
go 1.25.5

75
invoice.go Normal file
View 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
}