lawspec 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API-MIGRATION.md +78 -2
- package/LANGUAGE.md +10 -7
- package/NATIVE-BINDINGS.md +1046 -0
- package/PRIMITIVES.md +1 -1
- package/README.md +73 -23
- package/REFINEMENTS.md +1 -1
- package/RELEASE-0.10.md +60 -0
- package/api.mjs +23 -3
- package/bin/lawspec.mjs +17 -4
- package/build.json +56 -36
- package/core.wasm +0 -0
- package/examples/native-payments/go/example/payments/domain.go +38 -0
- package/examples/native-payments/go/example/payments/native_generators_test.go +13 -0
- package/examples/native-payments/go/lawspec.json +136 -0
- package/examples/native-payments/haskell/lawspec.json +149 -0
- package/examples/native-payments/haskell/src/PaymentsDomain.hs +21 -0
- package/examples/native-payments/haskell/test/PaymentGenerators.hs +12 -0
- package/examples/native-payments/java/lawspec.json +165 -0
- package/examples/native-payments/java/src/main/java/domain/PaymentsDomain.java +32 -0
- package/examples/native-payments/java/src/test/java/domain/PaymentGenerators.java +16 -0
- package/examples/native-payments/javascript/lawspec.json +149 -0
- package/examples/native-payments/javascript/src/payments_domain.mjs +44 -0
- package/examples/native-payments/javascript/test/lawspec_generators.mjs +6 -0
- package/examples/native-payments/kotlin/lawspec.json +165 -0
- package/examples/native-payments/kotlin/src/main/kotlin/domain/PaymentsDomain.kt +17 -0
- package/examples/native-payments/kotlin/src/test/kotlin/domain/PaymentGenerators.kt +12 -0
- package/examples/native-payments/python/lawspec.json +149 -0
- package/examples/native-payments/python/src/payments_domain.py +60 -0
- package/examples/native-payments/python/tests/lawspec_generators.py +15 -0
- package/examples/native-payments/rust/lawspec.json +167 -0
- package/examples/native-payments/rust/src/domain.rs +37 -0
- package/examples/native-payments/rust/src/lib.rs +2 -0
- package/examples/native-payments/rust/tests/support/lawspec_generators.rs +11 -0
- package/examples/native-payments/typescript/lawspec.json +149 -0
- package/examples/native-payments/typescript/src/payments_domain.ts +42 -0
- package/examples/native-payments/typescript/test/lawspec_generators.ts +8 -0
- package/examples/specs/payments.lawspec +76 -0
- package/examples-command.mjs +5 -0
- package/files.mjs +6 -6
- package/index.d.ts +53 -2
- package/native-examples.mjs +81 -0
- package/package.json +2 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"sources": [
|
|
4
|
+
"laws"
|
|
5
|
+
],
|
|
6
|
+
"machineBits": 64,
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"language": "javascript",
|
|
10
|
+
"root": ".",
|
|
11
|
+
"nativeBindings": {
|
|
12
|
+
"types": [
|
|
13
|
+
{
|
|
14
|
+
"type": "example.payments::type::Currency",
|
|
15
|
+
"native": [
|
|
16
|
+
"payments_domain",
|
|
17
|
+
"CurrencyCode"
|
|
18
|
+
],
|
|
19
|
+
"constructors": [
|
|
20
|
+
{
|
|
21
|
+
"constructor": "USD",
|
|
22
|
+
"native": [
|
|
23
|
+
"payments_domain",
|
|
24
|
+
"Dollars"
|
|
25
|
+
],
|
|
26
|
+
"style": "unit",
|
|
27
|
+
"fields": []
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"constructor": "EUR",
|
|
31
|
+
"native": [
|
|
32
|
+
"payments_domain",
|
|
33
|
+
"Euros"
|
|
34
|
+
],
|
|
35
|
+
"style": "unit",
|
|
36
|
+
"fields": []
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"constructor": "GBP",
|
|
40
|
+
"native": [
|
|
41
|
+
"payments_domain",
|
|
42
|
+
"Pounds"
|
|
43
|
+
],
|
|
44
|
+
"style": "unit",
|
|
45
|
+
"fields": []
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "example.payments::type::Money",
|
|
51
|
+
"native": [
|
|
52
|
+
"payments_domain",
|
|
53
|
+
"Price"
|
|
54
|
+
],
|
|
55
|
+
"constructors": [
|
|
56
|
+
{
|
|
57
|
+
"constructor": "Money",
|
|
58
|
+
"native": [
|
|
59
|
+
"payments_domain",
|
|
60
|
+
"Price"
|
|
61
|
+
],
|
|
62
|
+
"style": "record",
|
|
63
|
+
"fields": [
|
|
64
|
+
{
|
|
65
|
+
"field": "amount",
|
|
66
|
+
"native": "major"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"field": "currency",
|
|
70
|
+
"native": "unit"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "example.payments::type::Payment",
|
|
78
|
+
"native": [
|
|
79
|
+
"payments_domain",
|
|
80
|
+
"PaymentStatus"
|
|
81
|
+
],
|
|
82
|
+
"constructors": [
|
|
83
|
+
{
|
|
84
|
+
"constructor": "Paid",
|
|
85
|
+
"native": [
|
|
86
|
+
"payments_domain",
|
|
87
|
+
"Settled"
|
|
88
|
+
],
|
|
89
|
+
"style": "variant",
|
|
90
|
+
"fields": [
|
|
91
|
+
{
|
|
92
|
+
"field": "value",
|
|
93
|
+
"native": "price"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"constructor": "Declined",
|
|
99
|
+
"native": [
|
|
100
|
+
"payments_domain",
|
|
101
|
+
"Rejected"
|
|
102
|
+
],
|
|
103
|
+
"style": "variant",
|
|
104
|
+
"fields": [
|
|
105
|
+
{
|
|
106
|
+
"field": "reason",
|
|
107
|
+
"native": "explanation"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"functions": [
|
|
115
|
+
{
|
|
116
|
+
"declaration": "example.payments::addFee",
|
|
117
|
+
"native": [
|
|
118
|
+
"payments_domain",
|
|
119
|
+
"apply_fee"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"declaration": "example.payments::roundTrip",
|
|
124
|
+
"native": [
|
|
125
|
+
"payments_domain",
|
|
126
|
+
"restore"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"declaration": "example.payments::archive",
|
|
131
|
+
"native": [
|
|
132
|
+
"payments_domain",
|
|
133
|
+
"store"
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"generators": [
|
|
138
|
+
{
|
|
139
|
+
"type": "example.payments::type::Money",
|
|
140
|
+
"factory": [
|
|
141
|
+
"lawspec_generators",
|
|
142
|
+
"prices"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Application-owned domain types; no generated domain declarations.
|
|
2
|
+
import * as ls from './lawspec_runtime.mjs';
|
|
3
|
+
export class CurrencyCode {
|
|
4
|
+
}
|
|
5
|
+
export class Dollars extends CurrencyCode {
|
|
6
|
+
}
|
|
7
|
+
export class Euros extends CurrencyCode {
|
|
8
|
+
}
|
|
9
|
+
export class Pounds extends CurrencyCode {
|
|
10
|
+
}
|
|
11
|
+
export class Price {
|
|
12
|
+
major;
|
|
13
|
+
unit;
|
|
14
|
+
constructor({ unit, major }) {
|
|
15
|
+
this.unit = unit;
|
|
16
|
+
this.major = major;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class PaymentStatus {
|
|
20
|
+
}
|
|
21
|
+
export class Settled extends PaymentStatus {
|
|
22
|
+
price;
|
|
23
|
+
constructor({ price }) {
|
|
24
|
+
super();
|
|
25
|
+
this.price = price;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export class Rejected extends PaymentStatus {
|
|
29
|
+
explanation;
|
|
30
|
+
constructor({ explanation }) {
|
|
31
|
+
super();
|
|
32
|
+
this.explanation = explanation;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function apply_fee(price) {
|
|
36
|
+
const major = ls.binary('+', price.major, new ls.Decimal(2n, -1n), 'Decimal', 'Decimal');
|
|
37
|
+
return new Price({ major, unit: price.unit });
|
|
38
|
+
}
|
|
39
|
+
export function restore(payment) {
|
|
40
|
+
return payment;
|
|
41
|
+
}
|
|
42
|
+
export function store(payments) {
|
|
43
|
+
return payments;
|
|
44
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import * as fc from 'fast-check';
|
|
2
|
+
import * as ls from '../src/lawspec_runtime.mjs';
|
|
3
|
+
import { Price, Euros } from '../src/payments_domain.mjs';
|
|
4
|
+
export function prices() {
|
|
5
|
+
return fc.integer({ min: 100, max: 200 }).map(cents => new Price({ major: new ls.Decimal(BigInt(cents), -2n), unit: new Euros() }));
|
|
6
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"sources": [
|
|
4
|
+
"laws"
|
|
5
|
+
],
|
|
6
|
+
"machineBits": 64,
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"language": "kotlin",
|
|
10
|
+
"root": ".",
|
|
11
|
+
"nativeBindings": {
|
|
12
|
+
"types": [
|
|
13
|
+
{
|
|
14
|
+
"type": "example.payments::type::Currency",
|
|
15
|
+
"native": [
|
|
16
|
+
"domain",
|
|
17
|
+
"PaymentsDomain",
|
|
18
|
+
"CurrencyCode"
|
|
19
|
+
],
|
|
20
|
+
"constructors": [
|
|
21
|
+
{
|
|
22
|
+
"constructor": "USD",
|
|
23
|
+
"native": [
|
|
24
|
+
"domain",
|
|
25
|
+
"PaymentsDomain",
|
|
26
|
+
"CurrencyCode",
|
|
27
|
+
"Dollars"
|
|
28
|
+
],
|
|
29
|
+
"style": "unit",
|
|
30
|
+
"fields": []
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"constructor": "EUR",
|
|
34
|
+
"native": [
|
|
35
|
+
"domain",
|
|
36
|
+
"PaymentsDomain",
|
|
37
|
+
"CurrencyCode",
|
|
38
|
+
"Euros"
|
|
39
|
+
],
|
|
40
|
+
"style": "unit",
|
|
41
|
+
"fields": []
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"constructor": "GBP",
|
|
45
|
+
"native": [
|
|
46
|
+
"domain",
|
|
47
|
+
"PaymentsDomain",
|
|
48
|
+
"CurrencyCode",
|
|
49
|
+
"Pounds"
|
|
50
|
+
],
|
|
51
|
+
"style": "unit",
|
|
52
|
+
"fields": []
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"type": "example.payments::type::Money",
|
|
58
|
+
"native": [
|
|
59
|
+
"domain",
|
|
60
|
+
"PaymentsDomain",
|
|
61
|
+
"Price"
|
|
62
|
+
],
|
|
63
|
+
"constructors": [
|
|
64
|
+
{
|
|
65
|
+
"constructor": "Money",
|
|
66
|
+
"native": [
|
|
67
|
+
"domain",
|
|
68
|
+
"PaymentsDomain",
|
|
69
|
+
"Price"
|
|
70
|
+
],
|
|
71
|
+
"style": "record",
|
|
72
|
+
"fields": [
|
|
73
|
+
{
|
|
74
|
+
"field": "amount",
|
|
75
|
+
"native": "major"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"field": "currency",
|
|
79
|
+
"native": "unit"
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"type": "example.payments::type::Payment",
|
|
87
|
+
"native": [
|
|
88
|
+
"domain",
|
|
89
|
+
"PaymentsDomain",
|
|
90
|
+
"PaymentStatus"
|
|
91
|
+
],
|
|
92
|
+
"constructors": [
|
|
93
|
+
{
|
|
94
|
+
"constructor": "Paid",
|
|
95
|
+
"native": [
|
|
96
|
+
"domain",
|
|
97
|
+
"PaymentsDomain",
|
|
98
|
+
"Settled"
|
|
99
|
+
],
|
|
100
|
+
"style": "variant",
|
|
101
|
+
"fields": [
|
|
102
|
+
{
|
|
103
|
+
"field": "value",
|
|
104
|
+
"native": "price"
|
|
105
|
+
}
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"constructor": "Declined",
|
|
110
|
+
"native": [
|
|
111
|
+
"domain",
|
|
112
|
+
"PaymentsDomain",
|
|
113
|
+
"Rejected"
|
|
114
|
+
],
|
|
115
|
+
"style": "variant",
|
|
116
|
+
"fields": [
|
|
117
|
+
{
|
|
118
|
+
"field": "reason",
|
|
119
|
+
"native": "explanation"
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
}
|
|
125
|
+
],
|
|
126
|
+
"functions": [
|
|
127
|
+
{
|
|
128
|
+
"declaration": "example.payments::addFee",
|
|
129
|
+
"native": [
|
|
130
|
+
"domain",
|
|
131
|
+
"PaymentsDomain",
|
|
132
|
+
"apply_fee"
|
|
133
|
+
]
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"declaration": "example.payments::roundTrip",
|
|
137
|
+
"native": [
|
|
138
|
+
"domain",
|
|
139
|
+
"PaymentsDomain",
|
|
140
|
+
"restore"
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"declaration": "example.payments::archive",
|
|
145
|
+
"native": [
|
|
146
|
+
"domain",
|
|
147
|
+
"PaymentsDomain",
|
|
148
|
+
"store"
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
],
|
|
152
|
+
"generators": [
|
|
153
|
+
{
|
|
154
|
+
"type": "example.payments::type::Money",
|
|
155
|
+
"factory": [
|
|
156
|
+
"domain",
|
|
157
|
+
"PaymentGenerators",
|
|
158
|
+
"prices"
|
|
159
|
+
]
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
package domain
|
|
2
|
+
|
|
3
|
+
import java.math.BigDecimal
|
|
4
|
+
import lawspec.runtime.LawSpecRuntime.Maybe
|
|
5
|
+
|
|
6
|
+
// Application-owned types; no generated LawSpec domain declarations.
|
|
7
|
+
object PaymentsDomain {
|
|
8
|
+
enum class CurrencyCode { Dollars, Euros, Pounds }
|
|
9
|
+
data class Price(val major: BigDecimal, val unit: CurrencyCode)
|
|
10
|
+
sealed interface PaymentStatus
|
|
11
|
+
data class Settled(val price: Price) : PaymentStatus
|
|
12
|
+
data class Rejected(val explanation: String) : PaymentStatus
|
|
13
|
+
|
|
14
|
+
fun apply_fee(price: Price) = Price(price.major + BigDecimal("0.2"), price.unit)
|
|
15
|
+
fun restore(payment: PaymentStatus) = payment
|
|
16
|
+
fun store(payments: List<Maybe<PaymentStatus>>) = payments
|
|
17
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
package domain
|
|
2
|
+
|
|
3
|
+
import io.kotest.property.Arb
|
|
4
|
+
import io.kotest.property.arbitrary.int
|
|
5
|
+
import io.kotest.property.arbitrary.map
|
|
6
|
+
import java.math.BigDecimal
|
|
7
|
+
|
|
8
|
+
object PaymentGenerators {
|
|
9
|
+
fun prices(): Arb<PaymentsDomain.Price> = Arb.int(100..200).map { cents ->
|
|
10
|
+
PaymentsDomain.Price(BigDecimal.valueOf(cents.toLong(), 2), PaymentsDomain.CurrencyCode.Euros)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"sources": [
|
|
4
|
+
"laws"
|
|
5
|
+
],
|
|
6
|
+
"machineBits": 64,
|
|
7
|
+
"targets": [
|
|
8
|
+
{
|
|
9
|
+
"language": "python",
|
|
10
|
+
"root": ".",
|
|
11
|
+
"nativeBindings": {
|
|
12
|
+
"types": [
|
|
13
|
+
{
|
|
14
|
+
"type": "example.payments::type::Currency",
|
|
15
|
+
"native": [
|
|
16
|
+
"payments_domain",
|
|
17
|
+
"CurrencyCode"
|
|
18
|
+
],
|
|
19
|
+
"constructors": [
|
|
20
|
+
{
|
|
21
|
+
"constructor": "USD",
|
|
22
|
+
"native": [
|
|
23
|
+
"payments_domain",
|
|
24
|
+
"Dollars"
|
|
25
|
+
],
|
|
26
|
+
"style": "unit",
|
|
27
|
+
"fields": []
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"constructor": "EUR",
|
|
31
|
+
"native": [
|
|
32
|
+
"payments_domain",
|
|
33
|
+
"Euros"
|
|
34
|
+
],
|
|
35
|
+
"style": "unit",
|
|
36
|
+
"fields": []
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"constructor": "GBP",
|
|
40
|
+
"native": [
|
|
41
|
+
"payments_domain",
|
|
42
|
+
"Pounds"
|
|
43
|
+
],
|
|
44
|
+
"style": "unit",
|
|
45
|
+
"fields": []
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"type": "example.payments::type::Money",
|
|
51
|
+
"native": [
|
|
52
|
+
"payments_domain",
|
|
53
|
+
"Price"
|
|
54
|
+
],
|
|
55
|
+
"constructors": [
|
|
56
|
+
{
|
|
57
|
+
"constructor": "Money",
|
|
58
|
+
"native": [
|
|
59
|
+
"payments_domain",
|
|
60
|
+
"Price"
|
|
61
|
+
],
|
|
62
|
+
"style": "record",
|
|
63
|
+
"fields": [
|
|
64
|
+
{
|
|
65
|
+
"field": "amount",
|
|
66
|
+
"native": "major"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"field": "currency",
|
|
70
|
+
"native": "unit"
|
|
71
|
+
}
|
|
72
|
+
]
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"type": "example.payments::type::Payment",
|
|
78
|
+
"native": [
|
|
79
|
+
"payments_domain",
|
|
80
|
+
"PaymentStatus"
|
|
81
|
+
],
|
|
82
|
+
"constructors": [
|
|
83
|
+
{
|
|
84
|
+
"constructor": "Paid",
|
|
85
|
+
"native": [
|
|
86
|
+
"payments_domain",
|
|
87
|
+
"Settled"
|
|
88
|
+
],
|
|
89
|
+
"style": "variant",
|
|
90
|
+
"fields": [
|
|
91
|
+
{
|
|
92
|
+
"field": "value",
|
|
93
|
+
"native": "price"
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"constructor": "Declined",
|
|
99
|
+
"native": [
|
|
100
|
+
"payments_domain",
|
|
101
|
+
"Rejected"
|
|
102
|
+
],
|
|
103
|
+
"style": "variant",
|
|
104
|
+
"fields": [
|
|
105
|
+
{
|
|
106
|
+
"field": "reason",
|
|
107
|
+
"native": "explanation"
|
|
108
|
+
}
|
|
109
|
+
]
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"functions": [
|
|
115
|
+
{
|
|
116
|
+
"declaration": "example.payments::addFee",
|
|
117
|
+
"native": [
|
|
118
|
+
"payments_domain",
|
|
119
|
+
"apply_fee"
|
|
120
|
+
]
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
"declaration": "example.payments::roundTrip",
|
|
124
|
+
"native": [
|
|
125
|
+
"payments_domain",
|
|
126
|
+
"restore"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"declaration": "example.payments::archive",
|
|
131
|
+
"native": [
|
|
132
|
+
"payments_domain",
|
|
133
|
+
"store"
|
|
134
|
+
]
|
|
135
|
+
}
|
|
136
|
+
],
|
|
137
|
+
"generators": [
|
|
138
|
+
{
|
|
139
|
+
"type": "example.payments::type::Money",
|
|
140
|
+
"factory": [
|
|
141
|
+
"lawspec_generators",
|
|
142
|
+
"prices"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Application-owned payment types; no generated domain declarations."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from decimal import Decimal
|
|
5
|
+
from fractions import Fraction
|
|
6
|
+
|
|
7
|
+
import lawspec_runtime as ls
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CurrencyCode:
|
|
11
|
+
pass
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class Dollars(CurrencyCode):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True)
|
|
20
|
+
class Euros(CurrencyCode):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class Pounds(CurrencyCode):
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass(frozen=True, kw_only=True)
|
|
30
|
+
class Price:
|
|
31
|
+
# Order differs deliberately; bridges must use the mapped field names.
|
|
32
|
+
unit: CurrencyCode
|
|
33
|
+
major: Decimal
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PaymentStatus:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True, kw_only=True)
|
|
41
|
+
class Settled(PaymentStatus):
|
|
42
|
+
price: Price
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, kw_only=True)
|
|
46
|
+
class Rejected(PaymentStatus):
|
|
47
|
+
explanation: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def apply_fee(price):
|
|
51
|
+
amount = ls.finite_decimal(Fraction(price.major) + Fraction(1, 5))
|
|
52
|
+
return Price(major=amount, unit=price.unit)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def restore(payment):
|
|
56
|
+
return payment
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def store(payments):
|
|
60
|
+
return payments
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Application strategies retain ordinary Hypothesis shrinking."""
|
|
2
|
+
|
|
3
|
+
from decimal import Decimal
|
|
4
|
+
|
|
5
|
+
from hypothesis import strategies as st
|
|
6
|
+
|
|
7
|
+
from payments_domain import Euros, Price
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def prices():
|
|
11
|
+
def price(cents):
|
|
12
|
+
digits = tuple(map(int, str(cents)))
|
|
13
|
+
return Price(major=Decimal((0, digits, -2)), unit=Euros())
|
|
14
|
+
|
|
15
|
+
return st.integers(min_value=100, max_value=200).map(price)
|