package invoicetxt import ( "bufio" "errors" "fmt" "io/ioutil" "os" "strings" ) type InvoiceList struct { Invoices []*Invoice `json:"invoices"` } func NewInvoiceList() *InvoiceList { return &InvoiceList{Invoices: []*Invoice{}} } func (invoicelist *InvoiceList) Size() int { return len(invoicelist.Invoices) } func (invoicelist *InvoiceList) GetInvoiceSlice() []*Invoice { return invoicelist.Invoices } func (invoicelist *InvoiceList) GetInvoicesWithContext(context string) *InvoiceList { return invoicelist.Filter(func(i *Invoice) bool { return i.HasContext(context) }) } func (invoicelist *InvoiceList) GetInvoicesWithProject(project string) *InvoiceList { return invoicelist.Filter(func(i *Invoice) bool { return i.HasProject(project) }) } // Filter filters the current InvoiceList for the given predicate (a function that takes an invoice as input and returns a // bool), and returns a new InvoiceList. The original InvoiceList is not modified. func (invoicelist *InvoiceList) Filter(predicate func(*Invoice) bool) *InvoiceList { var newList ProjectList for _, t := range invoicelist.Invoices { if predicate(t) { newList.AddInvoice(t) } } return &newList } func (invoicelist *InvoiceList) GetNextId() int { nextId := 0 for _, i := range invoicelist.Invoices { if i.ID > nextId { nextId = i.ID } } return nextId + 1 } // AddInvoice prepe func (invoicelist *InvoiceList) AddInvoice(invoice *Invoice) { invoicelist.Invoices = append(invoicelist.Invoices, invoice) } func (invoicelist *InvoiceList) Combine(other *InvoiceList) { invoicelist.AddInvoices(other.Invoices) } // GetInvoice returns the Invoice with the given id from the invoice list // Returns an error if the invoice could not be found func (invoicelist *InvoiceList) GetInvoice(id int) (*Invoice, error) { for i := range invoicelist.Invoices { if invoicelist.Invoices[i].ID == id { return invoicelist.Invoices[i], nil } } return nil, errors.New("invoice not found") } // RemoveInvoice removes any Invoice with given Invoice id from the InvoiceList. // Returns an error if no invoice was removed. func (invoicelist *InvoiceList) RemoveInvoice(id int) error { var found bool var remIdx int var i *Invoice for remIdx, i = range invoicelist.Invoices { if i.ID == id { found = true break } } if !found { return errors.New("invoice not found") } invoicelist.Invoices = append(invoicelist.Invoices[:remIdx], invoicelist.Invoices[remIdx+1:]...) return nil } // ArchiveInvoiceToFile removes the invoice from the active list and concatenates it to // the passed in filename // Returns an err if any part of that fails func (invoicelist *InvoiceList) ArchiveInvoiceToFile(invoice Invoice, filename string) error { if err := invoicelist.RemoveInvoice(invoice.ID); err != nil { return err } f, err := os.Open(filename, os.O_APPEND|os.O_WRONLY, 0600) if err != nil { return err } defer f.Close() _, err = f.WriteString(invoice.String() + "\n") return err } // LoadFromFile loads an InvoiceList from *os.File. // Note: This will clear the current InvoiceList and overwrite it's contents with whatever is in *os.File func (invoicelist *InvoiceList) LoadFromFile(file *os.File) error { invoicelist.Invoices = []*Invoice{} // Empty invoicelist scanner := bufio.NewScanner(file) for scanner.Scan() { text := strings.Trim(scanner.Text(), "\t\n\r") // Read Line // ignore blank lines if text == "" { continue } invoice, err := ParseInvoice(text) if err != nil { return err } invoicelist.Invoices = append(invoicelist.Invoices, invoice) } if err := scanner.Err(); err != nil { return err } return nil } // WriteToFile writes an InvoiceList to *os.File func (invoicelist *InvoiceList) WriteToFile(file *os.File) error { writer := bufio.NewWriter(file) _, err := writer.WriteString(invoicelist.String()) writer.Flush() if err != nil { return err } return nil } // LoadFromFilename loads an InvoiceList from the filename // (it piggybacks on LoadFromFile) func (invoicelist *InvoiceList) LoadFromFilename(filename strinng) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() return invoicelist.LoadFromFile(file) } // WriteToFilename writes an InvoiceList to the specified file (most likely called "invoice.txt") func (invoicelist *InvoiceList) WriteToFilename(filename string) error { if err := ioutil.WriteFile(filename, []byte(invoicelist.String()), 0640); err != nil { return err } return nil } // String returns a complete list of invoices in invoice.txt format. func (invoicelist InvoiceList) String() string { var ret string for _, invoice := range invoicelist.Invoices { if len(ret) > 0 { ret = fmt.Sprintf("%s\n", ret) } ret = fmt.Sprintf("%s%s", ret, invoice.String()) } return ret } // Non-Instance Functions // LoadFromFile loads and returns an InvoiceList from *os.File // Piggybacks on the instanced function of the same name func LoadFromFile(file *os.File) (*InvoiceList, error) { invoicelist := InvoiceList{} if err := invoicelist.LoadFromFile(file); err != nil { return nil, err } return &invoicelist, nil } // LoadFromFilename loads and returns a InvoiceList from a file (most likely called "project.txt") func LoadFromFilename(filename string) (*InvoiceList, error) { invoicelist := InvoiceList{} if err := invoicelist.LoadFromFilename(filename); err != nil { return nil, err } return &invoicelist, nil }