{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}
module Charon.Backend.Fdo.PathData
(
PathData (..),
toPathData,
toCorePathData,
toCorePathDataDirectorySizes,
fromCorePathData,
)
where
import Charon.Backend.Default.Trash qualified as Trash
import Charon.Backend.Default.Utils qualified as Default.Utils
import Charon.Backend.Fdo.DirectorySizes (DirectorySizesEntry)
import Charon.Backend.Fdo.Utils qualified as Fdo.Utils
import Charon.Class.Serial (Serial (..), decodeUnit)
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 U
import Charon.Utils qualified as Utils
import Data.ByteString.Char8 qualified as C8
import Data.HashMap.Strict qualified as HMap
import Data.HashSet qualified as Set
import Effects.FileSystem.PathReader qualified as PR
import Numeric.Algebra (ASemigroup ((.+.)))
import Numeric.Literal.Integer (FromInteger (afromInteger))
data PathData = UnsafePathData
{
PathData -> PathI 'TrashEntryFileName
fileName :: PathI TrashEntryFileName,
PathData -> PathI 'TrashEntryOriginalPath
originalPath :: PathI TrashEntryOriginalPath,
PathData -> Timestamp
created :: Timestamp
}
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
toPathData ::
( HasCallStack,
MonadCatch m,
MonadLoggerNS m,
MonadPathReader m
) =>
Timestamp ->
PathI TrashHome ->
PathI TrashEntryOriginalPath ->
m (PathData, PathTypeW)
toPathData :: forall (m :: * -> *).
(HasCallStack, MonadCatch m, MonadLoggerNS m, MonadPathReader 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
(PathData, PathTypeW) -> m (PathData, PathTypeW)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
( UnsafePathData
{ $sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName = PathI 'TrashEntryFileName
fileName',
$sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath = PathI 'TrashEntryOriginalPath
originalPath',
$sel:created:UnsafePathData :: Timestamp
created = Timestamp
currTime
},
PathTypeW
pathType
)
instance Serial PathData where
type PathData = PathI TrashEntryFileName
encode :: PathData -> Either String ByteString
encode :: PathData -> Either String ByteString
encode PathData
pd =
case (PathI 'TrashEntryOriginalPath -> Either String ByteString
forall a. Serial a => a -> Either String ByteString
encode (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), Timestamp -> Either String ByteString
forall a. Serial a => a -> Either String ByteString
encode (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)) of
(Right ByteString
opath, Right ByteString
created) ->
ByteString -> Either String ByteString
forall a b. b -> Either a b
Right
(ByteString -> Either String ByteString)
-> ByteString -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ [ByteString] -> ByteString
C8.unlines
[ ByteString
Item [ByteString]
"[Trash Info]",
ByteString
"Path=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString -> ByteString
U.percentEncode ByteString
opath,
ByteString
"DeletionDate=" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
created
]
(Left String
ex, Either String ByteString
_) -> String -> Either String ByteString
forall a b. a -> Either a b
Left String
ex
(Either String ByteString
_, Left String
ex) -> String -> Either String ByteString
forall a b. a -> Either a b
Left String
ex
decode :: PathI TrashEntryFileName -> ByteString -> Either String PathData
decode :: PathI 'TrashEntryFileName -> ByteString -> Either String PathData
decode PathI 'TrashEntryFileName
name ByteString
bs = do
HashMap ByteString ByteString
mp <- HashSet ByteString
-> ByteString -> Either String (HashMap ByteString ByteString)
Default.Utils.parseTrashInfoMap HashSet ByteString
expectedKeys ByteString
bs
PathI 'TrashEntryOriginalPath
originalPath <- ByteString -> Either String (PathI 'TrashEntryOriginalPath)
forall a.
(DecodeExtra a ~ (), Serial a) =>
ByteString -> Either String a
decodeUnit (ByteString -> Either String (PathI 'TrashEntryOriginalPath))
-> (ByteString -> ByteString)
-> ByteString
-> Either String (PathI 'TrashEntryOriginalPath)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> ByteString
U.percentDecode (ByteString -> Either String (PathI 'TrashEntryOriginalPath))
-> Either String ByteString
-> Either String (PathI 'TrashEntryOriginalPath)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ByteString
-> HashMap ByteString ByteString -> Either String ByteString
forall b. ByteString -> HashMap ByteString b -> Either String b
Default.Utils.lookup ByteString
"Path" HashMap ByteString ByteString
mp
Timestamp
created <- ByteString -> Either String Timestamp
forall a.
(DecodeExtra a ~ (), Serial a) =>
ByteString -> Either String a
decodeUnit (ByteString -> Either String Timestamp)
-> Either String ByteString -> Either String Timestamp
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< ByteString
-> HashMap ByteString ByteString -> Either String ByteString
forall b. ByteString -> HashMap ByteString b -> Either String b
Default.Utils.lookup ByteString
"DeletionDate" HashMap ByteString ByteString
mp
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
{ $sel:fileName:UnsafePathData :: PathI 'TrashEntryFileName
fileName = PathI 'TrashEntryFileName
name,
PathI 'TrashEntryOriginalPath
$sel:originalPath:UnsafePathData :: PathI 'TrashEntryOriginalPath
originalPath :: PathI 'TrashEntryOriginalPath
originalPath,
Timestamp
$sel:created:UnsafePathData :: Timestamp
created :: Timestamp
created
}
where
expectedKeys :: HashSet ByteString
expectedKeys = [ByteString] -> HashSet ByteString
forall a. (Eq a, Hashable a) => [a] -> HashSet a
Set.fromList [ByteString
Item [ByteString]
"Path", ByteString
Item [ByteString]
"DeletionDate"]
toCorePathData ::
( HasCallStack,
MonadAsync m,
MonadCatch m,
MonadLoggerNS m,
MonadPathReader m,
MonadPosixCompat m,
MonadTerminal m
) =>
PathI TrashHome ->
PathData ->
m PathData.PathData
toCorePathData :: forall (m :: * -> *).
(HasCallStack, MonadAsync m, MonadCatch m, MonadLoggerNS m,
MonadPathReader m, MonadPosixCompat m, MonadTerminal m) =>
PathI 'TrashHome -> PathData -> m PathData
toCorePathData PathI 'TrashHome
trashHome PathData
pd = Text -> m PathData -> m PathData
forall (m :: * -> *) a. MonadLoggerNS m => Text -> m a -> m a
addNamespace Text
"toCorePathData" (m PathData -> m PathData) -> m PathData -> m PathData
forall a b. (a -> b) -> a -> b
$ do
$(logDebug) (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"PathData: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> PathData -> Text
forall a. Show a => a -> Text
showt PathData
pd
PathTypeW
pathType <- PathI 'TrashHome -> PathData -> m PathTypeW
forall k a (m :: * -> *).
(Is k A_Getter,
LabelOptic' "fileName" k a (PathI 'TrashEntryFileName),
HasCallStack, MonadCatch m, MonadPathReader m) =>
PathI 'TrashHome -> a -> m PathTypeW
Default.Utils.pathDataToType PathI 'TrashHome
trashHome PathData
pd
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 OsPath
path
PathData -> m PathData
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
(PathData -> m PathData) -> PathData -> m PathData
forall a b. (a -> b) -> a -> b
$ PathData.UnsafePathData
{ PathTypeW
pathType :: PathTypeW
$sel:pathType:UnsafePathData :: 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,
Bytes 'B Natural
size :: Bytes 'B Natural
$sel:size:UnsafePathData :: 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
}
where
MkPathI OsPath
path = PathI 'TrashHome
-> PathI 'TrashEntryFileName -> PathI 'TrashEntryPath
Trash.getTrashPath PathI 'TrashHome
trashHome (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)
toCorePathDataDirectorySizes ::
( HasCallStack,
MonadAsync m,
MonadCatch m,
MonadLoggerNS m,
MonadPathReader m,
MonadPosixCompat m,
MonadTerminal m
) =>
HashMap ByteString DirectorySizesEntry ->
PathI TrashHome ->
PathData ->
m PathData.PathData
toCorePathDataDirectorySizes :: forall (m :: * -> *).
(HasCallStack, MonadAsync m, MonadCatch m, MonadLoggerNS m,
MonadPathReader m, MonadPosixCompat m, MonadTerminal m) =>
HashMap ByteString DirectorySizesEntry
-> PathI 'TrashHome -> PathData -> m PathData
toCorePathDataDirectorySizes HashMap ByteString DirectorySizesEntry
dsizeMap PathI 'TrashHome
trashHome PathData
pd = Text -> m PathData -> m PathData
forall (m :: * -> *) a. MonadLoggerNS m => Text -> m a -> m a
addNamespace Text
"toCorePathDataDirectorySizes" (m PathData -> m PathData) -> m PathData -> m PathData
forall a b. (a -> b) -> a -> b
$ do
$(logDebug) (Text -> m ()) -> Text -> m ()
forall a b. (a -> b) -> a -> b
$ Text
"PathData: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> PathData -> Text
forall a. Show a => a -> Text
showt PathData
pd
PathTypeW
pathType <- PathI 'TrashHome -> PathData -> m PathTypeW
forall k a (m :: * -> *).
(Is k A_Getter,
LabelOptic' "fileName" k a (PathI 'TrashEntryFileName),
HasCallStack, MonadCatch m, MonadPathReader m) =>
PathI 'TrashHome -> a -> m PathTypeW
Default.Utils.pathDataToType PathI 'TrashHome
trashHome PathData
pd
Bytes 'B Natural
size <- case PathTypeW
pathType PathTypeW -> Optic' An_Iso NoIx PathTypeW PathType -> PathType
forall k s (is :: IxList) a.
Is k A_Getter =>
s -> Optic' k is s a -> a
^. Optic' An_Iso NoIx PathTypeW PathType
#unPathTypeW of
PathType
PathTypeFile -> Integer -> Bytes 'B Natural
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger (Integer -> Bytes 'B Natural) -> m Integer -> m (Bytes 'B Natural)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> OsPath -> m Integer
forall (m :: * -> *).
(MonadPathReader m, HasCallStack) =>
OsPath -> m Integer
PR.getFileSize OsPath
path
PathType
PathTypeOther -> Integer -> Bytes 'B Natural
forall a. (FromInteger a, HasCallStack) => Integer -> a
afromInteger (Integer -> Bytes 'B Natural) -> m Integer -> m (Bytes 'B Natural)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> OsPath -> m Integer
forall (m :: * -> *).
(MonadPathReader m, HasCallStack) =>
OsPath -> m Integer
PR.getFileSize OsPath
path
PathType
PathTypeDirectory -> do
ByteString
name <- PathData -> m ByteString
forall (m :: * -> *) pd k.
(HasCallStack, Is k A_Getter,
LabelOptic' "fileName" k pd (PathI 'TrashEntryFileName),
MonadThrow m) =>
pd -> m ByteString
Fdo.Utils.percentEncodeFileName PathData
pd
case ByteString
-> HashMap ByteString DirectorySizesEntry
-> Maybe DirectorySizesEntry
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HMap.lookup ByteString
name HashMap ByteString DirectorySizesEntry
dsizeMap of
Just DirectorySizesEntry
entry -> do
Natural
dirSize <- forall a b. (Integral a, Num b) => a -> b
fromIntegral @_ @Natural (Integer -> Natural) -> m Integer -> m Natural
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> OsPath -> m Integer
forall (m :: * -> *).
(MonadPathReader m, HasCallStack) =>
OsPath -> m Integer
PR.getFileSize OsPath
path
Bytes 'B Natural -> m (Bytes 'B Natural)
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Bytes 'B Natural -> m (Bytes 'B Natural))
-> Bytes 'B Natural -> m (Bytes 'B Natural)
forall a b. (a -> b) -> a -> b
$ Natural -> Bytes 'B Natural
forall (s :: Size) n. n -> Bytes s n
MkBytes Natural
dirSize Bytes 'B Natural -> Bytes 'B Natural -> Bytes 'B Natural
forall s. ASemigroup s => s -> s -> s
.+. DirectorySizesEntry
entry DirectorySizesEntry
-> Optic' A_Lens NoIx DirectorySizesEntry (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 DirectorySizesEntry (Bytes 'B Natural)
#size
Maybe DirectorySizesEntry
Nothing -> 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 OsPath
path
PathType
PathTypeSymbolicLink -> OsPath -> m (Bytes 'B Natural)
forall (m :: * -> *).
(HasCallStack, MonadPosixCompat m, MonadThrow m) =>
OsPath -> m (Bytes 'B Natural)
Utils.getSymLinkSize OsPath
path
PathData -> m PathData
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure
(PathData -> m PathData) -> PathData -> m PathData
forall a b. (a -> b) -> a -> b
$ PathData.UnsafePathData
{ PathTypeW
$sel:pathType:UnsafePathData :: PathTypeW
pathType :: 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,
Bytes 'B Natural
$sel:size:UnsafePathData :: Bytes 'B Natural
size :: 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
}
where
MkPathI OsPath
path = PathI 'TrashHome
-> PathI 'TrashEntryFileName -> PathI 'TrashEntryPath
Trash.getTrashPath PathI 'TrashHome
trashHome (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)
fromCorePathData ::
PathData.PathData ->
PathData
fromCorePathData :: PathData -> PathData
fromCorePathData PathData
pd =
UnsafePathData
{ $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
}