aboutsummaryrefslogtreecommitdiff
path: root/montis/src/Montis/Core/State/Marshal.hs
blob: 04a2a576ed37e47dc5f8e267de48c1487ef25c02 (plain) (blame)
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
module Montis.Core.State.Marshal (marshalState, demarshalState) where

import Data.Map qualified as M
import Data.Maybe (mapMaybe)
import Montis.Core.Extensions
import Montis.Core.State

data MarshalledState where
  MarshalledState ::
    [(String, String)] ->
    MarshalledState
  deriving (Show, Read)

-- | Marshals the serializable parts of the state to a string. This happens
-- during a hot-reload where some state must be saved to persist across hot
-- reloads.
-- Only the extensible state is persisted; hooks and other runtime data are
-- reconstructed from the config on restart.
marshalState :: State m -> String
marshalState
  ( State
      { extensibleState = xs
      }
    ) =
    show $
      MarshalledState
        (mapMaybe (\(k, v) -> (extensionKeyValue k,) <$> doMarshalEx v) (M.toList xs))
    where
      -- Left values are already marshalled; Right values are re-encoded here.
      doMarshalEx :: Either String (Extension StateExtension) -> Maybe String
      doMarshalEx (Left s) = Just s
      doMarshalEx (Right (Extension a)) = marshalExtension a

-- | Demarshals the string from "marshalState" into a state. Uses the provided
-- config to fill out non-persistent parts of the state.
-- The extensible map is rehydrated as marshalled strings so decoding can be
-- deferred until a specific extension is requested.
demarshalState :: Config m -> String -> State m
demarshalState Config {startingHooks = hooks} str =
  State hooks xs
  where
    ( MarshalledState
        (M.mapKeys ExtensionKey . fmap Left . M.fromList -> xs)
      ) = read str