package printer import ( "strconv" "olexsmir.xyz/clerk/internal/decimal" "olexsmir.xyz/clerk/journal/ast" ) func (p *printer) writeAmount(a *ast.Amount, pos CommodityPos) { if a == nil { return } if a.IsExpr { p.buf.WriteString(a.Expr) return } comm := a.Commodity if comm == "" { p.writeDecimal(a.Quantity, a.QuantityFmt, 2) return } switch pos { case CommodityBefore: p.buf.WriteString(comm) if a.HasSpace { p.buf.WriteByte(' ') } p.writeDecimal(a.Quantity, a.QuantityFmt, 2) case CommodityAfter: p.writeDecimal(a.Quantity, a.QuantityFmt, 2) if a.HasSpace { p.buf.WriteByte(' ') } p.buf.WriteString(comm) default: panic("invalid CommodityPos value") } } func (p *printer) writeCost(c *ast.Cost, pos CommodityPos) { if c == nil { return } if c.IsTotal { p.buf.WriteString(" @@ ") } else { p.buf.WriteString(" @ ") } p.writeAmount(&c.Amount, pos) } func (p *printer) writeBalanceAssertion(ba *ast.BalanceAssertion, pos CommodityPos) { if ba == nil { return } p.buf.WriteByte(' ') switch { case ba.IsInclusive: p.buf.WriteString("=== ") case ba.IsStrict: p.buf.WriteString("== ") default: p.buf.WriteString("= ") } p.writeAmount(&ba.Amount, pos) } func (p *printer) writeDecimal(d decimal.Decimal, fmt ast.QuantityFormat, forcePrec int) { if d.IsZero() { p.writeZero(fmt, forcePrec) return } coeff := d.Coeff() if coeff == nil || coeff.Sign() == 0 { p.writeZero(fmt, forcePrec) return } offset := d.Scale() neg := coeff.Sign() < 0 raw := coeff.String() if neg { raw = raw[1:] } decSep := byte('.') if fmt.Decimal != 0 { decSep = fmt.Decimal } if offset >= len(raw) { zeros := offset - len(raw) + 1 if neg { p.buf.WriteByte('-') } p.buf.WriteByte('0') p.buf.WriteByte(decSep) for i := 0; i < zeros-1; i++ { p.buf.WriteByte('0') } if len(raw) < forcePrec { p.buf.WriteString(raw) for i := len(raw); i < forcePrec; i++ { p.buf.WriteByte('0') } } else { p.buf.WriteString(raw[:forcePrec]) } return } split := len(raw) - offset intPart := raw[:split] fracPart := raw[split:] intPart = trimLeadingZeros(intPart) if intPart == "" { intPart = "0" } if neg { p.buf.WriteByte('-') } if fmt.Thousands != 0 && len(intPart) > 3 { p.writeThousands(intPart, fmt.Thousands) } else { p.buf.WriteString(intPart) } p.buf.WriteByte(decSep) if len(fracPart) < forcePrec { p.buf.WriteString(fracPart) for i := len(fracPart); i < forcePrec; i++ { p.buf.WriteByte('0') } } else { p.buf.WriteString(fracPart[:forcePrec]) } } func (p *printer) writeZero(fmt ast.QuantityFormat, prec int) { p.buf.WriteByte('0') sep := "." if fmt.Decimal != 0 { sep = string(fmt.Decimal) } p.buf.WriteString(sep) for range prec { p.buf.WriteByte('0') } } func (p *printer) writeThousands(s string, sep byte) { n := len(s) for i := range n { if i > 0 && (n-i)%3 == 0 { p.buf.WriteByte(sep) } p.buf.WriteByte(s[i]) } } func trimLeadingZeros(s string) string { for i := 0; i < len(s); i++ { if s[i] != '0' { return s[i:] } } return "" } func quoteString(s string) string { needsQuote := false for _, c := range s { if c == ' ' || c == '\t' || c == '"' || c == ';' || c == '#' { needsQuote = true break } } if !needsQuote { return s } return strconv.Quote(s) }