Skip to content

Commit 047f0fd

Browse files
committed
feat: decimal format
1 parent ec1b379 commit 047f0fd

File tree

22 files changed

+1898
-162
lines changed

22 files changed

+1898
-162
lines changed

_testdata/positive/format_gen.json

Lines changed: 1323 additions & 117 deletions
Large diffs are not rendered by default.

_testdata/positive/sample.json

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,26 @@
504504
}
505505
}
506506
},
507+
"/testDecimalValidation": {
508+
"post": {
509+
"operationId": "testDecimalValidation",
510+
"requestBody": {
511+
"required": true,
512+
"content": {
513+
"application/json": {
514+
"schema": {
515+
"$ref": "#/components/schemas/TestDecimalValidation"
516+
}
517+
}
518+
}
519+
},
520+
"responses": {
521+
"200": {
522+
"description": "Ok"
523+
}
524+
}
525+
}
526+
},
507527
"/oneofBug": {
508528
"post": {
509529
"operationId": "oneofBug",
@@ -1656,6 +1676,26 @@
16561676
}
16571677
}
16581678
},
1679+
"TestDecimalValidation": {
1680+
"type": "object",
1681+
"required": [
1682+
"minmax",
1683+
"multipleOf"
1684+
],
1685+
"properties": {
1686+
"minmax": {
1687+
"type": "number",
1688+
"format": "decimal",
1689+
"minimum": 1.5,
1690+
"maximum": 2.0
1691+
},
1692+
"multipleOf": {
1693+
"type": "number",
1694+
"format": "decimal",
1695+
"multipleOf": 5.0
1696+
}
1697+
}
1698+
},
16591699
"AnyTest": {
16601700
"type": "object",
16611701
"properties": {

conv/decode.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/go-faster/jx"
1616
"github.com/google/uuid"
17+
"github.com/shopspring/decimal"
1718

1819
"github.com/ogen-go/ogen/json"
1920
)
@@ -74,6 +75,10 @@ func ToFloat64(s string) (float64, error) {
7475
return strconv.ParseFloat(s, 64)
7576
}
7677

78+
func ToDecimal(s string) (decimal.Decimal, error) {
79+
return decimal.NewFromString(s)
80+
}
81+
7782
func ToString(s string) (string, error) {
7883
return s, nil
7984
}
@@ -210,6 +215,10 @@ func ToStringFloat64(s string) (float64, error) {
210215
return strconv.ParseFloat(s, 64)
211216
}
212217

218+
func ToStringDecimal(s string) (decimal.Decimal, error) {
219+
return decimal.NewFromString(s)
220+
}
221+
213222
type (
214223
ogenUnmarshaler[T any] interface {
215224
json.Unmarshaler

conv/encode.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"github.com/go-faster/jx"
1515
"github.com/google/uuid"
16+
"github.com/shopspring/decimal"
1617

1718
"github.com/ogen-go/ogen/json"
1819
)
@@ -32,6 +33,8 @@ func Uint64ToString(v uint64) string { return strconv.FormatUint(v, 10) }
3233
func Float32ToString(v float32) string { return strconv.FormatFloat(float64(v), 'f', 10, 64) }
3334
func Float64ToString(v float64) string { return strconv.FormatFloat(v, 'f', 10, 64) }
3435

36+
func DecimalToString(v decimal.Decimal) string { return v.String() }
37+
3538
func BoolToString(v bool) string { return strconv.FormatBool(v) }
3639

3740
func StringToString(v string) string { return v }
@@ -73,6 +76,8 @@ func StringUint64ToString(v uint64) string { return strconv.FormatUint(v, 10) }
7376
func StringFloat32ToString(v float32) string { return strconv.FormatFloat(float64(v), 'g', 10, 32) }
7477
func StringFloat64ToString(v float64) string { return strconv.FormatFloat(v, 'g', 10, 64) }
7578

79+
func StringDecimalToString(v decimal.Decimal) string { return v.String() }
80+
7681
type (
7782
marshaler[T any] interface {
7883
json.Marshaler

gen/_template/validators.tmpl

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@
9595
Hostname: {{ $v.Hostname }},
9696
{{- if $v.Regex }}
9797
Regex: regexMap[{{ quote $v.Regex }}],
98-
{{- else }}
98+
{{- else }}
9999
Regex: nil,
100100
{{- end }}
101101
}).Validate(string({{ $.Var }})); err != nil {
@@ -120,8 +120,8 @@
120120
}
121121
{{- end }}
122122

123-
{{- if $t.IsFloat }}
124-
{{- $validated = true }}
123+
{{- if $t.IsFloat }}
124+
{{- $validated = true }}
125125
{{- $v := $va.Float }}
126126
if err := (validate.Float{ {{ if $v.Set }}
127127
MinSet: {{ $v.MinSet }},
@@ -133,13 +133,34 @@
133133
MultipleOfSet:{{ $v.MultipleOfSet }},
134134
{{- if $v.MultipleOf }}
135135
MultipleOf: ratMap[{{ quote $v.MultipleOf.RatString }}],
136-
{{- else }}
136+
{{- else }}
137137
MultipleOf: nil,
138138
{{- end }}
139139
{{ end }} }).Validate{{- if $t.IsStringifiedFloat -}}Stringified{{- end }}(float64({{ $.Var }})); err != nil {
140140
return errors.Wrap(err, "float")
141141
}
142-
{{- end }}
142+
{{- end }}
143+
144+
{{- if $t.IsDecimal }}
145+
{{- $validated = true }}
146+
{{- $v := $va.Decimal }}
147+
if err := (validate.Decimal{ {{ if $v.Set }}
148+
MinSet: {{ $v.MinSet }},
149+
Min: decimal.RequireFromString({{ quote $v.Min.String }}),
150+
MaxSet: {{ $v.MaxSet }},
151+
Max: decimal.RequireFromString({{ quote $v.Max.String }}),
152+
MinExclusive: {{ $v.MinExclusive }},
153+
MaxExclusive: {{ $v.MaxExclusive }},
154+
MultipleOfSet:{{ $v.MultipleOfSet }},
155+
{{- if $v.MultipleOfSet }}
156+
MultipleOf: decimal.RequireFromString({{ quote $v.MultipleOf.String }}),
157+
{{- else }}
158+
MultipleOf: decimal.Zero,
159+
{{- end }}
160+
{{ end }} }).Validate({{ $.Var }}); err != nil {
161+
return errors.Wrap(err, "decimal")
162+
}
163+
{{- end }}
143164
{{- end }}
144165

145166
{{- if gt (len $va.Ogen) 0 }}
@@ -167,9 +188,9 @@
167188
{{- if $t.NeedValidation }}{{/*Need validation*/}}
168189
{{ if $t.IsStruct }}{{/*If struct*/}}
169190
func (s {{ $t.ReadOnlyReceiver }}) Validate() error {
170-
if s == nil {
171-
return validate.ErrNilPointer
172-
}
191+
if s == nil {
192+
return validate.ErrNilPointer
193+
}
173194

174195
var failures []validate.FieldError
175196
{{- if gt (len $t.Validators.Ogen) 0 }}

gen/imports.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func defaultImports() map[string]string {
2828
"github.com/go-faster/errors": "",
2929
"github.com/go-faster/jx": "",
3030
"github.com/google/uuid": "",
31+
"github.com/shopspring/decimal": "",
3132
"go.opentelemetry.io/otel": "",
3233
"go.opentelemetry.io/otel/attribute": "",
3334
"go.opentelemetry.io/otel/codes": "",

gen/ir/faker.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ func (t *Type) FakeValue() string {
4242
return `url.URL{Scheme:"https", Host:"github.com", Path:"/ogen-go/ogen"}`
4343
case Bool:
4444
return "true"
45+
case Decimal:
46+
return "decimal.Zero"
4547
case Null:
4648
return "struct{}{}"
4749
default:

gen/ir/json.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ func (j JSON) Format() string {
178178
return typePrefix("UnixMicro")
179179
case "unix-milli":
180180
return typePrefix("UnixMilli")
181+
case "decimal": //nolint:goconst // This whole switch is duplicated in NamePostfix. Should create a common helper.
182+
return typePrefix("Decimal")
181183
default:
182184
return ""
183185
}

gen/ir/primitive.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,6 @@ const (
3838
IP PrimitiveType = "netip.Addr"
3939
URL PrimitiveType = "url.URL"
4040
File PrimitiveType = "ht.MultipartFile"
41+
Decimal PrimitiveType = "decimal.Decimal"
4142
Custom PrimitiveType = "<custom>"
4243
)

gen/ir/template_helpers.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ func (t *Type) EncodeFn() string {
2121
Float32, Float64,
2222
String, Bool:
2323
return naming.Capitalize(t.Primitive.String())
24-
case UUID, Time, MAC, IP, Duration, URL:
24+
case UUID, Time, MAC, IP, Duration, URL, Decimal:
2525
return naming.AfterDot(t.Primitive.String())
2626
default:
2727
return ""
@@ -61,7 +61,8 @@ func (t Type) uriFormat() string {
6161
"uint8",
6262
"uint16",
6363
"uint32",
64-
"uint64":
64+
"uint64",
65+
"decimal":
6566
if s.Type != jsonschema.String {
6667
break
6768
}
@@ -149,6 +150,10 @@ func (t *Type) IsFloat() bool {
149150
}
150151
}
151152

153+
func (t *Type) IsDecimal() bool {
154+
return t.Primitive == Decimal
155+
}
156+
152157
func (t *Type) IsStringifiedFloat() bool {
153158
s := t.Schema
154159
return t.IsFloat() &&
@@ -173,7 +178,7 @@ func (t *Type) IsInterface() bool { return t.Is(KindInterface) }
173178
func (t *Type) IsSum() bool { return t.Is(KindSum) }
174179
func (t *Type) IsAny() bool { return t.Is(KindAny) }
175180
func (t *Type) IsStream() bool { return t.Is(KindStream) }
176-
func (t *Type) IsNumeric() bool { return t.IsInteger() || t.IsFloat() }
181+
func (t *Type) IsNumeric() bool { return t.IsInteger() || t.IsFloat() || t.IsDecimal() }
177182
func (t *Type) IsExternal() bool { return t.Schema != nil && t.Schema.XOgenType != "" }
178183

179184
func (t *Type) MustField(name string) *Field {

0 commit comments

Comments
 (0)