From 7e77e6335bab772c4be1b3f0594113d09cd4a366 Mon Sep 17 00:00:00 2001 From: stuebinm Date: Thu, 23 Sep 2021 03:01:09 +0200 Subject: handle all maps in entire repositories (+ checking that paths don't run outside of respositories) --- lib/CheckDir.hs | 98 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 60 insertions(+), 38 deletions(-) (limited to 'lib/CheckDir.hs') diff --git a/lib/CheckDir.hs b/lib/CheckDir.hs index 1ca71eb..753d5ab 100644 --- a/lib/CheckDir.hs +++ b/lib/CheckDir.hs @@ -2,35 +2,44 @@ {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TupleSections #-} -- | Module that contains high-level checking for an entire directory module CheckDir (recursiveCheckDir) where -import CheckMap (MapResult, loadAndLintMap, mapresultDepends) -import Data.Aeson (ToJSON) -import Data.Map (Map) -import qualified Data.Map as M -import Data.Text (Text) -import qualified Data.Text as T -import GHC.Generics (Generic) -import Paths (normalise) -import Types (Dep (LocalMap)) -import Util (PrettyPrint (prettyprint)) +import CheckMap (MapResult, loadAndLintMap, + mapresultDepends) +import Data.Aeson (ToJSON) +import Data.Functor ((<&>)) +import Data.Map (Map) +import qualified Data.Map as M +import Data.Maybe (mapMaybe) +import Data.Text (Text) +import qualified Data.Text as T +import GHC.Generics (Generic) +import Paths (normalise) +import System.FilePath (splitPath, ()) +import qualified System.FilePath as FP +import System.FilePath.Posix (takeDirectory) +import Types (Dep (LocalMap)) +import Util (PrettyPrint (prettyprint)) +-- based on the startling observation that Data.Map has lower complexity +-- for difference than Data.Set, but the same complexity for fromList +type Set a = Map a () +setFromList :: Ord a => [a] -> Set a +setFromList = M.fromList . flip zip (repeat ()) +listFromSet :: Set a -> [a] +listFromSet = map fst . M.toList data DirResult = DirResult - { dirresultMaps :: [MapResult] + { dirresultMaps :: Map FilePath MapResult , dirresultDeps :: [Text] } deriving (Generic, ToJSON) - instance PrettyPrint DirResult where - prettyprint res = "Here's a result:" <> T.concat (map prettyprint $ dirresultMaps res) - --- based on the startling observation that Data.Map has lower complexity --- for difference than Data.Set, but the same complexity for fromList -type Set a = Map a () - + prettyprint res = T.concat + (map (\(p,lints) -> "\nin " <> T.pack p <> ":\n" <> prettyprint lints) $ M.toList $ dirresultMaps res) instance Semigroup DirResult where a <> b = DirResult @@ -40,37 +49,50 @@ instance Semigroup DirResult where instance Monoid DirResult where mempty = DirResult - { dirresultMaps = [] + { dirresultMaps = mempty , dirresultDeps = [] } -- TODO: options? -recursiveCheckDir :: FilePath -> IO DirResult -recursiveCheckDir root = recursiveCheckDir' [root] mempty mempty +recursiveCheckDir :: FilePath -> FilePath -> IO DirResult +recursiveCheckDir prefix root = recursiveCheckDir' prefix [root] mempty mempty -recursiveCheckDir' :: [FilePath] -> Set FilePath -> DirResult -> IO DirResult -recursiveCheckDir' paths done acc = do +recursiveCheckDir' :: FilePath -> [FilePath] -> Set FilePath -> DirResult -> IO DirResult +recursiveCheckDir' prefix paths done acc = do putStrLn $ "linting " <> show paths + -- lint all maps in paths - lints <- mapM loadAndLintMap paths - -- get new deps - let deps = concatMap mapresultDepends lints - -- filter deps for map dependencies - let mapdeps = - map (\(LocalMap path) -> normalise path) - . filter (\case { LocalMap _ -> True; _ -> False }) - $ deps - -- build a Map FilePath () containing all map dependencies - let mapmapdeps = M.fromList $ zip mapdeps (repeat ()) - -- take difference of that with what's already done (O(m+n)) - let unknowns = map fst . M.toList $ M.difference mapmapdeps done - let known = M.union done . M.fromList . zip paths $ repeat () + lints <- + let lintPath p = loadAndLintMap (prefix p) depth <&> (p,) + where depth = length (splitPath p) - 1 + in mapM lintPath paths + + + let mapdeps = concatMap + (\(m,res) -> + let ps = mapMaybe + (\case {LocalMap p -> Just p; _ -> Nothing}) + (mapresultDepends res) + in map (FP.normalise . normalise (takeDirectory m)) ps + ) + lints + + -- build a Set containing all newly found dependencies, with paths + -- from the repository's directory, normalised not to start with ./ etc. + let setdeps = setFromList + mapdeps + -- that which is yet to do (O(m+n)) + let unknowns = listFromSet $ M.difference setdeps done + -- that which is done + let knowns = M.union done $ setFromList paths + -- Monoids! let acc' = acc <> DirResult - { dirresultMaps = lints + { dirresultMaps = M.fromList lints , dirresultDeps = [] } + -- Tail recursion! case unknowns of [] -> pure acc' - _ -> recursiveCheckDir' unknowns known acc' + _ -> recursiveCheckDir' prefix unknowns knowns acc' -- cgit v1.2.3