package models import ( "fmt" "github.com/alpacahq/alpacadecimal" ) type Percentage struct { alpacadecimal.Decimal } // NewPercentage creates a new Percentage from a numeric value, representation: // 50% is represented as 50 func NewPercentage[T float64 | int | alpacadecimal.Decimal](value T) Percentage { p := Percentage{} switch v := any(value).(type) { case int: p = Percentage{Decimal: alpacadecimal.NewFromInt(int64(v))} case float64: p = Percentage{Decimal: alpacadecimal.NewFromFloat(v)} case alpacadecimal.Decimal: p = Percentage{Decimal: v} } return p } func (p Percentage) MarshalJSON() ([]byte, error) { // alpacadecimal by default marshals to a string with quotes return []byte(p.Decimal.String()), nil } func (p *Percentage) UnmarshalJSON(data []byte) error { // alpacadecimal supports unmarshaling both from quoted and non-quoted strings return p.Decimal.UnmarshalJSON(data) } func (p Percentage) String() string { return fmt.Sprintf("%s%%", p.Decimal.String()) } // ApplyTo applies the percentage to a value, e.g: // NewPercentage(50).ApplyTo(100) = 50 func (p Percentage) ApplyTo(value alpacadecimal.Decimal) alpacadecimal.Decimal { return value.Mul(p.Decimal).Div(alpacadecimal.NewFromInt(100)) } // ApplyMarkupTo applies the percentage to a value as a markup, e.g: // NewPercentage(50).ApplyMarkupTo(100) = 150 func (p Percentage) ApplyMarkupTo(value alpacadecimal.Decimal) alpacadecimal.Decimal { return value.Mul(p.Decimal.Add(alpacadecimal.NewFromInt(100))).Div(alpacadecimal.NewFromInt(100)) } func (p Percentage) Add(other Percentage) Percentage { return Percentage{Decimal: p.Decimal.Add(other.Decimal)} }