{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Provides 'PathData' for use with the Cbor backend.
module Charon.Backend.Cbor.PathData
  ( -- * PathData
    PathData (..),
    toPathData,
    toCorePathData,
    fromCorePathData,
  )
where

import Charon.Backend.Default.Utils qualified as Default.Utils
import Charon.Class.Serial (Serial (DecodeExtra, decode, encode))
import Charon.Data.PathData qualified as PathData
import Charon.Data.PathType (PathTypeW)
import Charon.Data.Paths
  ( PathI (MkPathI),
    PathIndex
      ( TrashEntryFileName,
        TrashEntryOriginalPath,
        TrashHome
      ),
  )
import Charon.Data.Timestamp (Timestamp)
import Charon.Prelude
import Charon.Utils qualified as Utils
import Codec.Serialise qualified as Serialise
import Data.Bifunctor (Bifunctor (first))
import Data.ByteString.Lazy qualified as BSL

-- | Data for an Fdo path. Maintains an invariant that the original path is not
-- the root nor is it empty.
data PathData = UnsafePathData
  { -- | The path type.
    PathData -> PathTypeW
pathType :: PathTypeW,
    -- | The path to be used in the trash directory.
    PathData -> PathI 'TrashEntryFileName
fileName :: PathI TrashEntryFileName,
    -- | The original path on the file system.
    PathData -> PathI 'TrashEntryOriginalPath
originalPath :: PathI TrashEntryOriginalPath,
    -- | Time this entry was created.
    PathData -> Timestamp
created :: Timestamp,
    -- | The size.
    PathData -> Bytes 'B Natural
size :: Bytes B Natural
  }
  deriving stock (PathData -> PathData -> Bool
(PathData -> PathData -> Bool)
-> (PathData -> PathData -> Bool) -> Eq PathData
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: PathData -> PathData -> Bool
== :: PathData -> PathData -> Bool
$c/= :: PathData -> PathData -> Bool
/= :: PathData -> PathData -> Bool
Eq, (forall x. PathData -> Rep PathData x)
-> (forall x. Rep PathData x -> PathData) -> Generic PathData
forall x. Rep PathData x -> PathData
forall x. PathData -> Rep PathData x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. PathData -> Rep PathData x
from :: forall x. PathData -> Rep PathData x
$cto :: forall x. Rep PathData x -> PathData
to :: forall x. Rep PathData x -> PathData
Generic, Int -> PathData -> ShowS
[PathData] -> ShowS
PathData -> String
(Int -> PathData -> ShowS)
-> (PathData -> String) -> ([PathData] -> ShowS) -> Show PathData
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> PathData -> ShowS
showsPrec :: Int -> PathData -> ShowS
$cshow :: PathData -> String
show :: PathData -> String
$cshowList :: [PathData] -> ShowS
showList :: [PathData] -> ShowS
Show)
  deriving anyclass (Eq PathData
Eq PathData =>
(Int -> PathData -> Int) -> (PathData -> Int) -> Hashable PathData
Int -> PathData -> Int
PathData -> Int
forall a. Eq a => (Int -> a -> Int) -> (a -> Int) -> Hashable a
$chashWithSalt :: Int -> PathData -> Int
hashWithSalt :: Int -> PathData -> Int
$chash :: PathData -> Int
hash :: PathData -> Int
Hashable, PathData -> ()
(PathData -> ()) -> NFData PathData
forall a. (a -> ()) -> NFData a
$crnf :: PathData -> ()
rnf :: PathData -> ()
NFData)

makeFieldLabelsNoPrefix ''PathData

-- | For a given filepath, attempts to capture the following data:
--
-- * Canonical path.
-- * Unique name to be used in the trash directory.
-- * File/directory type.
toPathData ::
  ( HasCallStack,
    MonadAsync m,
    MonadCatch m,
    MonadLoggerNS m,
    MonadPathReader m,
    MonadPosixCompat m,
    MonadTerminal m
  ) =>
  Timestamp ->
  PathI TrashHome ->
  PathI TrashEntryOriginalPath ->
  m (PathData, PathTypeW)
toPathData :: forall (m :: * -> *).
(HasCallStack, MonadAsync m, MonadCatch m, MonadLoggerNS m,
 MonadPathReader m, MonadPosixCompat m, MonadTerminal m) =>
Timestamp
-> PathI 'TrashHome
-> PathI 'TrashEntryOriginalPath
-> m (PathData, PathTypeW)
toPathData Timestamp
currTime PathI 'TrashHome
trashHome PathI 'TrashEntryOriginalPath
origPath = Text -> m (PathData, PathTypeW) -> m (PathData, PathTypeW)
forall (m :: * -> *) a. MonadLoggerNS m => Text -> m a -> m a
addNamespace Text
"toPathData" (m (PathData, PathTypeW) -> m (PathData, PathTypeW))
-> m (PathData, PathTypeW) -> m (PathData, PathTypeW)
forall a b. (a -> b) -> a -> b
$ do
  (PathI 'TrashEntryFileName
fileName, PathI 'TrashEntryOriginalPath
originalPath, PathTypeW
pathType) <- PathI 'TrashHome
-> PathI 'TrashEntryOriginalPath
-> m (PathI 'TrashEntryFileName, PathI 'TrashEntryOriginalPath,
      PathTypeW)
forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadLoggerNS m, MonadPathReader m) =>
PathI 'TrashHome
-> PathI 'TrashEntryOriginalPath
-> m (PathI 'TrashEntryFileName, PathI 'TrashEntryOriginalPath,
      PathTypeW)
