{-# LANGUAGE RankNTypes #-}

module Unison.Codebase.Serialization where

import Data.ByteString (ByteString, readFile, writeFile)
import Data.Bytes.Get (MonadGet, runGetS)
import Data.Bytes.Put (MonadPut, runPutS)
import System.FilePath (takeDirectory)
import UnliftIO (MonadIO, liftIO)
import UnliftIO.Directory (createDirectoryIfMissing, doesFileExist)
import Prelude hiding (readFile, writeFile)

type Get a = forall m. (MonadGet m) => m a

type Put a = forall m. (MonadPut m) => a -> m ()

-- todo: do we use this?
data Format a = Format
  { forall a. Format a -> Get a
get :: Get a,
    forall a. Format a -> Put a
put :: Put a
  }

getFromBytes :: Get a -> ByteString -> Maybe a
getFromBytes :: forall a. Get a -> ByteString -> Maybe a
getFromBytes Get a
getA ByteString
bytes =
  case Get a -> ByteString -> Either String a
forall a. Get a -> ByteString -> Either String a
runGetS Get a
Get a
getA ByteString
bytes of Left String
_ -> Maybe a
forall a. Maybe a
Nothing; Right a
a -> a -> Maybe a
forall a. a -> Maybe a
Just a
a

getFromFile :: (MonadIO m) => Get a -> FilePath -> m (Maybe a)
getFromFile :: forall (m :: * -> *) a. MonadIO m => Get a -> String -> m (Maybe a)
getFromFile Get a
getA String
file = do
  Bool
b <- String -> m Bool
forall (m :: * -> *). MonadIO m => String -> m Bool
doesFileExist String
file
  if Bool
b then Get a -> ByteString -> Maybe a
forall a. Get a -> ByteString -> Maybe a
getFromBytes m a
Get a
getA (ByteString -> Maybe a) -> m ByteString -> m (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString -> m ByteString
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO ByteString
readFile String
file) else Maybe a -> m (Maybe a)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing

getFromFile' :: (MonadIO m) => Get a -> FilePath -> m (Either String a)
getFromFile' :: forall (m :: * -> *) a.
MonadIO m =>
Get a -> String -> m (Either String a)
getFromFile' Get a
getA String
file = do
  Bool
b <- String -> m Bool
forall (m :: * -> *). MonadIO m => String -> m Bool
doesFileExist String
file
  if Bool
b
    then Get a -> ByteString -> Either String a
forall a. Get a -> ByteString -> Either String a
runGetS Get a
Get a
getA (ByteString -> Either String a)
-> m ByteString -> m (Either String a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO ByteString -> m ByteString
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (String -> IO ByteString
readFile String
file)
    else Either String a -> m (Either String a)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String a -> m (Either String a))
-> (String -> Either String a) -> String -> m (Either String a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Either String a
forall a b. a -> Either a b
Left (String -> m (Either String a)) -> String -> m (Either String a)
forall a b. (a -> b) -> a -> b
$ String
"No such file: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
file

putBytes :: Put a -> a -> ByteString
putBytes :: forall a. Put a -> a -> ByteString
putBytes Put a
put a
a = Put -> ByteString
runPutS (a -> Put
Put a
put a
a)

putWithParentDirs :: (MonadIO m) => Put a -> FilePath -> a -> m ()
putWithParentDirs :: forall (m :: * -> *) a. MonadIO m => Put a -> String -> a -> m ()
putWithParentDirs Put a
putA String
file a
a = do
  Bool -> String -> m ()
forall (m :: * -> *). MonadIO m => Bool -> String -> m ()
createDirectoryIfMissing Bool
True (String -> String
takeDirectory String
file)
  IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO (IO () -> m ()) -> (ByteString -> IO ()) -> ByteString -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString -> IO ()
writeFile String
file (ByteString -> m ()) -> ByteString -> m ()
forall a b. (a -> b) -> a -> b
$ Put a -> a -> ByteString
forall a. Put a -> a -> ByteString
putBytes a -> m ()
Put a
putA a
a