| 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
|