Default.Utils.getPathInfo PathI 'TrashHome
trashHome PathI 'TrashEntryOriginalPath
origPath
  Bytes 'B Natural
size <- OsPath -> m (Bytes 'B Natural)
forall (m :: * -> *).
(HasCallStack, MonadAsync m, MonadCatch m, MonadLoggerNS m,
 MonadPathReader m, MonadPosixCompat m, MonadTerminal m) =>
OsPath -> m (Bytes 'B Natural)
Utils.getPathSize (PathI 'TrashEntryOriginalPath
originalPath PathI 'TrashEntryOriginalPath
-> Optic' An_Iso NoIx (PathI 'TrashEntryOriginalPath) OsPath
-> OsPath
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' An_Iso NoIx (PathI 'TrashEntryOriginalPath) OsPath
#unPathI)

  (PathData, PathTypeW) -> m (PathData, PathTypeW)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
    ( UnsafePathData
        { PathTypeW
$sel:pathType:UnsafePathData :: PathTypeW
pathType :: PathTypeW
pathType,
          PathI 'TrashEntryFileName
$sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName :: PathI 'TrashEntryFileName
fileName,
          PathI 'TrashEntryOriginalPath
$sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath :: PathI 'TrashEntryOriginalPath
originalPath,
          $sel:created:UnsafePathData :: Timestamp
created = Timestamp
currTime,
          Bytes 'B Natural
$sel:size:UnsafePathData :: Bytes 'B Natural
size :: Bytes 'B Natural
size
        },
      PathTypeW
pathType
    )

instance Serial PathData where
  type DecodeExtra PathData = PathI TrashEntryFileName

  encode :: PathData -> Either String ByteString
  encode :: PathData -> Either String ByteString
encode (UnsafePathData PathTypeW
pathType PathI 'TrashEntryFileName
_ (MkPathI OsPath
opath) Timestamp
ts (MkBytes Natural
sz)) =
    case OsPath -> Either EncodingException String
decodeOsToFp OsPath
opath of
      Right String
opath' -> ByteString -> Either String ByteString
forall a. a -> Either String a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Either String ByteString)
-> ByteString -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ ByteString -> ByteString
BSL.toStrict (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ (PathTypeW, String, Timestamp, Natural) -> ByteString
forall a. Serialise a => a -> ByteString
Serialise.serialise (PathTypeW
pathType, String
opath', Timestamp
ts, Natural
sz)
      Left EncodingException
ex -> String -> Either String ByteString
forall a b. a -> Either a b
Left (String -> Either String ByteString)
-> String -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ EncodingException -> String
forall e. Exception e => e -> String
displayException EncodingException
ex

  decode :: PathI TrashEntryFileName -> ByteString -> Either String PathData
  decode :: PathI 'TrashEntryFileName -> ByteString -> Either String PathData
decode PathI 'TrashEntryFileName
name ByteString
bs = do
    (PathTypeW
pathType, String
opath, Timestamp
created, Natural
size) <- (DeserialiseFailure -> String)
-> Either
     DeserialiseFailure (PathTypeW, String, Timestamp, Natural)
-> Either String (PathTypeW, String, Timestamp, Natural)
forall a b c. (a -> b) -> Either a c -> Either b c
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first DeserialiseFailure -> String
forall a. Show a => a -> String
show (Either DeserialiseFailure (PathTypeW, String, Timestamp, Natural)
 -> Either String (PathTypeW, String, Timestamp, Natural))
-> Either
     DeserialiseFailure (PathTypeW, String, Timestamp, Natural)
-> Either String (PathTypeW, String, Timestamp, Natural)
forall a b. (a -> b) -> a -> b
$ ByteString
-> Either
     DeserialiseFailure (PathTypeW, String, Timestamp, Natural)
forall a. Serialise a => ByteString -> Either DeserialiseFailure a
Serialise.deserialiseOrFail (ByteString -> ByteString
BSL.fromStrict ByteString
bs)

    case String -> Either EncodingException OsPath
encodeFpToOs String
opath of
      Right OsPath
opath' ->
        PathData -> Either String PathData
forall a b. b -> Either a b
Right
          (PathData -> Either String PathData)
-> PathData -> Either String PathData
forall a b. (a -> b) -> a -> b
$ UnsafePathData
            { PathTypeW
$sel:pathType:UnsafePathData :: PathTypeW
pathType :: PathTypeW
pathType,
              $sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName = PathI 'TrashEntryFileName
name,
              $sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath = OsPath -> PathI 'TrashEntryOriginalPath
forall (i :: PathIndex). OsPath -> PathI i
MkPathI OsPath
opath',
              Timestamp
$sel:created:UnsafePathData :: Timestamp
created :: Timestamp
created,
              $sel:size:UnsafePathData :: Bytes 'B Natural
size = Natural -> Bytes 'B Natural
forall (s :: Size) n. n -> Bytes s n
MkBytes Natural
size
            }
      Left EncodingException
ex -> String -> Either String PathData
forall a b. a -> Either a b
Left (String -> Either String PathData)
-> String -> Either String PathData
forall a b. (a -> b) -> a -> b
$ EncodingException -> String
forall e. Exception e => e -> String
displayException EncodingException
ex

toCorePathData :: PathData -> PathData.PathData
toCorePathData :: PathData -> PathData
toCorePathData PathData
pd = do
  PathData.UnsafePathData
    { $sel:pathType:UnsafePathData :: PathTypeW
pathType = PathData
pd PathData -> Optic' A_Lens NoIx PathData PathTypeW -> PathTypeW
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData PathTypeW
#pathType,
      $sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (PathI 'TrashEntryFileName)
-> PathI 'TrashEntryFileName
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (PathI 'TrashEntryFileName)
#fileName,
      $sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (PathI 'TrashEntryOriginalPath)
-> PathI 'TrashEntryOriginalPath
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (PathI 'TrashEntryOriginalPath)
#originalPath,
      $sel:size:UnsafePathData :: Bytes 'B Natural
size = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (Bytes 'B Natural)
-> Bytes 'B Natural
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (Bytes 'B Natural)
#size,
      $sel:created:UnsafePathData :: Timestamp
created = PathData
pd PathData -> Optic' A_Lens NoIx PathData Timestamp -> Timestamp
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData Timestamp
#created
    }

fromCorePathData :: PathData.PathData -> PathData
fromCorePathData :: PathData -> PathData
fromCorePathData PathData
pd =
  UnsafePathData
    { $sel:pathType:UnsafePathData :: PathTypeW
pathType = PathData
pd PathData -> Optic' A_Lens NoIx PathData PathTypeW -> PathTypeW
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData PathTypeW
#pathType,
      $sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (PathI 'TrashEntryFileName)
-> PathI 'TrashEntryFileName
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (PathI 'TrashEntryFileName)
#fileName,
      $sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (PathI 'TrashEntryOriginalPath)
-> PathI 'TrashEntryOriginalPath
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (PathI 'TrashEntryOriginalPath)
#originalPath,
      $sel:created:UnsafePathData :: Timestamp
created = PathData
pd PathData -> Optic' A_Lens NoIx PathData Timestamp -> Timestamp
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData Timestamp
#created,
      $sel:size:UnsafePathData :: Bytes 'B Natural
size = PathData
pd PathData
-> Optic' A_Lens NoIx PathData (Bytes 'B Natural)
-> Bytes 'B Natural
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' A_Lens NoIx PathData (Bytes 'B Natural)
#size
    }