aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
authorLibravatar Alexander Foremny <aforemny@posteo.de>2024-02-08 04:51:59 +0100
committerLibravatar Alexander Foremny <aforemny@posteo.de>2024-02-08 04:51:59 +0100
commit0c4cee8aa80f3793bd26ebccd2f60249a7c144fd (patch)
treeefa2d7e3c4f61090b04907c077ae7d2fd1d64067 /app
parenta980a128c54dff021ec21478e60b5e241749d504 (diff)
add Git store
Diffstat (limited to 'app')
-rw-r--r--app/Main.hs37
-rw-r--r--app/Store.hs98
2 files changed, 122 insertions, 13 deletions
diff --git a/app/Main.hs b/app/Main.hs
index 493a807..47f0280 100644
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
@@ -8,14 +9,17 @@ import Data.Aeson qualified as J
import Data.Aeson.Key qualified as JK
import Data.Aeson.KeyMap qualified as JM
import Data.ByteString.Lazy.Char8 qualified as LB
-import Data.List (foldl')
+import Data.List (foldl', isSuffixOf)
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Set qualified as S
import Data.String (IsString (fromString))
+import Data.Tagged (Tagged (Tagged))
import Data.Text qualified as T
import Debug.Trace (trace)
-import System.Directory (listDirectory, setCurrentDirectory)
-import System.FilePath (takeExtension, (</>))
+import Git
+import Store qualified as S
+import System.Directory (setCurrentDirectory)
+import System.FilePath ((</>))
import Text.Megaparsec qualified as P
import Text.Megaparsec.Char qualified as P
import Text.Megaparsec.Char.Lexer qualified as P
@@ -121,20 +125,30 @@ instance IsString Query where
query :: Query -> IO [J.Value]
query (Select fs c js ws) = do
- c' <- mapM (fmap (Record c) . decodeFile . (c </>)) =<< ls c
+ c' <-
+ mapM (fmap (Record c) . decodeFile . (c </>)) =<< ls c
js' <-
mapM
( \j ->
case j of
LeftJoin c ws ->
- fmap (\j' -> LeftJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>)) =<< ls c
+ fmap (\j' -> LeftJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>))
+ =<< ls c
RightJoin c ws ->
- fmap (\j' -> RightJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>)) =<< ls c
+ fmap (\j' -> RightJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>))
+ =<< ls c
FullJoin c ws ->
- fmap (\j' -> FullJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>)) =<< ls c
+ fmap (\j' -> FullJoin (map (Record c) j') ws) . mapM (decodeFile . (c </>))
+ =<< ls c
)
js
pure $ map (select fs) $ where_ ws $ combine c' js'
+ where
+ ls c =
+ filter (not . (isSuffixOf "/"))
+ <$> S.withStore "." do
+ Just cid <- fmap Tagged <$> resolveReference "HEAD"
+ S.listDirectory cid c
combine :: [Record J.Value] -> [Join [Record J.Value]] -> [[Record J.Value]]
combine vs js = combine' (map (: []) vs) js
@@ -177,18 +191,15 @@ combine vs js = combine' (map (: []) vs) js
)
jss
-ls :: FilePath -> IO [FilePath]
-ls =
- fmap (filter ((== ".json") . takeExtension)) . listDirectory
-
data DecodeException = DecodeException
deriving (Show)
instance Exception DecodeException
decodeFile :: J.FromJSON a => FilePath -> IO a
-decodeFile =
- fmap (fromMaybe (throw DecodeException)) . J.decodeFileStrict
+decodeFile fp = S.withStore "." do
+ Just cid <- fmap Tagged <$> resolveReference "HEAD"
+ fromMaybe (throw DecodeException) . J.decode <$> S.readFile cid fp
select :: FieldSelector -> [Record J.Value] -> J.Value
select All vs =
diff --git a/app/Store.hs b/app/Store.hs
new file mode 100644
index 0000000..704a1cc
--- /dev/null
+++ b/app/Store.hs
@@ -0,0 +1,98 @@
+{-# LANGUAGE AllowAmbiguousTypes #-}
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Store
+ ( withStore,
+ listDirectory,
+ readFile,
+ )
+where
+
+import Control.Arrow (first)
+import Control.Monad.Catch
+import Control.Monad.IO.Unlift (MonadUnliftIO)
+import Control.Monad.Reader (ReaderT)
+import Data.ByteString qualified as B
+import Data.ByteString.Lazy qualified as LB
+import Data.ByteString.UTF8 qualified as B
+import Data.List (isPrefixOf, sort)
+import Data.Text qualified as T
+import Git
+import Git.Libgit2 (LgRepo, lgFactory)
+import System.FilePath
+import Prelude hiding (readFile)
+
+withStore ::
+ (MonadMask m, MonadUnliftIO m) =>
+ FilePath ->
+ ReaderT LgRepo m a ->
+ m a
+withStore = withRepository lgFactory
+
+listDirectory ::
+ MonadGit r m =>
+ CommitOid r ->
+ FilePath ->
+ m [FilePath]
+listDirectory cid dir' = do
+ let dir = normalise $ if hasDrive dir' then dir' else joinDrive "/" dir'
+ n = length (splitPath dir)
+ tid <- (.commitTree) <$> lookupCommit cid
+ tree <- lookupTree tid
+ sort
+ . map (makeRelative dir)
+ . filter ((== n + 1) . length . splitPath)
+ . filter (isPrefixOf (addTrailingPathSeparator dir))
+ . map fst
+ . map
+ ( \e ->
+ case snd e of
+ BlobEntry _ _ -> e
+ CommitEntry _ -> error "XXX commit entry"
+ TreeEntry _ -> first addTrailingPathSeparator e
+ )
+ . map (first (("/" <>) . B.toString))
+ <$> listTreeEntries tree
+
+data DoesNotExist = DoesNotExist String FilePath
+ deriving (Show)
+
+instance Exception DoesNotExist
+
+data InappropriateType = InappropriateType String FilePath
+ deriving (Show)
+
+instance Exception InappropriateType
+
+class Readable a where
+ readFile :: MonadGit r m => CommitOid r -> FilePath -> m a
+
+instance Readable T.Text where
+ readFile = readFile' catBlobUtf8
+
+instance Readable B.ByteString where
+ readFile = readFile' catBlob
+
+instance Readable LB.ByteString where
+ readFile = readFile' catBlobLazy
+
+readFile' ::
+ MonadGit r m =>
+ (BlobOid r -> m a) ->
+ CommitOid r ->
+ FilePath ->
+ m a
+readFile' cat cid fp = do
+ tid <- (.commitTree) <$> lookupCommit cid
+ tree <- lookupTree tid
+ maybe
+ (throwM (DoesNotExist "readFile" fp))
+ ( \e ->
+ case e of
+ BlobEntry bid _ -> cat bid
+ CommitEntry _ -> error "XXX commit entry"
+ TreeEntry _ -> throwM (InappropriateType "readFile" fp)
+ )
+ =<< treeEntry tree (B.fromString fp)