From 6c5f0b513c980544fd6784a1fb5a2af42f97c57a Mon Sep 17 00:00:00 2001 From: Alexander Kobjolke Date: Tue, 31 Oct 2023 13:28:08 +0100 Subject: [PATCH] lib: Make generic FilePack of any encodable type --- src/FilePack/Encode.hs | 6 +++ src/FilePack/FileData.hs | 81 +++++++++++++++++++++++++--------------- 2 files changed, 56 insertions(+), 31 deletions(-) diff --git a/src/FilePack/Encode.hs b/src/FilePack/Encode.hs index e71e4f7..c2e54e4 100644 --- a/src/FilePack/Encode.hs +++ b/src/FilePack/Encode.hs @@ -73,3 +73,9 @@ instance Encode Word32 where instance Encode FileMode where encode (CMode mode) = encode mode + +instance (Encode a, Encode b) => Encode (a, b) where + encode (a, b) = encodeWithSize a <> encodeWithSize b + +instance {-# OVERLAPPABLE #-} Encode a => Encode [a] where + encode = foldMap encodeWithSize diff --git a/src/FilePack/FileData.hs b/src/FilePack/FileData.hs index 2cbae28..b5ad5bc 100644 --- a/src/FilePack/FileData.hs +++ b/src/FilePack/FileData.hs @@ -1,42 +1,61 @@ +{-# LANGUAGE ImpredicativeTypes #-} +{-# LANGUAGE RecordWildCards #-} + module FilePack.FileData where -import Data.ByteString (ByteString) +import Data.Text -import Data.Text (Text) +import System.Posix.Types (FileMode) -import Data.ByteString.Base64 qualified as B64 -import Data.ByteString.Char8 qualified as BC -import GHC.Natural (Natural) -import System.Posix.Types (CMode (..), FileMode) -import Text.Read (readEither) +import Data.Word (Word32) +import FilePack.Encode (Encode (encode, encodeWithSize)) -import FilePack.Encode () +data FileData a = FileData + { fileName :: Text + , fileSize :: Word32 + , filePermissions :: FileMode + , fileData :: a + } + deriving (Eq, Read, Show) -data FileData = FileData - { fileName :: Text - , fileSize :: Natural - , filePermissions :: FileMode - , fileData :: ByteString - } - deriving (Eq, Read, Show) +instance Encode a => Encode (FileData a) where + encode FileData{..} = + mconcat + [ encodeWithSize fileName + , encodeWithSize fileSize + , encodeWithSize filePermissions + , encodeWithSize fileData + ] -newtype FilePack = FilePack {getPackedFiles :: [FileData]} - deriving (Eq, Read, Show) +data Packable = forall a. Encode a => Packable {getPackable :: a} -{- | @packFiles@ takes a FilePack and encodes it into a serialized form. +instance Encode Packable where + encode (Packable a) = encode a - >>> import Data.Text qualified as T - >>> import Data.ByteString qualified as BS - >>> import System.Posix.Files (stdFileMode) - >>> let sampleFileData = FileData{fileName=T.pack "foo.txt", fileSize = 1024, filePermissions = stdFileMode, fileData = BS.empty} - >>> let sampleFilePack = FilePack [sampleFileData] - >>> (Right sampleFilePack) == (unpackFiles . packFiles $ sampleFilePack) - True --} -packFiles :: FilePack -> ByteString -packFiles = B64.encode . BC.pack . show +newtype FilePack = FilePack [Packable] + +instance Encode FilePack where + encode (FilePack ps) = encode ps + +infixr 6 .: +(.:) :: Encode a => FileData a -> FilePack -> FilePack +(.:) = addFileDataToPack + +-- | @addFileDataToPack@ takes an encodeable FileData and adds it to a FilePack. +-- +-- >>> import Data.ByteString qualified as BS +-- >>> d = FileData{fileName = pack "a", fileSize = 4, filePermissions = 0644, fileData = "foo"} +-- >>> BS.length $ encode d +-- 28 +addFileDataToPack :: Encode a => FileData a -> FilePack -> FilePack +addFileDataToPack a (FilePack as) = FilePack (Packable a : as) + +-- | @packFiles@ takes a FilePack and encodes it into a serialized form. +-- +-- packFiles :: FilePack -> ByteString +-- packFiles = B64.encode . encode -- | @unpackFiles@ tries to recover a FilePack from its serialized form. -unpackFiles :: ByteString -> Either String FilePack -unpackFiles serializedData = - B64.decode serializedData >>= readEither . BC.unpack +-- unpackFiles :: ByteString -> Either String FilePack +-- unpackFiles serializedData = +-- B64.decode serializedData >>= readEither . BC.unpack