-
1
-
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
-
10
-
11
-
12
-
13
-
14
-
15
-
16
-
17
-
18
-
19
-
20
-
21
-
22
-
23
-
24
-
25
-
26
-
27
-
28
-
29
-
30
-
31
-
32
-
33
-
34
-
35
-
36
-
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
-
46
-
47
-
48
-
49
-
50
-
51
-
52
-
53
-
54
-
55
-
56
-
57
-
58
-
59
-
60
-
61
-
62
-
63
-
64
-
65
-
66
-
67
-
68
-
69
-
70
-
71
-
72
-
73
-
74
-
75
-
76
-
77
-
78
-
79
-
80
-
81
-
82
-
83
-
84
-
85
-
86
-
87
-
88
-
89
-
90
-
91
-
92
-
93
-
94
-
95
-
96
-
97
-
98
-
99
-
100
-
101
-
102
-
103
-
104
-
105
-
106
-
107
-
108
-
109
-
110
-
111
-
112
-
113
-
114
-
115
-
116
-
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
-
132
-
133
-
134
-
135
-
136
-
137
-
138
-
139
-
140
-
141
-
142
-
143
-
144
-
145
-
146
-
147
-
148
-
149
-
150
-
151
-
152
-
153
-
154
-
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
-
163
-
164
-
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
178
-
179
-
180
-
181
-
182
-
183
-
184
-
185
-
186
-
187
-
188
-
189
-
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
203
-
204
-
205
-
206
-
207
-
208
-
209
-
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
-
218
-
219
-
220
-
221
-
222
-
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
-
231
-
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
-
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
-
266
-
267
-
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
-
294
-
295
-
296
-
297
-
298
-
299
-
300
-
301
-
302
-
303
-
304
-
305
-
306
-
307
-
308
-
309
-
310
-
311
-
312
-
313
-
314
-
315
-
316
-
317
-
318
-
319
-
320
-
321
-
322
-
323
-
324
-
325
-
326
-
327
-
328
-
329
-
330
-
331
-
332
-
333
-
334
-
335
-
336
-
337
-
338
-
339
-
340
-
341
-
342
-
343
-
344
-
345
-
346
-
347
-
348
-
349
-
350
-
351
-
352
-
353
-
354
-
355
-
356
-
357
-
358
-
359
-
360
-
361
-
362
-
363
-
364
-
365
-
366
-
367
-
368
-
369
-
370
-
371
-
372
-
373
-
374
-
375
-
376
-
377
-
378
-
379
-
380
-
381
-
382
-
383
-
384
-
385
-
386
-
387
-
388
-
389
-
390
-
391
-
392
-
393
-
394
-
395
-
396
-
397
-
398
-
399
-
400
-
401
-
402
-
403
-
404
-
405
-
406
-
407
-
408
-
409
-
410
-
411
-
412
-
413
-
414
-
415
-
416
-
417
-
418
-
419
-
420
-
421
-
422
-
423
-
424
-
425
-- Copyright 2026 Shota FUJI
--
-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.
--
-- SPDX-License-Identifier: MPL-2.0
module Parameters.Parser exposing (Error(..), Errors, parse)
import Dict exposing (Dict)
import Length exposing (Length, mm, toMM)
import Parameters exposing (ColorSchema(..), LoopStyle(..), Parameters, ParametersDict, getKey, hasKey)
import Parameters.Constraints as Constraints exposing (constraints)
import Parameters.Key as Key exposing (Key(..))
type Error
= MissingValue
| BelowMin Float
| AboveMax Float
| NotALength
| NotAnInt
| NonexistentVariant String
| NotABool
| UnsupportedParametersVersion String
type alias Errors =
Dict String Error
mkErrors : List ( Key, Maybe Error ) -> Errors
mkErrors list =
list
|> List.filterMap (\( key, error ) -> Maybe.map (\e -> ( Key.toString key, e )) error)
|> Dict.fromList
getError : Result a b -> Maybe a
getError r =
case r of
Err e ->
Just e
_ ->
Nothing
parseInt : Constraints.NumberConstraints Int -> String -> Result Error Int
parseInt constraints text =
case text of
"" ->
Err MissingValue
_ ->
let
min =
constraints.min
|> Maybe.map
(\m ->
Result.andThen
(\x ->
if x >= m then
Ok x
else
Err (BelowMin (toFloat m))
)
)
|> Maybe.withDefault identity
max =
constraints.max
|> Maybe.map
(\m ->
Result.andThen
(\x ->
if x <= m then
Ok x
else
Err (AboveMax (toFloat m))
)
)
|> Maybe.withDefault identity
in
String.toInt text
|> Result.fromMaybe NotAnInt
|> min
|> max
parseLength : Constraints.NumberConstraints Length -> String -> Result Error Length
parseLength constraints text =
case text of
"" ->
Err MissingValue
_ ->
let
min =
constraints.min
|> Maybe.map toMM
|> Maybe.map
(\m ->
Result.andThen
(\x ->
if toMM x >= m then
Ok x
else
Err (BelowMin m)
)
)
|> Maybe.withDefault identity
max =
constraints.max
|> Maybe.map toMM
|> Maybe.map
(\m ->
Result.andThen
(\x ->
if toMM x <= m then
Ok x
else
Err (AboveMax m)
)
)
|> Maybe.withDefault identity
in
String.toFloat text
|> Maybe.map mm
|> Result.fromMaybe NotALength
|> min
|> max
parseColorSchema : String -> Result Error ColorSchema
parseColorSchema text =
case text of
"white-on-black" ->
Ok WhiteOnBlack
"black-on-white" ->
Ok BlackOnWhite
_ ->
Err (NonexistentVariant text)
parseLoopStyle : String -> Result Error LoopStyle
parseLoopStyle text =
case text of
"simple" ->
Ok Simple
"folded" ->
Ok Folded
_ ->
Err (NonexistentVariant text)
parseField : Key -> (String -> Result Error a) -> ParametersDict -> Result Error a
parseField key f fields =
getKey key fields
|> Maybe.withDefault ""
|> f
parseFieldWithDefault : Key -> a -> (String -> Result Error a) -> ParametersDict -> Result Error a
parseFieldWithDefault key default f fields =
getKey key fields
|> Maybe.map f
|> Maybe.withDefault (Ok default)
parseFixedLoop : ParametersDict -> Result Errors (Maybe Parameters.FixedLoop)
parseFixedLoop fields =
if hasKey HasFixedLoop fields then
case
( parseField FixedLoopWidth (parseLength constraints.shortPiece.loops.fixed.width) fields
, parseField FixedLoopPlay (parseLength constraints.shortPiece.loops.fixed.play) fields
)
of
( Ok width, Ok play ) ->
let
base =
Parameters.defaultFixedLoop
in
Ok (Just { base | width = width, play = play })
( width, play ) ->
Err
(mkErrors
[ ( FixedLoopWidth, getError width )
, ( FixedLoopPlay, getError play )
]
)
else
Ok Nothing
parseFreeLoop : ParametersDict -> Result Errors (Maybe Parameters.FreeLoop)
parseFreeLoop fields =
if hasKey HasFreeLoop fields then
case
( parseField FreeLoopWidth (parseLength constraints.shortPiece.loops.free.width) fields
, parseField FreeLoopPlay (parseLength constraints.shortPiece.loops.free.play) fields
, parseField FreeLoopOverlap (parseLength constraints.shortPiece.loops.free.overlap) fields
)
of
( Ok width, Ok play, Ok overlap ) ->
Ok (Just { width = width, play = play, overlap = overlap })
( width, play, overlap ) ->
Err
(mkErrors
[ ( FreeLoopWidth, getError width )
, ( FreeLoopPlay, getError play )
, ( FreeLoopOverlap, getError overlap )
]
)
else
Ok Nothing
parseLoops : ParametersDict -> Result Errors Parameters.Loops
parseLoops fields =
case
( ( parseFixedLoop fields, parseFreeLoop fields )
, ( parseField LoopStyle parseLoopStyle fields
, parseField LoopThickness (parseLength constraints.shortPiece.loops.thickness) fields
)
)
of
( ( Ok fixed, Ok free ), ( Ok style, Ok thickness ) ) ->
Ok { fixed = fixed, free = free, style = style, thickness = thickness }
( ( fixed, free ), ( style, thickness ) ) ->
[ getError fixed |> Maybe.withDefault Dict.empty
, getError free |> Maybe.withDefault Dict.empty
, mkErrors [ ( LoopStyle, getError style ), ( LoopThickness, getError thickness ) ]
]
|> List.map Dict.toList
|> List.concat
|> Dict.fromList
|> Err
parseBuckle : ParametersDict -> Result Errors Parameters.Buckle
parseBuckle fields =
case
parseField BuckleSpringBarDiameter (parseLength constraints.shortPiece.buckle.springBarDiameter) fields
of
Ok springBarDiameter ->
Ok { springBarDiameter = springBarDiameter }
Err err ->
Err (mkErrors [ ( BuckleSpringBarDiameter, Just err ) ])
parseShortPiece : ParametersDict -> Result Errors Parameters.ShortPiece
parseShortPiece fields =
case
( parseLoops fields
, parseField ShortPieceLength (parseLength constraints.shortPiece.length) fields
, parseBuckle fields
)
of
( Ok loops, Ok length, Ok buckle ) ->
let
base =
Parameters.default.shortPiece
in
Ok { base | loops = loops, length = length, buckle = buckle }
( loops, length, buckle ) ->
[ getError loops |> Maybe.withDefault Dict.empty
, mkErrors [ ( ShortPieceLength, getError length ) ]
, getError buckle |> Maybe.withDefault Dict.empty
]
|> List.map Dict.toList
|> List.concat
|> Dict.fromList
|> Err
parseBuckleHole : ParametersDict -> Result Errors Parameters.BuckleHole
parseBuckleHole fields =
case
( ( parseField BuckleHoleCount (parseInt constraints.longPiece.buckleHole.count) fields
, parseField BuckleHoleDiameter (parseLength constraints.longPiece.buckleHole.diameter) fields
)
, ( parseField BuckleHoleOffset (parseLength constraints.longPiece.buckleHole.offset) fields
, parseField BuckleHoleInterval (parseLength constraints.longPiece.buckleHole.interval) fields
)
)
of
( ( Ok 0, _ ), _ ) ->
let
default =
Parameters.default.longPiece.buckleHole
in
Ok { default | count = 0 }
( ( Ok count, Ok diameter ), ( Ok offset, Ok interval ) ) ->
Ok { count = count, diameter = diameter, offset = offset, interval = interval }
( ( count, diameter ), ( offset, interval ) ) ->
Err
(mkErrors
[ ( BuckleHoleCount, getError count )
, ( BuckleHoleDiameter, getError diameter )
, ( BuckleHoleOffset, getError offset )
, ( BuckleHoleInterval, getError interval )
]
)
parseLongPiece : ParametersDict -> Result Errors Parameters.LongPiece
parseLongPiece fields =
case ( parseBuckleHole fields, parseField LongPieceLength (parseLength constraints.longPiece.length) fields ) of
( Ok buckleHole, Ok length ) ->
let
base =
Parameters.default.longPiece
in
Ok { base | buckleHole = buckleHole, length = length }
( buckleHole, length ) ->
Err
(Dict.union
(getError buckleHole |> Maybe.withDefault Dict.empty)
(mkErrors [ ( LongPieceLength, getError length ) ])
)
parseRendering : ParametersDict -> Result Errors Parameters.Rendering
parseRendering fields =
case
( ( parseField CanvasMargin (parseLength constraints.rendering.margin) fields
, parseField LineWidth (parseLength constraints.rendering.lineWidth) fields
, parseField ColorSchema parseColorSchema fields
)
, hasKey QRCode fields
)
of
( ( Ok margin, Ok lineWidth, Ok colorSchema ), qrCode ) ->
let
base =
Parameters.default.rendering
in
Ok
{ base
| margin = margin
, lineWidth = lineWidth
, colorSchema = colorSchema
, qrCode = qrCode
}
( ( margin, lineWidth, colorSchema ), _ ) ->
Err
(mkErrors
[ ( CanvasMargin, getError margin )
, ( LineWidth, getError lineWidth )
, ( ColorSchema, getError colorSchema )
]
)
parse : ParametersDict -> Result Errors Parameters
parse fields =
case getKey Version fields of
Just "1.0" ->
case
( ( parseField LugWidth (parseLength constraints.lugWidth) fields
, parseField LiningThickness (parseLength constraints.lining) fields
, parseField SurfaceThickness (parseLength constraints.thickness) fields
)
, ( parseLongPiece fields
, parseShortPiece fields
, parseFieldWithDefault Taper (mm 0) (parseLength constraints.taper) fields
)
, parseRendering fields
)
of
( ( Ok lugWidth, Ok lining, Ok thickness ), ( Ok longPiece, Ok shortPiece, Ok taper ), Ok rendering ) ->
Ok
{ lugWidth = lugWidth
, lining = lining
, thickness = thickness
, longPiece = longPiece
, shortPiece = shortPiece
, rendering = rendering
, taper = taper
}
( ( lugWidth, lining, thickness ), ( longPiece, shortPiece, taper ), rendering ) ->
[ mkErrors
[ ( LugWidth, getError lugWidth )
, ( LiningThickness, getError lining )
, ( SurfaceThickness, getError thickness )
, ( Taper, getError taper )
]
, getError longPiece |> Maybe.withDefault Dict.empty
, getError shortPiece |> Maybe.withDefault Dict.empty
, getError rendering |> Maybe.withDefault Dict.empty
]
|> List.map Dict.toList
|> List.concat
|> Dict.fromList
|> Err
Just str ->
Err (mkErrors [ ( Version, Just (UnsupportedParametersVersion str) ) ])
Nothing ->
Err (mkErrors [ ( Version, Just MissingValue ) ])