-- 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 Control.Lens
import Data.Set   (Set)
import Data.Maybe (catMaybes)

import qualified Data.Set         as Set
import qualified Restless.Git     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
/= :: RepoAt -> RepoAt -> Bool
$c/= :: RepoAt -> RepoAt -> Bool
== :: RepoAt -> RepoAt -> Bool
$c== :: 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
min :: RepoAt -> RepoAt -> RepoAt
$cmin :: RepoAt -> RepoAt -> RepoAt
max :: RepoAt -> RepoAt -> RepoAt
$cmax :: RepoAt -> RepoAt -> RepoAt
>= :: RepoAt -> RepoAt -> Bool
$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
compare :: RepoAt -> RepoAt -> Ordering
$ccompare :: RepoAt -> RepoAt -> Ordering
$cp1Ord :: Eq RepoAt
Ord, Int -> RepoAt -> ShowS
[RepoAt] -> ShowS
RepoAt -> String
(Int -> RepoAt -> ShowS)
-> (RepoAt -> String) -> ([RepoAt] -> ShowS) -> Show RepoAt
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [RepoAt] -> ShowS
$cshowList :: [RepoAt] -> ShowS
show :: RepoAt -> String
$cshow :: RepoAt -> String
showsPrec :: Int -> RepoAt -> ShowS
$cshowsPrec :: Int -> RepoAt -> ShowS
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 :: p File (f File) -> p File (f File)
fileRepr = (File -> File) -> (File -> File) -> Iso File File 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 (Getting File File File -> File -> File
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view Getting File File File
Iso File File 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 :: 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 (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
. Getting File File File -> File -> File
forall s (m :: * -> *) a. MonadReader s m => Getting a s a -> m a
view (AnIso File File File File -> Iso File File File File
forall s t a b. AnIso s t a b -> Iso b a t s
from AnIso File File File File
Iso File File File File
fileRepr)))
    (String -> IO (Set File)
forall (m :: * -> *).
(Monad m, MonadIO m) =>
String -> m (Set File)
Git.load String
src)