24 | 24 |
-- * Section-Level Parsing
|
25 | 25 |
-- $sections
|
26 | 26 |
, section
|
27 | |
, sectionOpt
|
28 | 27 |
-- * Field-Level Parsing
|
29 | 28 |
-- $fields
|
30 | 29 |
, FieldDescription
|
|
33 | 32 |
, field
|
34 | 33 |
, flag
|
35 | 34 |
, comment
|
36 | |
, defaultValue
|
37 | 35 |
, placeholderValue
|
38 | 36 |
, skipIfMissing
|
39 | 37 |
-- * FieldValues
|
|
139 | 137 |
section :: Text -> SectionSpec s () -> IniSpec s ()
|
140 | 138 |
section name (SectionSpec mote) = IniSpec $ do
|
141 | 139 |
let fields = runBidirM mote
|
142 | |
modify (Seq.|> Section name fields False)
|
143 | |
|
144 | |
-- | Define the specification of an optional top-level INI section. If
|
145 | |
-- this section does not appear in a parsed INI file, then it will be
|
146 | |
-- skipped.
|
147 | |
sectionOpt :: Text -> SectionSpec s () -> IniSpec s ()
|
148 | |
sectionOpt name (SectionSpec mote) = IniSpec $ do
|
149 | |
let fields = runBidirM mote
|
150 | |
modify (Seq.|> Section name fields True)
|
| 140 |
modify (Seq.|> Section name fields (allOptional fields))
|
| 141 |
|
| 142 |
allOptional :: (Seq (Field s)) -> Bool
|
| 143 |
allOptional = all isOptional
|
| 144 |
where isOptional (Field _ fd) = fdSkipIfMissing fd
|
| 145 |
isOptional (FieldMb _ fd) = fdSkipIfMissing fd
|
151 | 146 |
|
152 | 147 |
data Section s = Section Text (Seq (Field s)) Bool
|
153 | 148 |
|
|
173 | 168 |
data FieldDescription t = FieldDescription
|
174 | 169 |
{ fdName :: Text
|
175 | 170 |
, fdValue :: FieldValue t
|
176 | |
, fdDefault :: Maybe t
|
177 | 171 |
, fdComment :: Seq Text
|
178 | 172 |
, fdDummy :: Maybe Text
|
179 | 173 |
, fdSkipIfMissing :: Bool
|
|
210 | 204 |
-}
|
211 | 205 |
comment :: [Text] -> FieldDescription t -> FieldDescription t
|
212 | 206 |
comment cmt fd = fd { fdComment = Seq.fromList cmt }
|
213 | |
|
214 | |
{- |
|
215 | |
Choose a default value to be used in case of a missing value. This will
|
216 | |
only be used in the case of non-optional fields.
|
217 | |
-}
|
218 | |
defaultValue :: t -> FieldDescription t -> FieldDescription t
|
219 | |
defaultValue def fd = fd { fdDefault = Just def }
|
220 | 207 |
|
221 | 208 |
-- | Choose a placeholder value to be displayed for optional fields.
|
222 | 209 |
-- This is used when serializing an optional Ini field: the
|
|
254 | 241 |
field name value = FieldDescription
|
255 | 242 |
{ fdName = name
|
256 | 243 |
, fdValue = value
|
257 | |
, fdDefault = Nothing
|
258 | 244 |
, fdComment = Seq.empty
|
259 | 245 |
, fdDummy = Nothing
|
260 | 246 |
, fdSkipIfMissing = False
|
|
392 | 378 |
runFields (set l value s) (Seq.viewl fs) sect
|
393 | 379 |
| fdSkipIfMissing descr =
|
394 | 380 |
runFields s (Seq.viewl fs) sect
|
395 | |
| Just def <- fdDefault descr =
|
396 | |
runFields (set l def s) (Seq.viewl fs) sect
|
397 | 381 |
| otherwise = Left ("Unable to find field " ++ show (fdName descr))
|
398 | 382 |
runFields s (FieldMb l descr Seq.:< fs) sect
|
399 | 383 |
| Just v <- lkp (fdName descr) (isVals sect) = do
|
|
435 | 419 |
mkIniValue (fvEmit (fdValue descr) (get l s)) descr False
|
436 | 420 |
toVal (FieldMb l descr) =
|
437 | 421 |
case get l s of
|
438 | |
Nothing
|
439 | |
| Just d <- fdDefault descr ->
|
440 | |
mkIniValue (fvEmit (fdValue descr) d) descr True
|
441 | |
| otherwise ->
|
442 | |
mkIniValue "" descr True
|
| 422 |
Nothing ->
|
| 423 |
mkIniValue "" descr True
|
443 | 424 |
Just v ->
|
444 | 425 |
mkIniValue (fvEmit (fdValue descr) v) descr True
|
445 | 426 |
|
|
511 | 492 |
-> Seq (Section s)
|
512 | 493 |
-> UpdatePolicy
|
513 | 494 |
-> Either String (Seq (Text, IniSection))
|
514 | |
updateIniSections s sections fields pol =
|
515 | |
F.for sections $ \ (name, sec) -> do
|
| 495 |
updateIniSections s sections fields pol = do
|
| 496 |
existingSections <- F.for sections $ \ (name, sec) -> do
|
516 | 497 |
let err = (Left ("Unexpected top-level section: " ++ show name))
|
517 | 498 |
Section _ spec _ <- maybe err Right
|
518 | 499 |
(F.find (\ (Section n _ _) -> T.toLower n == name) fields)
|
519 | 500 |
newVals <- updateIniSection s (isVals sec) spec pol
|
520 | 501 |
return (name, sec { isVals = newVals })
|
| 502 |
let existingSectionNames = fmap fst existingSections
|
| 503 |
newSections <- F.for fields $
|
| 504 |
\ (Section nm spec isOpt) ->
|
| 505 |
if nm `elem` existingSectionNames
|
| 506 |
then return mempty
|
| 507 |
else return mempty
|
| 508 |
return (existingSections <> F.asum newSections)
|
521 | 509 |
|
522 | 510 |
updateIniSection :: s -> Seq (Text, IniValue) -> Seq (Field s)
|
523 | 511 |
-> UpdatePolicy -> Either String (Seq (Text, IniValue))
|
|
580 | 568 |
-- were left out, but if we have any non-optional fields left
|
581 | 569 |
-- over, then we definitely need to include them.
|
582 | 570 |
go EmptyL fs = return (finish (Seq.viewl fs))
|
583 | |
finish (f@(Field l descr) :< fs)
|
584 | |
| or [ updateAddOptionalFields pol
|
585 | |
, fdDefault descr /= Just (get l s)
|
586 | |
]
|
| 571 |
finish (f@(Field l _) :< fs)
|
| 572 |
| updateAddOptionalFields pol
|
587 | 573 |
, Just val <- mkValue (fieldName f) f '=' =
|
588 | 574 |
(fieldName f, val) <| finish (Seq.viewl fs)
|
589 | 575 |
| otherwise = finish (Seq.viewl fs)
|
590 | 576 |
finish (f@(FieldMb _ descr) :< fs)
|
591 | |
| not (fdSkipIfMissing descr) && fdDefault descr == Nothing
|
| 577 |
| not (fdSkipIfMissing descr)
|
592 | 578 |
, Just val <- mkValue (fieldName f) f '=' =
|
593 | 579 |
(fieldName f, val) <| finish (Seq.viewl fs)
|
594 | 580 |
| updateAddOptionalFields pol
|