-- This is a backend for the fact representation that uses a Git
-- repository as the store.

module EVM.Facts.Git
  ( saveFacts
  , loadFacts
  , RepoAt (..)
  ) where

import EVM.Facts (Fact (..), File (..), Path (..), Data (..), fileToFact, factToFile)

import Optics.Core
import Data.Set   (Set)
import Data.Set qualified as Set
import Data.Maybe (catMaybes)
import Restless.Git qualified as Git

newtype RepoAt = RepoAt String
  deriving (RepoAt -> RepoAt -> Bool
(RepoAt -> RepoAt -> Bool)
-> (RepoAt -> RepoAt -> Bool) -> Eq RepoAt
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RepoAt -> RepoAt -> Bool
== :: RepoAt -> RepoAt -> Bool
$c/= :: RepoAt -> RepoAt -> Bool
/= :: RepoAt -> RepoAt -> Bool
Eq, Eq RepoAt
Eq RepoAt
-> (RepoAt -> RepoAt -> Ordering)
-> (RepoAt -> RepoAt -> Bool)
-> (RepoAt -> RepoAt -> Bool)
-> (RepoAt -> RepoAt -> Bool)
-> (RepoAt -> RepoAt -> Bool)
-> (RepoAt -> RepoAt -> RepoAt)
-> (RepoAt -> RepoAt -> RepoAt)
-> Ord RepoAt
RepoAt -> RepoAt -> Bool
RepoAt -> RepoAt -> Ordering
RepoAt -> RepoAt -> RepoAt
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: RepoAt -> RepoAt -> Ordering
compare :: RepoAt -> RepoAt -> Ordering
$c< :: RepoAt -> RepoAt -> Bool
< :: RepoAt -> RepoAt -> Bool
$c<= :: RepoAt -> RepoAt -> Bool
<= :: RepoAt -> RepoAt -> Bool
$c> :: RepoAt -> RepoAt -> Bool
> :: RepoAt -> RepoAt -> Bool
$c>= :: RepoAt -> RepoAt -> Bool
>= :: RepoAt -> RepoAt -> Bool
$cmax :: RepoAt -> RepoAt -> RepoAt
max :: RepoAt -> RepoAt -> RepoAt
$cmin :: RepoAt -> RepoAt -> RepoAt
min :: RepoAt -> RepoAt -> RepoAt
Ord, Int -> RepoAt -> String -> String
[RepoAt] -> String -> String
RepoAt -> String
(Int -> RepoAt -> String -> String)
-> (RepoAt -> String)
-> ([RepoAt] -> String -> String)
-> Show RepoAt
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> RepoAt -> String -> String
showsPrec :: Int -> RepoAt -> String -> String
$cshow :: RepoAt -> String
show :: RepoAt -> String
$cshowList :: [RepoAt] -> String -> String
showList :: [RepoAt] -> String -> String
Show)

-- For modularity reasons, we have our own file data type that is
-- isomorphic with the one in the `restless-git` library.  We declare
-- the isomorphism so we can go between them easily.
fileRepr :: Iso' File Git.File
fileRepr :: Iso' File File
fileRepr = (File -> File) -> (File -> File) -> Iso' File File
forall s a b t. (s -> a) -> (b -> t) -> Iso s t a b
iso File -> File
f File -> File
g
  where
    f :: File -> Git.File
    f :: File -> File
f (File     (Path [ASCII]
ps ASCII
p)     (Data ASCII
x)) =
      Path -> ASCII -> File
Git.File ([ASCII] -> ASCII -> Path
Git.Path [ASCII]
ps ASCII
p) ASCII
x

    g :: Git.File -> File
    g :: File -> File
g (Git.File (Git.Path [ASCII]
ps ASCII
p) ASCII
x) =
      Path -> Data -> File
File     ([ASCII] -> ASCII -> Path
Path [ASCII]
ps ASCII
p)     (ASCII -> Data
Data ASCII
x)

saveFacts :: RepoAt -> Set Fact -> IO ()
saveFacts :: RepoAt -> Set Fact -> IO ()
saveFacts (RepoAt String
repo) Set Fact
facts =
  String -> Text -> Set File -> IO ()
forall (m :: * -> *).
(Monad m, MonadIO m) =>
String -> Text -> Set File -> m ()
Git.save String
repo Text
"hevm execution"
    ((Fact -> File) -> Set Fact -> Set File
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (Iso' File File -> File -> File
forall k (is :: IxList) s a.
Is k A_Getter =>
Optic' k is s a -> s -> a
view Iso' File File
fileRepr (File -> File) -> (Fact -> File) -> Fact -> File
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Fact -> File
factToFile) Set Fact
facts)

prune :: Ord a => Set (Maybe a) -> Set a
prune :: forall a. Ord a => Set (Maybe a) -> Set a
prune = [a] -> Set a
forall a. Ord a => [a] -> Set a
Set.fromList ([a] -> Set a) -> (Set (Maybe a) -> [a]) -> Set (Maybe a) -> Set a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Maybe a] -> [a]
forall a. [Maybe a] -> [a]
catMaybes ([Maybe a] -> [a])
-> (Set (Maybe a) -> [Maybe a]) -> Set (Maybe a) -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Set (Maybe a) -> [Maybe a]
forall a. Set a -> [a]
Set.toList

loadFacts :: RepoAt -> IO (Set Fact)
loadFacts :: RepoAt -> IO (Set Fact)
loadFacts (RepoAt String
src) =
  (Set File -> Set Fact) -> IO (Set File) -> IO (Set Fact)
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
    (Set (Maybe Fact) -> Set Fact
forall a. Ord a => Set (Maybe a) -> Set a
prune (Set (Maybe Fact) -> Set Fact)
-> (Set File -> Set (Maybe Fact)) -> Set File -> Set Fact
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (File -> Maybe Fact) -> Set File -> Set (Maybe Fact)
forall b a. Ord b => (a -> b) -> Set a -> Set b
Set.map (File -> Maybe Fact
fileToFact (File -> Maybe Fact) -> (File -> File) -> File -> Maybe Fact
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Iso' File File -> File -> File
forall k (is :: IxList) t b.
Is k A_Review =>
Optic' k is t b -> b -> t
review Iso' File File
fileRepr))
    (String -> IO (Set File)
forall (m :: * -> *).
(Monad m, MonadIO m) =>
String -> m (Set File)
Git.load String
src)