Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline synchronous processing #19

Merged
merged 1 commit into from
Mar 15, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 100 additions & 82 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,23 @@ const (
//
// Transactions are sorted by date.
func ParseLedger(ledgerReader io.Reader) (generalLedger []*Transaction, err error) {
c, e := ParseLedgerAsync(ledgerReader)
for {
select {
case trans := <-c:
generalLedger = append(generalLedger, trans)
case err := <-e:
sort.Sort(sortTransactionsByDate{generalLedger})
return generalLedger, err
parseLedger(ledgerReader, func(t *Transaction, e error) (stop bool) {
if e != nil {
err = e
stop = true
return
}

generalLedger = append(generalLedger, t)
return
})

if err != nil {
sort.Sort(sortTransactionsByDate{generalLedger})
}
}

var accountToAmountSpace = regexp.MustCompile(" {2,}|\t+")
return
}

// ParseLedgerAsync parses a ledger file and returns a Transaction and error channels .
//
Expand All @@ -42,94 +46,108 @@ func ParseLedgerAsync(ledgerReader io.Reader) (c chan *Transaction, e chan error
e = make(chan error)

go func() {
parseLedger(ledgerReader, func(t *Transaction, err error) (stop bool) {
if err != nil {
e <- err
} else {
c <- t
}
return
})

e <- nil
}()
return c, e
}

var accountToAmountSpace = regexp.MustCompile(" {2,}|\t+")

var trans *Transaction
scanner := bufio.NewScanner(ledgerReader)
var line string
var filename string
var lineCount int
func parseLedger(ledgerReader io.Reader, callback func(t *Transaction, err error) (stop bool)) {
var trans *Transaction
scanner := bufio.NewScanner(ledgerReader)
var line string
var filename string
var lineCount int

errorMsg := func(msg string) {
e <- fmt.Errorf("%s:%d: %s", filename, lineCount, msg)
errorMsg := func(msg string) (stop bool) {
return callback(nil, fmt.Errorf("%s:%d: %s", filename, lineCount, msg))
}

for scanner.Scan() {
line = scanner.Text()

// update filename/line if sentinel comment is found
if strings.HasPrefix(line, markerPrefix) {
filename, lineCount = parseMarker(line)
continue
}

for scanner.Scan() {
line = scanner.Text()
// remove heading and tailing space from the line
trimmedLine := strings.Trim(line, whitespace)
lineCount++

// update filename/line if sentinel comment is found
if strings.HasPrefix(line, markerPrefix) {
filename, lineCount = parseMarker(line)
// handle comments
if commentIdx := strings.Index(trimmedLine, ";"); commentIdx >= 0 {
trimmedLine = trimmedLine[:commentIdx]
if len(trimmedLine) == 0 {
continue
}
}

// remove heading and tailing space from the line
trimmedLine := strings.Trim(line, whitespace)
lineCount++

// handle comments
if commentIdx := strings.Index(trimmedLine, ";"); commentIdx >= 0 {
trimmedLine = trimmedLine[:commentIdx]
if len(trimmedLine) == 0 {
continue
if len(trimmedLine) == 0 {
if trans != nil {
transErr := balanceTransaction(trans)
if transErr != nil {
errorMsg("Unable to balance transaction, " + transErr.Error())
}
callback(trans, nil)
trans = nil
}

if len(trimmedLine) == 0 {
if trans != nil {
transErr := balanceTransaction(trans)
if transErr != nil {
errorMsg("Unable to balance transaction, " + transErr.Error())
}
c <- trans
trans = nil
}
} else if trans == nil {
lineSplit := strings.SplitN(line, " ", 2)
if len(lineSplit) != 2 {
errorMsg("Unable to parse payee line: " + line)
continue
} else if trans == nil {
lineSplit := strings.SplitN(line, " ", 2)
if len(lineSplit) != 2 {
if errorMsg("Unable to parse payee line: " + line) {
return
}
dateString := lineSplit[0]
transDate, dateErr := date.Parse(dateString)
if dateErr != nil {
errorMsg("Unable to parse date: " + dateString)
continue
}
dateString := lineSplit[0]
transDate, dateErr := date.Parse(dateString)
if dateErr != nil {
errorMsg("Unable to parse date: " + dateString)
}
payeeString := lineSplit[1]
trans = &Transaction{Payee: payeeString, Date: transDate}
} else {
var accChange Account
lineSplit := accountToAmountSpace.Split(trimmedLine, -1)
var nonEmptyWords []string
for _, word := range lineSplit {
if len(word) > 0 {
nonEmptyWords = append(nonEmptyWords, word)
}
payeeString := lineSplit[1]
trans = &Transaction{Payee: payeeString, Date: transDate}
}
lastIndex := len(nonEmptyWords) - 1
balErr, rationalNum := getBalance(strings.Trim(nonEmptyWords[lastIndex], whitespace))
if !balErr {
// Assuming no balance and whole line is account name
accChange.Name = strings.Join(nonEmptyWords, " ")
} else {
var accChange Account
lineSplit := accountToAmountSpace.Split(trimmedLine, -1)
nonEmptyWords := []string{}
for _, word := range lineSplit {
if len(word) > 0 {
nonEmptyWords = append(nonEmptyWords, word)
}
}
lastIndex := len(nonEmptyWords) - 1
balErr, rationalNum := getBalance(strings.Trim(nonEmptyWords[lastIndex], whitespace))
if !balErr {
// Assuming no balance and whole line is account name
accChange.Name = strings.Join(nonEmptyWords, " ")
} else {
accChange.Name = strings.Join(nonEmptyWords[:lastIndex], " ")
accChange.Balance = rationalNum
}
trans.AccountChanges = append(trans.AccountChanges, accChange)
accChange.Name = strings.Join(nonEmptyWords[:lastIndex], " ")
accChange.Balance = rationalNum
}
trans.AccountChanges = append(trans.AccountChanges, accChange)
}
// If the file does not end on empty line, we must attempt to balance last
// transaction of the file.
if trans != nil {
transErr := balanceTransaction(trans)
if transErr != nil {
errorMsg("Unable to balance transaction, " + transErr.Error())
}
c <- trans
trans = nil
}
// If the file does not end on empty line, we must attempt to balance last
// transaction of the file.
if trans != nil {
transErr := balanceTransaction(trans)
if transErr != nil {
errorMsg("Unable to balance transaction, " + transErr.Error())
}
e <- nil
}()
return c, e
callback(trans, nil)
}
}

func getBalance(balance string) (bool, *big.Rat) {
Expand Down