tinytools-0.1.0.5: src/Potato/Flow/Serialization/SnakeWrangler.hs
{-# LANGUAGE RecordWildCards #-}
module Potato.Flow.Serialization.SnakeWrangler where
import Relude
import Potato.Flow.Types
import Potato.Flow.Controller.Types
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Encode.Pretty as PrettyAeson
import qualified Data.Binary as Binary
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Text.Encoding as Text
-- | list of all version supported
versions :: [Int]
versions = [1]
-- | version of the current build
currentVersion :: Int
currentVersion = 1
data Snake = Snake {
_snake_version :: Int
-- string instead of enum type to ensure compatibility if new formats are ever added
-- currently supports "binary" and "json"
, _snake_format :: String
, _snake_data :: Text
} deriving (Eq, Generic, Show)
instance Aeson.FromJSON Snake
instance Aeson.ToJSON Snake
instance Binary.Binary Snake
instance NFData Snake
data SnakeFormat = SF_Json | SF_Binary
serialize :: SnakeFormat -> (SPotatoFlow, ControllerMeta) -> LBS.ByteString
serialize f x = r where
(inner, format) = case f of
SF_Json -> (PrettyAeson.encodePretty x, "json")
SF_Binary -> (Binary.encode x, "binary")
outer = Snake {
_snake_version = currentVersion
, _snake_format = format
, _snake_data = Text.decodeUtf8 (LBS.toStrict inner)
}
r = PrettyAeson.encodePretty outer
deserialize :: LBS.ByteString -> Either String (SPotatoFlow, ControllerMeta)
deserialize lbs = case Aeson.eitherDecode lbs of
Left e -> Left $ "failed to decode Snake with error " <> e
Right vso -> deserialize_internal vso
deserialize_internal :: Snake -> Either String (SPotatoFlow, ControllerMeta)
deserialize_internal Snake {..} = do
if _snake_version /= currentVersion
then Left $ "version mismatch, got: " <> show _snake_version <> " expected: " <> show currentVersion
else return ()
case _snake_format of
"json" -> Aeson.eitherDecodeStrict (Text.encodeUtf8 _snake_data)
"binary" -> case Binary.decode (LBS.fromStrict $ Text.encodeUtf8 _snake_data) of
Just x -> Right x
Nothing -> Left "failed to decode binary"
x -> Left $ "unrecognized fromat" <> show x
decodeFile :: FilePath -> IO (Either String (SPotatoFlow, ControllerMeta))
decodeFile fp = do
vsobs <- LBS.readFile fp
return $ deserialize vsobs
decodeFileMaybe :: FilePath -> IO (Maybe (SPotatoFlow, ControllerMeta))
decodeFileMaybe fp = do
x <- decodeFile fp
case x of
Left _ -> return Nothing
Right x -> return (Just x)