summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuebinm2022-02-18 18:09:23 +0100
committerstuebinm2022-02-18 18:09:23 +0100
commit7c49e6c367c9d021f3630c08a4a13ba9abc5df08 (patch)
treec278d23a6e39c353f5aa02d1ce9785122e1eea62
parentfaa244e1a7e760be88054a5f15b3e115ad8e32e5 (diff)
switch to universum prelude
also don't keep adjusted maps around if not necessary
-rw-r--r--.hlint.yaml3
-rw-r--r--lib/Badges.hs5
-rw-r--r--lib/CheckDir.hs101
-rw-r--r--lib/CheckMap.hs58
-rw-r--r--lib/Dirgraph.hs18
-rw-r--r--lib/KindLinter.hs7
-rw-r--r--lib/LayerData.hs12
-rw-r--r--lib/LintConfig.hs32
-rw-r--r--lib/LintWriter.hs26
-rw-r--r--lib/Paths.hs31
-rw-r--r--lib/Properties.hs79
-rw-r--r--lib/Tiled.hs26
-rw-r--r--lib/TiledAbstract.hs15
-rw-r--r--lib/Types.hs15
-rw-r--r--lib/Uris.hs44
-rw-r--r--lib/Util.hs19
-rw-r--r--lib/WriteRepo.hs16
-rw-r--r--package.yaml5
-rw-r--r--server/HtmlOrphans.hs2
-rw-r--r--server/Server.hs5
-rw-r--r--server/Worker.hs5
-rw-r--r--src/Main.hs30
-rw-r--r--src/Version.hs4
-rw-r--r--walint.cabal8
24 files changed, 279 insertions, 287 deletions
diff --git a/.hlint.yaml b/.hlint.yaml
index 0415941..202635a 100644
--- a/.hlint.yaml
+++ b/.hlint.yaml
@@ -136,9 +136,6 @@
- warn: {lhs: "m ?: mempty", rhs: maybeToMonoid m}
-- hint: {lhs: pure (), rhs: pass}
-- hint: {lhs: return (), rhs: pass}
-
# Probably will be reduced when function equality is done:
# https://github.com/ndmitchell/hlint/issues/434
- warn: {lhs: (case m of Just x -> f x; Nothing -> pure () ), rhs: Universum.whenJust m f}
diff --git a/lib/Badges.hs b/lib/Badges.hs
index c1a17b3..d6afc43 100644
--- a/lib/Badges.hs
+++ b/lib/Badges.hs
@@ -8,14 +8,13 @@
-- | module defining Badge types and utility functions
module Badges where
-import Control.DeepSeq (NFData)
+import Universum
+
import Data.Aeson (Options (fieldLabelModifier, sumEncoding),
SumEncoding (UntaggedValue), ToJSON (toJSON),
defaultOptions, genericToJSON, (.=))
import qualified Data.Aeson as A
import Data.Char (toLower)
-import Data.Text (Text)
-import GHC.Generics (Generic)
import Text.Regex.TDFA ((=~))
diff --git a/lib/CheckDir.hs b/lib/CheckDir.hs
index 652d58f..be23747 100644
--- a/lib/CheckDir.hs
+++ b/lib/CheckDir.hs
@@ -1,38 +1,46 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
-- | Module that contains high-level checking for an entire directory
-module CheckDir (maximumLintLevel, recursiveCheckDir, DirResult(..), MissingAsset(..), MissingDep(..), resultIsFatal) where
-
-import CheckMap (MapResult (..), loadAndLintMap)
-import Control.DeepSeq (NFData)
-import Control.Monad (void)
+module CheckDir ( maximumLintLevel
+ , recursiveCheckDir
+ , DirResult (..)
+ , MissingAsset(..)
+ , MissingDep(..)
+ , resultIsFatal
+ ,shrinkDirResult) where
+
+import Universum hiding (Set)
+
+import CheckMap (MapResult (..), Optional,
+ ResultKind (..), loadAndLintMap,
+ shrinkMapResult)
import Control.Monad.Extra (mapMaybeM)
import Data.Aeson (ToJSON, (.=))
import qualified Data.Aeson as A
-import Data.Foldable (fold)
-import Data.Functor ((<&>))
-import Data.Map (Map, elems, keys)
import qualified Data.Map as M
import Data.Map.Strict (mapKeys, mapWithKey, (\\))
-import Data.Maybe (isJust, mapMaybe)
-import Data.Text (Text, isInfixOf)
+import Data.Text (isInfixOf)
import qualified Data.Text as T
import Dirgraph (graphToDot, invertGraph, resultToGraph,
unreachableFrom)
-import GHC.Generics (Generic)
import LintConfig (LintConfig', configMaxLintLevel)
import Paths (normalise, normaliseWithFrag)
import System.Directory.Extra (doesFileExist)
import System.FilePath (splitPath, (</>))
import qualified System.FilePath as FP
import System.FilePath.Posix (takeDirectory)
-import Text.Dot (Dot, showDot)
+import Text.Dot (showDot)
+import Tiled (Tiledmap)
import Types (Dep (Local, LocalMap), Hint (Hint),
Level (..), hintLevel)
import Util (PrettyPrint (prettyprint))
@@ -47,15 +55,18 @@ listFromSet :: Set a -> [a]
listFromSet = map fst . M.toList
-- | Result of linting an entire directory / repository
-data DirResult = DirResult
- { dirresultMaps :: Map FilePath MapResult
+data DirResult (complete :: ResultKind) = DirResult
+ { dirresultMaps :: Map FilePath (MapResult complete)
-- ^ all maps of this respository, by (local) filepath
, dirresultDeps :: [MissingDep]
-- ^ all dependencies to things outside this repository
, dirresultMissingAssets :: [MissingAsset]
-- ^ entrypoints of maps which are referred to but missing
, dirresultGraph :: Text
- } deriving (Generic, NFData)
+ } deriving (Generic)
+
+instance NFData (Optional a (Maybe Tiledmap)) => NFData (DirResult a)
+
data MissingDep = MissingDep
{ depFatal :: Maybe Bool
@@ -69,8 +80,14 @@ data MissingDep = MissingDep
newtype MissingAsset = MissingAsset MissingDep
deriving (Generic, NFData)
+
+-- | "shrink" the result by throwing the adjusted tiledmaps away
+shrinkDirResult :: DirResult Full -> DirResult Shrunk
+shrinkDirResult !res =
+ res { dirresultMaps = fmap shrinkMapResult (dirresultMaps res) }
+
-- | given this config, should the result be considered to have failed?
-resultIsFatal :: LintConfig' -> DirResult -> Bool
+resultIsFatal :: LintConfig' -> DirResult Full -> Bool
resultIsFatal config res =
not (null (dirresultMissingAssets res) || not (any (isJust . depFatal) (dirresultDeps res)))
|| maximumLintLevel res > configMaxLintLevel config
@@ -78,11 +95,11 @@ resultIsFatal config res =
-- | maximum lint level that was observed anywhere in any map.
-- note that it really does go through all lints, so don't
-- call it too often
-maximumLintLevel :: DirResult -> Level
+maximumLintLevel :: DirResult a -> Level
maximumLintLevel res
| not (null (dirresultMissingAssets res)) = Fatal
| otherwise =
- (\t -> if null t then Info else maximum t)
+ (maybe Info maximum . nonEmpty)
. map hintLevel
. concatMap (\map -> keys (mapresultLayer map)
<> keys (mapresultTileset map)
@@ -94,7 +111,7 @@ maximumLintLevel res
-instance ToJSON DirResult where
+instance ToJSON (DirResult a) where
toJSON res = A.object [
"result" .= A.object
[ "missingDeps" .= dirresultDeps res
@@ -114,7 +131,7 @@ instance ToJSON MissingAsset where
]
-instance PrettyPrint (Level, DirResult) where
+instance PrettyPrint (Level, DirResult a) where
prettyprint (level, res) = prettyMapLints <> prettyMissingDeps
where
prettyMissingDeps = if not (null (dirresultDeps res))
@@ -122,9 +139,9 @@ instance PrettyPrint (Level, DirResult) where
else ""
prettyMapLints = T.concat
(map prettyLint $ M.toList $ dirresultMaps res)
- prettyLint :: (FilePath, MapResult) -> Text
+ prettyLint :: (FilePath, MapResult a) -> Text
prettyLint (p, lint) =
- "\nin " <> T.pack p <> ":\n" <> prettyprint (level, lint)
+ "\nin " <> toText p <> ":\n" <> prettyprint (level, lint)
instance PrettyPrint MissingDep where
prettyprint (MissingDep _ f n) =
@@ -132,7 +149,7 @@ instance PrettyPrint MissingDep where
<> prettyDependents <> "\n"
where
prettyDependents =
- T.intercalate "," $ map T.pack n
+ T.intercalate "," $ map toText n
-- | check an entire repository
@@ -142,7 +159,7 @@ recursiveCheckDir
-- ^ the repository's prefix (i.e. path to its directory)
-> FilePath
-- ^ the repository's entrypoint (filename of a map, from the repo's root)
- -> IO DirResult
+ -> IO (DirResult Full)
recursiveCheckDir config prefix root = do
maps <- recursiveCheckDir' config prefix [root] mempty
@@ -157,7 +174,7 @@ recursiveCheckDir config prefix root = do
let maps' = flip mapWithKey maps $ \path res ->
if path `elem` nowayback
then res { mapresultGeneral =
- Hint Warning ("Cannot go back to " <> T.pack root <> " from this map.")
+ Hint Warning ("Cannot go back to " <> toText root <> " from this map.")
: mapresultGeneral res
}
else res
@@ -166,15 +183,15 @@ recursiveCheckDir config prefix root = do
pure $ DirResult { dirresultDeps = missingDeps root maps'
, dirresultMissingAssets = mAssets
, dirresultMaps = maps'
- , dirresultGraph = T.pack $ showDot $ graphToDot exitGraph
+ , dirresultGraph = toText $ showDot $ graphToDot exitGraph
}
-- | Given a (partially) completed DirResult, check which local
-- maps are referenced but do not actually exist.
-missingDeps :: FilePath -> Map FilePath MapResult -> [MissingDep]
+missingDeps :: FilePath -> Map FilePath (MapResult a) -> [MissingDep]
missingDeps entrypoint maps =
- let simple = M.insert (T.pack entrypoint) [] used \\ M.union defined trivial
+ let simple = M.insert (toText entrypoint) [] used \\ M.union defined trivial
in M.foldMapWithKey (\f n -> [MissingDep (Just $ not ("#" `isInfixOf` f)) f n]) simple
where
-- which maps are linked somewhere?
@@ -184,19 +201,19 @@ missingDeps entrypoint maps =
(\path v -> map (, [path]) . mapMaybe (extractLocalDeps path) . mapresultDepends $ v)
maps
where extractLocalDeps prefix = \case
- LocalMap name -> Just $ T.pack $ normaliseWithFrag prefix name
+ LocalMap name -> Just $ toText $ normaliseWithFrag prefix name
_ -> Nothing
-- which are defined using startLayer?
defined :: Set Text
defined = setFromList
$ M.foldMapWithKey
- (\k v -> map ((T.pack k <> "#") <>) . mapresultProvides $ v)
+ (\k v -> map ((toText k <> "#") <>) . mapresultProvides $ v)
maps
-- each map file is an entrypoint by itself
- trivial = mapKeys T.pack $ void maps
+ trivial = mapKeys toText $ void maps
-- | Checks if all assets referenced in the result actually exist as files
-missingAssets :: FilePath -> Map FilePath MapResult -> IO [MissingAsset]
+missingAssets :: FilePath -> Map FilePath (MapResult a) -> IO [MissingAsset]
missingAssets prefix maps =
mapM (fmap (fmap (fmap MissingAsset)) missingOfMap) (M.toList maps) <&> fold
where missingOfMap (path, mapres) = mapMaybeM
@@ -204,7 +221,7 @@ missingAssets prefix maps =
let asset = normalise (takeDirectory path) relpath
in doesFileExist (prefix </> asset) <&>
\case True -> Nothing
- False -> Just $ MissingDep Nothing (T.pack asset) [path]
+ False -> Just $ MissingDep Nothing (toText asset) [path]
_ -> pure Nothing)
(mapresultDepends mapres)
@@ -216,9 +233,9 @@ recursiveCheckDir'
-- ^ the repo's directory
-> [FilePath]
-- ^ paths of maps yet to check
- -> Map FilePath MapResult
+ -> Map FilePath (MapResult Full)
-- ^ accumulator for map results
- -> IO (Map FilePath MapResult)
+ -> IO (Map FilePath (MapResult Full))
recursiveCheckDir' config prefix paths acc = do
-- lint all maps in paths. The double fmap skips maps which cause IO errors
diff --git a/lib/CheckMap.hs b/lib/CheckMap.hs
index 9dd0530..3ac03bd 100644
--- a/lib/CheckMap.hs
+++ b/lib/CheckMap.hs
@@ -1,27 +1,30 @@
-{-# LANGUAGE DeriveAnyClass #-}
-{-# LANGUAGE DeriveGeneric #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BangPatterns #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE KindSignatures #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE UndecidableInstances #-}
-- | Module that contains the high-level checking functions
-module CheckMap (loadAndLintMap, MapResult(..)) where
+module CheckMap (loadAndLintMap, MapResult(..), ResultKind(..), Optional,shrinkMapResult) where
+
+import Universum
import Data.Aeson (ToJSON (toJSON))
import qualified Data.Aeson as A
import Data.Aeson.Types ((.=))
-import Data.Functor ((<&>))
-import Data.Map (Map, toList)
import qualified Data.Map as M
-import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Vector as V
-import GHC.Generics (Generic)
import Badges (Badge)
-import Control.DeepSeq (NFData)
import LintConfig (LintConfig')
import LintWriter (LintResult, invertLintResult,
resultToAdjusted, resultToBadges,
@@ -39,9 +42,14 @@ import Types (Dep (MapLink),
import Util (PrettyPrint (prettyprint), prettyprint)
+data ResultKind = Full | Shrunk
+
+type family Optional (a :: ResultKind) (b :: *) where
+ Optional Full b = b
+ Optional Shrunk b = ()
-- | What this linter produces: lints for a single map
-data MapResult = MapResult
+data MapResult (kind :: ResultKind) = MapResult
{ mapresultLayer :: Map Hint [Text]
-- ^ lints that occurred in one or more layers
, mapresultTileset :: Map Hint [Text]
@@ -50,15 +58,18 @@ data MapResult = MapResult
-- ^ (external and local) dependencies of this map
, mapresultProvides :: [Text]
-- ^ entrypoints provided by this map (needed for dependency checking)
- , mapresultAdjusted :: Maybe Tiledmap
+ , mapresultAdjusted :: Optional kind (Maybe Tiledmap)
-- ^ the loaded map, with adjustments by the linter
, mapresultBadges :: [Badge]
-- ^ badges that can be found on this map
, mapresultGeneral :: [Hint]
-- ^ general-purpose lints that didn't fit anywhere else
- } deriving (Generic, NFData)
+ } deriving (Generic)
+
+instance NFData (Optional a (Maybe Tiledmap)) => NFData (MapResult a)
-instance ToJSON MapResult where
+
+instance ToJSON (MapResult a) where
toJSON res = A.object
[ "layer" .= CollectedLints (mapresultLayer res)
, "tileset" .= CollectedLints (mapresultTileset res)
@@ -76,13 +87,16 @@ instance ToJSON CollectedLints where
else cs
+shrinkMapResult :: MapResult Full -> MapResult Shrunk
+shrinkMapResult !res = res { mapresultAdjusted = () }
+
-- | this module's raison d'être
-- Lints the map at `path`, and limits local links to at most `depth`
-- layers upwards in the file hierarchy
-loadAndLintMap :: LintConfig' -> FilePath -> Int -> IO (Maybe MapResult)
+loadAndLintMap :: LintConfig' -> FilePath -> Int -> IO (Maybe (MapResult Full))
loadAndLintMap config path depth = loadTiledmap path <&> (\case
DecodeErr err -> Just (MapResult mempty mempty mempty mempty Nothing mempty
- [ Hint Fatal . T.pack $
+ [ Hint Fatal . toText $
path <> ": Fatal: " <> err
])
IOErr _ -> Nothing
@@ -90,7 +104,7 @@ loadAndLintMap config path depth = loadTiledmap path <&> (\case
Just (runLinter (takeFileName path == "main.json") config waMap depth))
-- | lint a loaded map
-runLinter :: Bool -> LintConfig' -> Tiledmap -> Int -> MapResult
+runLinter :: Bool -> LintConfig' -> Tiledmap -> Int -> MapResult Full
runLinter isMain config tiledmap depth = MapResult
{ mapresultLayer = invertThing layer
, mapresultTileset = invertThing tileset
@@ -174,7 +188,7 @@ checkLayerRec config depth layers =
-- human-readable lint output, e.g. for consoles
-instance PrettyPrint (Level, MapResult) where
+instance PrettyPrint (Level, MapResult a) where
prettyprint (_, mapResult) = if complete == ""
then " all good!\n" else complete
where
@@ -185,7 +199,7 @@ instance PrettyPrint (Level, MapResult) where
-- | pretty-prints a collection of Hints, printing each
-- Hint only once, then a list of its occurences line-wrapped
-- to fit onto a decent-sized terminal
- prettyLints :: (MapResult -> Map Hint [Text]) -> [Text]
+ prettyLints :: (MapResult a -> Map Hint [Text]) -> [Text]
prettyLints getter = fmap
(\(h, cs) -> prettyprint h
<> "\n (in "
@@ -197,7 +211,7 @@ instance PrettyPrint (Level, MapResult) where
)
(0, "") cs)
<> ")\n")
- (toList . getter $ mapResult)
+ (M.toList . getter $ mapResult)
prettyGeneral :: [Text]
prettyGeneral = map
diff --git a/lib/Dirgraph.hs b/lib/Dirgraph.hs
index b97a644..4873228 100644
--- a/lib/Dirgraph.hs
+++ b/lib/Dirgraph.hs
@@ -1,23 +1,21 @@
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE TupleSections #-}
-- | Simple directed graphs, for dependency checking
module Dirgraph where
+import Universum
import CheckMap (MapResult (mapresultDepends))
-import Control.Monad (forM_)
-import Data.Functor ((<&>))
-import Data.Map.Strict (Map, mapMaybeWithKey, mapWithKey,
- traverseWithKey)
+import Data.Map.Strict (mapMaybeWithKey, mapWithKey, traverseWithKey)
import qualified Data.Map.Strict as M
-import Data.Set (Set, (\\))
+import Data.Set ((\\))
import qualified Data.Set as S
import Paths (normalise)
import Text.Dot (Dot, (.->.))
import qualified Text.Dot as D
import Types (Dep (LocalMap))
-import Witherable (mapMaybe)
-- | a simple directed graph
type Graph a = Map a (Set a)
@@ -26,7 +24,7 @@ nodes :: Graph a -> Set a
nodes = M.keysSet
-- | simple directed graph of exits
-resultToGraph :: Map FilePath MapResult -> Graph FilePath
+resultToGraph :: Map FilePath (MapResult a) -> Graph FilePath
resultToGraph = fmap (S.fromList . mapMaybe onlyLocalMaps . mapresultDepends)
where onlyLocalMaps = \case
LocalMap path -> Just (normalise "" path)
@@ -35,7 +33,7 @@ resultToGraph = fmap (S.fromList . mapMaybe onlyLocalMaps . mapresultDepends)
-- | invert edges of a directed graph
invertGraph :: (Eq a, Ord a) => Graph a -> Graph a
invertGraph graph = mapWithKey collectFroms graph
- where collectFroms to _ = S.fromList . M.elems . mapMaybeWithKey (select to) $ graph
+ where collectFroms to _ = S.fromList . elems . mapMaybeWithKey (select to) $ graph
select to from elems = if to `elem` elems then Just from else Nothing
-- | all nodes reachable from some entrypoint
diff --git a/lib/KindLinter.hs b/lib/KindLinter.hs
index ccca1db..a876a8f 100644
--- a/lib/KindLinter.hs
+++ b/lib/KindLinter.hs
@@ -13,11 +13,10 @@
module KindLinter where
+import Universum
+
import Data.HList
-import Data.Kind (Type)
-import Data.Map.Strict
-import Data.Void (Void)
-import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
+import GHC.TypeLits (KnownSymbol, Symbol, symbolVal)
func :: a -> HList [Int, String]
diff --git a/lib/LayerData.hs b/lib/LayerData.hs
index 1a07982..6956c92 100644
--- a/lib/LayerData.hs
+++ b/lib/LayerData.hs
@@ -2,12 +2,12 @@
module LayerData where
+import Universum hiding (maximum, uncons)
import Control.Monad.Zip (mzipWith)
-import Data.Set (Set, insert)
-import Data.Text (Text)
-import qualified Data.Text as T
-import Data.Vector (Vector, uncons)
+import Data.Set (insert)
+import Data.Vector (maximum, uncons)
+import qualified Text.Show as TS
import Tiled (GlobalId (unGlobalId), Layer (..))
import Util (PrettyPrint (..))
@@ -22,8 +22,8 @@ instance Eq Collision where
instance PrettyPrint Collision where
prettyprint (Collision (a,b)) = a <> " and " <> b
-instance Show Collision where
- show c = T.unpack $ prettyprint c
+instance TS.Show Collision where
+ show c = toString $ prettyprint c
-- | Finds pairwise tile collisions between the given layers.
layerOverlaps :: Vector Layer -> Set Collision
diff --git a/lib/LintConfig.hs b/lib/LintConfig.hs
index e71638b..11a8122 100644
--- a/lib/LintConfig.hs
+++ b/lib/LintConfig.hs
@@ -12,21 +12,21 @@
-- | Module that deals with handling config options
module LintConfig (LintConfig(..), LintConfig', patchConfig) where
-import Control.Monad.Identity (Identity)
-import Data.Aeson (FromJSON (parseJSON), Options (..),
- defaultOptions, eitherDecode)
-import Data.Aeson.Types (genericParseJSON)
-import qualified Data.ByteString.Char8 as C8
-import qualified Data.ByteString.Lazy as LB
-import qualified Data.Map.Strict as M
-import Data.Text (Text)
-import GHC.Generics (Generic (Rep, from, to), K1 (..),
- M1 (..), (:*:) (..))
-import Types (Level)
-import Uris (SchemaSet,
- Substitution (DomainSubstitution))
-import WithCli (Proxy (..))
-import WithCli.Pure (Argument (argumentType, parseArgument))
+import Universum
+import qualified Universum.Unsafe as Unsafe
+
+import Data.Aeson (FromJSON (parseJSON), Options (..),
+ defaultOptions, eitherDecode)
+import Data.Aeson.Types (genericParseJSON)
+import qualified Data.ByteString.Char8 as C8
+import qualified Data.ByteString.Lazy as LB
+import qualified Data.Map.Strict as M
+import GHC.Generics (Generic (Rep, from, to), K1 (..),
+ M1 (..), (:*:) (..))
+import Types (Level)
+import Uris (SchemaSet,
+ Substitution (DomainSubstitution))
+import WithCli.Pure (Argument (argumentType, parseArgument))
type family HKD f a where
HKD Identity a = a
@@ -138,7 +138,7 @@ patchConfig config p = config'
DomainSubstitution (M.fromList generated) scope
where generated = (\slug -> (slug, "/@/rc3_21/"<>slug)) <$> configAssemblies config'
scope = (\(DomainSubstitution _ s) -> s)
- . snd . head
+ . snd . Unsafe.head
. filter ((==) "world" . fst)
$ configUriSchemas config'
diff --git a/lib/LintWriter.hs b/lib/LintWriter.hs
index 2b891c3..d0c6c4e 100644
--- a/lib/LintWriter.hs
+++ b/lib/LintWriter.hs
@@ -42,21 +42,15 @@ module LintWriter
, adjust
) where
-import Data.Text (Text)
-
-import Badges (Badge)
-import Control.Monad.State (MonadState (put), StateT, modify)
-import Control.Monad.Trans.Reader (Reader, asks, runReader)
-import Control.Monad.Trans.State (get, runStateT)
-import Control.Monad.Writer.Lazy (lift)
-import Data.Bifunctor (Bifunctor (second))
-import Data.Map (Map, fromListWith)
-import Data.Maybe (mapMaybe)
-import qualified Data.Set as S
-import LintConfig (LintConfig')
-import TiledAbstract (HasName (getName))
-import Types (Dep, Hint, Level (..), Lint (..),
- hint, lintsToHints)
+import Universum
+
+
+import Badges (Badge)
+import Data.Map (fromListWith)
+import LintConfig (LintConfig')
+import TiledAbstract (HasName (getName))
+import Types (Dep, Hint, Level (..), Lint (..), hint,
+ lintsToHints)
-- | A monad modelling the main linter features
@@ -109,7 +103,7 @@ zoom embed extract operation = do
-- | "invert" a linter's result, grouping lints by their messages
invertLintResult :: HasName ctxt => LintResult ctxt -> Map Hint [Text]
invertLintResult (LinterState (lints, ctxt)) =
- fmap (S.toList . S.fromList . fmap getName) . fromListWith (<>) $ (, [ctxt]) <$> lintsToHints lints
+ fmap (sortNub . map getName) . fromListWith (<>) $ (, [ctxt]) <$> lintsToHints lints
resultToDeps :: LintResult a -> [Dep]
resultToDeps (LinterState (lints,_)) = mapMaybe lintToDep lints
diff --git a/lib/Paths.hs b/lib/Paths.hs
index 15dc66b..f4dc3ed 100644
--- a/lib/Paths.hs
+++ b/lib/Paths.hs
@@ -7,15 +7,16 @@
-- I just hope you are running this on some kind of Unix
module Paths where
-import Control.DeepSeq (NFData)
-import Data.Text (Text, isPrefixOf)
+import Universum
+import qualified Universum.Unsafe as Unsafe
+
import qualified Data.Text as T
-import GHC.Generics (Generic)
import System.FilePath (splitPath)
import System.FilePath.Posix ((</>))
import Text.Regex.TDFA
import Util (PrettyPrint (prettyprint))
+
-- | a normalised path: a number of "upwards" steps, and
-- a path without any . or .. in it. Also possibly a
-- fragment, mostly for map links.
@@ -36,9 +37,9 @@ parsePath :: Text -> PathResult
parsePath text =
if | T.isInfixOf "{{" text || T.isInfixOf "}}" text -> PathVarsDisallowed
| rest =~ ("^([^/]*[^\\./]/)*[^/]*[^\\./]$" :: Text) -> OkRelPath (Path up path fragment)
- | "/_/" `isPrefixOf` text -> UnderscoreMapLink
- | "/@/" `isPrefixOf` text -> AtMapLink
- | "/" `isPrefixOf` text -> AbsolutePath
+ | "/_/" `T.isPrefixOf` text -> UnderscoreMapLink
+ | "/@/" `T.isPrefixOf` text -> AtMapLink
+ | "/" `T.isPrefixOf` text -> AbsolutePath
| otherwise -> NotAPath
where
(_, prefix, rest, _) =
@@ -47,10 +48,10 @@ parsePath text =
up = length . filter (".." ==) . T.splitOn "/" $ prefix
parts = T.splitOn "#" rest
-- `head` is unsafe, but splitOn will always produce lists with at least one element
- path = head parts
- fragment = if length parts >= 2
- then Just $ T.concat $ tail parts
- else Nothing
+ path = Unsafe.head parts
+ fragment = case nonEmpty parts of
+ Nothing -> Nothing
+ Just p -> Just $ T.concat $ tail p
instance PrettyPrint RelPath where
prettyprint (Path up rest frag) = ups <> rest <> fragment
@@ -63,14 +64,14 @@ instance PrettyPrint RelPath where
-- at the end of the prefix, i.e. it will never return paths
-- that lie (naïvely) outside of the prefix.
normalise :: FilePath -> RelPath -> FilePath
-normalise prefix (Path 0 path _) = prefix </> T.unpack path
+normalise prefix (Path 0 path _) = prefix </> toString path
normalise prefix (Path i path _) =
- concat (take (length dirs - i) dirs) </> T.unpack path
+ concat (take (length dirs - i) dirs) </> toString path
where dirs = splitPath prefix
normaliseWithFrag :: FilePath -> RelPath -> FilePath
normaliseWithFrag prefix (Path i path frag) =
- normalise prefix (Path (i+1) path frag) <> T.unpack (maybe mempty ("#" <>) frag)
+ normalise prefix (Path (i+1) path frag) <> toString (maybe mempty ("#" <>) frag)
-- | does this path contain an old-style pattern for inter-repository
-- links as was used at rc3 in 2020?
@@ -81,7 +82,5 @@ isOldStyle (Path _ text frag) = path =~ ("{<.+>*}" :: Text)
_ -> text
getExtension :: RelPath -> Text
-getExtension (Path _ text _) = case length splitted of
- 0 -> ""
- _ -> last splitted
+getExtension (Path _ text _) = maybe "" last (nonEmpty splitted)
where splitted = T.splitOn "." text
diff --git a/lib/Properties.hs b/lib/Properties.hs
index 87b2a28..5d9c094 100644
--- a/lib/Properties.hs
+++ b/lib/Properties.hs
@@ -1,18 +1,21 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE MultiWayIf #-}
-{-# LANGUAGE NamedFieldPuns #-}
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RecordWildCards #-}
-{-# LANGUAGE TupleSections #-}
-{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE MultiWayIf #-}
+{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE TupleSections #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
-- | Contains checks for custom ties of the map json
module Properties (checkMap, checkTileset, checkLayer) where
+import Universum hiding (intercalate, isPrefixOf)
-import Control.Monad (forM, forM_, unless, when)
-import Data.Text (Text, intercalate, isInfixOf, isPrefixOf)
+import Data.Text (intercalate, isInfixOf, isPrefixOf)
import qualified Data.Text as T
import qualified Data.Vector as V
import Tiled (Layer (..), Object (..), Property (..),
@@ -21,19 +24,14 @@ import Tiled (Layer (..), Object (..), Property (..),
import TiledAbstract (HasName (..), HasProperties (..),
HasTypeName (..), IsProperty (..))
import Util (layerIsEmpty, mkProxy, naiveEscapeHTML,
- prettyprint, showText)
+ prettyprint)
import Badges (Badge (Badge),
BadgeArea (BadgePoint, BadgeRect),
BadgeToken, parseToken)
-import Data.Data (Proxy (Proxy))
-import Data.Functor ((<&>))
import Data.List ((\\))
-import Data.Maybe (fromMaybe, isJust)
-import Data.Set (Set)
import qualified Data.Set as S
import Data.Text.Metrics (damerauLevenshtein)
-import Data.Vector (Vector)
import GHC.TypeLits (KnownSymbol)
import LayerData (Collision, layerOverlaps)
import LintConfig (LintConfig (..))
@@ -85,7 +83,7 @@ checkMap = do
let unlessLayer = unlessElement layers
-- test custom map properties
- mapM_ checkMapProperty (fromMaybe mempty $ tiledmapProperties tiledmap)
+ mapM_ checkMapProperty (maybeToMonoid $ tiledmapProperties tiledmap)
-- can't have these with the rest of layer/tileset lints since they're
-- not specific to any one of them
@@ -143,10 +141,10 @@ checkMapProperty p@(Property name _) = case name of
-- scripts can be used by one map
_ | T.toLower name == "script" ->
unwrapString p $ \str ->
- unless ((checkIsRc3Url str) &&
- (not $ "/../" `isInfixOf` str) &&
- (not $ "%" `isInfixOf` str) &&
- (not $ "@" `isInfixOf` str))
+ unless (checkIsRc3Url str &&
+ not ( "/../" `isInfixOf` str) &&
+ not ( "%" `isInfixOf` str) &&
+ not ( "@" `isInfixOf` str))
$ forbid "only scripts hosted on static.rc3.world are allowed."
| name `elem` ["jitsiRoom", "bbbRoom", "playAudio", "openWebsite"
, "url", "exitUrl", "silent", "getBadge"]
@@ -185,7 +183,7 @@ checkTileset = do
$ complain "The \"filename\" property on tilesets was removed; use \"image\" instead (and perhaps a newer version of the Tiled Editor)."
-- check individual tileset properties
- mapM_ checkTilesetProperty (fromMaybe mempty $ tilesetProperties tileset)
+ mapM_ checkTilesetProperty (maybeToMonoid $ tilesetProperties tileset)
case tilesetTiles tileset of
Nothing -> pure ()
@@ -193,7 +191,7 @@ checkTileset = do
-- can't set properties on the same tile twice
refuseDoubledThings tileId
(\tile -> complain $ "cannot set properties on the \
- \tile with the id" <> showText (tileId tile) <> "twice.")
+ \tile with the id" <> show (tileId tile) <> "twice.")
tiles
mapM_ checkTile tiles
@@ -217,7 +215,7 @@ checkTileset = do
\not an individual tile."
_ -> warnUnknown' ("unknown tile property "
<> prettyprint name <> " in tile with global id "
- <> showText (tileId tile)) p knownTilesetProperties
+ <> show (tileId tile)) p knownTilesetProperties
-- | collect lints on a single map layer
@@ -252,14 +250,14 @@ checkLayer = do
forM_ (getProperties layer) checkObjectGroupProperty
unless (layerName layer == "floorLayer") $
- when (null (layerObjects layer) || layerObjects layer == Just mempty) $
+ when (isNothing (layerObjects layer) || layerObjects layer == Just mempty) $
warn "objectgroup layer (which aren't the floorLayer) \
\are useless if they are empty."
ty -> complain $ "unsupported layer type " <> prettyprint ty <> "."
if layerType layer == "group"
- then when (null (layerLayers layer))
+ then when (isNothing (layerLayers layer))
$ warn "Empty group layers are pointless."
else when (isJust (layerLayers layer))
$ complain "Layer is not of type \"group\", but has sublayers."
@@ -310,7 +308,7 @@ checkObjectProperty p@(Property name _) = do
unless (objectType obj == "variable") $
complain $ "the "<>prettyprint name<>" property should only be set \
\on objects of type \"variable\""
- when (null (objectName obj) || objectName obj == Just mempty) $
+ when (isNothing (objectName obj) || objectName obj == Just mempty) $
complain $ "Objects with the property "<>prettyprint name<>" set must \
\be named."
| name `elem` [ "openSound", "closeSound", "bellSound", "loadSound" ] -> do
@@ -504,7 +502,7 @@ checkTileLayerProperty p@(Property name _value) = case name of
requireProperty req = propertyRequiredBy req name
requireOneOf names = do
context <- askContext
- when (all (not . containsProperty context) names)
+ unless (any (containsProperty context) names)
$ complain $ "property " <> prettyprint name <> " requires one of "
<> prettyprint names
@@ -528,9 +526,8 @@ checkTileLayerProperty p@(Property name _value) = case name of
-- | refuse doubled names in everything that's somehow a collection of names
refuseDoubledNames
- :: (HasName a, HasTypeName a)
- => (Foldable t, Functor t)
- => t a
+ :: (Container t, HasName (Element t), HasTypeName (Element t))
+ => t
-> LintWriter b
refuseDoubledNames = refuseDoubledThings
getName
@@ -539,10 +536,10 @@ refuseDoubledNames = refuseDoubledThings
-- | refuse doubled things via equality on after applying some function
refuseDoubledThings
- :: (Eq a, Ord a, Foldable t, Functor t)
- => (a' -> a)
- -> (a' -> LintWriter b)
- -> t a'
+ :: (Eq a, Ord a, Container t)
+ => (Element t -> a)
+ -> (Element t -> LintWriter b)
+ -> t
-> LintWriter b
refuseDoubledThings f ifDouble things = foldr folding base things (mempty, mempty)
where
@@ -570,15 +567,15 @@ warnUnknown p@(Property name _) =
---- General functions ----
unlessElement
- :: Foldable f
- => f a
- -> (a -> Bool)
+ :: Container f
+ => f
+ -> (Element f -> Bool)
-> LintWriter b
-> LintWriter b
unlessElement things op = unless (any op things)
-unlessElementNamed :: (HasName a, Foldable f)
- => f a -> Text -> LintWriter b -> LintWriter b
+unlessElementNamed :: (HasName (Element f), Container f)
+ => f -> Text -> LintWriter b -> LintWriter b
unlessElementNamed things name =
unlessElement things ((==) name . getName)
@@ -756,4 +753,4 @@ isOrdInRange :: (Ord a, Show a)
isOrdInRange unwrapa l r p@(Property name _) = unwrapa p $ \int ->
if l < int && int < r then pure ()
else complain $ "Property " <> prettyprint name <> " should be between "
- <> showText l <> " and " <> showText r<>"."
+ <> show l <> " and " <> show r<>"."
diff --git a/lib/Tiled.hs b/lib/Tiled.hs
index ab7d4f4..3162dfd 100644
--- a/lib/Tiled.hs
+++ b/lib/Tiled.hs
@@ -17,21 +17,15 @@
-- http://doc.mapeditor.org/en/latest/reference/tmx-map-format/
module Tiled where
-import Control.DeepSeq (NFData)
-import Control.Exception (try)
-import Control.Exception.Base (SomeException)
-import Data.Aeson hiding (Object)
-import qualified Data.Aeson as A
-import Data.Aeson.Types (typeMismatch)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as LB
-import Data.Char (toLower)
-import Data.Map (Map)
-import Data.String (IsString (fromString))
-import Data.Text (Text)
-import qualified Data.Text as T
-import Data.Vector (Vector)
-import GHC.Generics (Generic)
+import Universum
+
+-- TODO: what ever are these aeson imports
+import Data.Aeson hiding (Object)
+import qualified Data.Aeson as A
+import Data.Aeson.Types (typeMismatch)
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as LB
+import Data.Char (toLower)
-- | options for Aeson's generic encoding and parsing functions
@@ -68,7 +62,7 @@ data PropertyValue = StrProp Text | BoolProp Bool | IntProp Int | FloatProp Floa
deriving (Eq, Generic, Show, NFData)
instance IsString PropertyValue where
- fromString s = StrProp (T.pack s)
+ fromString s = StrProp (toText s)
instance FromJSON Property where
parseJSON (A.Object o) = do
diff --git a/lib/TiledAbstract.hs b/lib/TiledAbstract.hs
index 6d58f46..9fd2df0 100644
--- a/lib/TiledAbstract.hs
+++ b/lib/TiledAbstract.hs
@@ -2,9 +2,8 @@
module TiledAbstract where
-import Data.Maybe (fromMaybe)
-import Data.Proxy (Proxy)
-import Data.Text (Text)
+import Universum
+
import qualified Data.Vector as V
import Tiled (Layer (..), Object (..), Property (..),
PropertyValue (..), Tile (..), Tiledmap (..),
@@ -15,27 +14,27 @@ class HasProperties a where
adjustProperties :: ([Property] -> Maybe [Property]) -> a -> a
instance HasProperties Layer where
- getProperties = fromMaybe mempty . layerProperties
+ getProperties = maybeToMonoid . layerProperties
adjustProperties f layer = layer
{ layerProperties = f (getProperties layer) }
instance HasProperties Tileset where
- getProperties = fromMaybe mempty . tilesetProperties
+ getProperties = maybeToMonoid . tilesetProperties
adjustProperties f tileset = tileset
{ tilesetProperties = f (getProperties tileset) }
instance HasProperties Tile where
- getProperties = V.toList . fromMaybe mempty . tileProperties
+ getProperties = V.toList . maybeToMonoid . tileProperties
adjustProperties f tile = tile
{ tileProperties = (fmap V.fromList . f) (getProperties tile) }
instance HasProperties Object where
- getProperties = V.toList . fromMaybe mempty . objectProperties
+ getProperties = V.toList . maybeToMonoid . objectProperties
adjustProperties f obj = obj
{ objectProperties = (fmap V.fromList . f) (getProperties obj) }
instance HasProperties Tiledmap where
- getProperties = fromMaybe mempty . tiledmapProperties
+ getProperties = maybeToMonoid . tiledmapProperties
adjustProperties f tiledmap = tiledmap
{ tiledmapProperties = f (getProperties tiledmap) }
diff --git a/lib/Types.hs b/lib/Types.hs
index 43a5131..f58705a 100644
--- a/lib/Types.hs
+++ b/lib/Types.hs
@@ -17,20 +17,17 @@ module Types
, lintsToHints
) where
+import Universum
+
import Control.Monad.Trans.Maybe ()
import Data.Aeson (FromJSON, ToJSON (toJSON),
ToJSONKey, (.=))
-import Data.Text (Text)
-import GHC.Generics (Generic)
import Badges (Badge)
-import Control.DeepSeq (NFData)
import qualified Data.Aeson as A
-import Data.Maybe (mapMaybe)
import Paths (RelPath)
-import Util (PrettyPrint (..), showText)
-import WithCli (Argument, Proxy (..),
- atomicArgumentsParser)
+import Util (PrettyPrint (..))
+import WithCli (Argument, atomicArgumentsParser)
import WithCli.Pure (Argument (argumentType, parseArgument),
HasArguments (argumentsParser))
@@ -83,7 +80,7 @@ lintsToHints = mapMaybe (\case {Lint hint -> Just hint ; _ -> Nothing})
instance PrettyPrint Lint where
prettyprint (Lint Hint { hintMsg, hintLevel } ) =
- " " <> showText hintLevel <> ": " <> hintMsg
+ " " <> show hintLevel <> ": " <> hintMsg
prettyprint (Depends dep) =
" Info: found dependency: " <> prettyprint dep
prettyprint (Offers dep) =
@@ -92,7 +89,7 @@ instance PrettyPrint Lint where
" Info: found a badge."
instance PrettyPrint Hint where
- prettyprint (Hint level msg) = " " <> showText level <> ": " <> msg
+ prettyprint (Hint level msg) = " " <> show level <> ": " <> msg
instance ToJSON Lint where
toJSON (Lint h) = toJSON h
diff --git a/lib/Uris.hs b/lib/Uris.hs
index 00f86a4..a8c7068 100644
--- a/lib/Uris.hs
+++ b/lib/Uris.hs
@@ -8,26 +8,16 @@
-- | Functions to deal with uris and custom uri schemes
module Uris where
+import Universum
-
-import Control.Monad (unless, when)
-import Data.Aeson (FromJSON (..), Options (..),
- SumEncoding (UntaggedValue),
- defaultOptions, genericParseJSON)
-import Data.Data (Proxy)
-import Data.Either.Combinators (maybeToRight, rightToMaybe)
-import Data.Map.Strict (Map)
-import qualified Data.Map.Strict as M
-import Data.Text (Text, pack, unpack)
-import qualified Data.Text as T
-import GHC.Generics (Generic)
-import GHC.TypeLits (KnownSymbol, symbolVal)
-import Network.URI.Encode as URI
-import Text.Regex.TDFA ((=~))
-import Witherable (mapMaybe)
-
-import Data.String
-import Network.URI as NativeUri
+import Data.Aeson (FromJSON (..), Options (..),
+ SumEncoding (UntaggedValue),
+ defaultOptions, genericParseJSON)
+import qualified Data.Map.Strict as M
+import qualified Data.Text as T
+import GHC.TypeLits (KnownSymbol, symbolVal)
+import Network.URI (URI (..), URIAuth (..), parseURI)
+import qualified Network.URI.Encode as URI
data Substitution =
Prefixed { prefix :: Text, blocked :: [Text], allowed :: [Text], scope :: [String] }
@@ -56,7 +46,7 @@ extractDomain url =
parseUri :: Text -> Maybe (Text, Text, Text)
parseUri uri =
- case parseURI (unpack uri) of
+ case parseURI (toString uri) of
Nothing -> Nothing
Just parsedUri -> case uriAuthority parsedUri of
Nothing -> Nothing
@@ -84,15 +74,15 @@ data SubstError =
applySubsts :: KnownSymbol s
=> Proxy s -> SchemaSet -> Text -> Either SubstError Text
applySubsts s substs uri = do
- when (T.isInfixOf (pack "{{") uri || T.isInfixOf (pack "}}") uri)
+ when (T.isInfixOf (toText "{{") uri || T.isInfixOf (toText "}}") uri)
$ Left VarsDisallowed
parts@(schema, _, _) <- note NotALink $ parseUri uri
let rules = filter ((==) schema . fst) substs
- case fmap (applySubst parts . snd) rules of
- [] -> Left (SchemaDoesNotExist schema)
- results@(_:_) -> case mapMaybe rightToMaybe results of
+ case nonEmpty (map (applySubst parts . snd) rules) of
+ Nothing -> Left (SchemaDoesNotExist schema)
+ Just results -> case rights (toList results) of
suc:_ -> Right suc
_ -> minimum results
@@ -104,14 +94,14 @@ applySubsts s substs uri = do
(fmap fst . filter (elem (symbolVal s) . scope . snd) $ substs))
case rule of
DomainSubstitution table _ -> do
- prefix <- note (DomainDoesNotExist (schema <> pack "://" <> domain))
+ prefix <- note (DomainDoesNotExist (schema <> toText "://" <> domain))
$ M.lookup domain table
pure (prefix <> rest)
Prefixed {..}
| domain `elem` blocked -> Left IsBlocked
- | domain `elem` allowed || pack "streamproxy.rc3.world" `T.isSuffixOf` domain -> Right uri
+ | domain `elem` allowed || toText "streamproxy.rc3.world" `T.isSuffixOf` domain -> Right uri
| otherwise -> Right (prefix <> URI.encodeText uri)
Allowed _ domains -> if domain `elem` domains
- || pack "streamproxy.rc3.world" `T.isSuffixOf` domain
+ || toText "streamproxy.rc3.world" `T.isSuffixOf` domain
then Right uri
else Left (DomainIsBlocked domains)
diff --git a/lib/Util.hs b/lib/Util.hs
index 21a2661..f935e78 100644
--- a/lib/Util.hs
+++ b/lib/Util.hs
@@ -6,30 +6,23 @@
-- concerns itself with wrangling haskell's string types
module Util
( mkProxy
- , showText
, PrettyPrint(..)
, printPretty
, naiveEscapeHTML
, layerIsEmpty
) where
+import Universum
+
import Data.Aeson as Aeson
-import Data.Proxy (Proxy (..))
-import Data.Set (Set)
import qualified Data.Set as S
-import Data.Text (Text)
import qualified Data.Text as T
import Tiled (Layer (layerData), PropertyValue (..),
Tileset (tilesetName), layerName, mkTiledId)
--- | helper function to create proxies
mkProxy :: a -> Proxy a
mkProxy = const Proxy
--- | haskell's many string types are FUN …
-showText :: Show a => a -> Text
-showText = T.pack . show
-
-- | a class to address all the string conversions necessary
-- when using Show to much that just uses Text instead
class PrettyPrint a where
@@ -43,7 +36,7 @@ instance PrettyPrint Text where
instance PrettyPrint Aeson.Value where
prettyprint = \case
Aeson.String s -> prettyprint s
- v -> (T.pack . show) v
+ v -> show v
instance PrettyPrint t => PrettyPrint (Set t) where
prettyprint = prettyprint . S.toList
@@ -52,8 +45,8 @@ instance PrettyPrint PropertyValue where
prettyprint = \case
StrProp str -> str
BoolProp bool -> if bool then "true" else "false"
- IntProp int -> showText int
- FloatProp float -> showText float
+ IntProp int -> show int
+ FloatProp float -> show float
-- | here since Unit is sometimes used as dummy type
instance PrettyPrint () where
@@ -69,7 +62,7 @@ instance PrettyPrint a => PrettyPrint [a] where
prettyprint = T.intercalate ", " . fmap prettyprint
printPretty :: PrettyPrint a => a -> IO ()
-printPretty = putStr . T.unpack . prettyprint
+printPretty = putStr . toString . prettyprint
layerIsEmpty :: Layer -> Bool
diff --git a/lib/WriteRepo.hs b/lib/WriteRepo.hs
index 36c0df7..e4815fe 100644
--- a/lib/WriteRepo.hs
+++ b/lib/WriteRepo.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE DataKinds #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
@@ -5,14 +6,11 @@
-- | Module for writing an already linted map Repository back out again.
module WriteRepo (writeAdjustedRepository) where
+import Universum
+
import CheckDir (DirResult (..), resultIsFatal)
-import CheckMap (MapResult (..))
-import Control.Monad (forM_, unless)
-import Control.Monad.Extra (ifM)
+import CheckMap (MapResult (..), ResultKind (..))
import Data.Aeson (encodeFile)
-import Data.Map.Strict (toList)
-import Data.Maybe (mapMaybe)
-import Data.Set (Set)
import qualified Data.Set as S
import LintConfig (LintConfig (configDontCopyAssets),
LintConfig')
@@ -27,7 +25,7 @@ import Types (Dep (Local))
-writeAdjustedRepository :: LintConfig' -> FilePath -> FilePath -> DirResult -> IO ExitCode
+writeAdjustedRepository :: LintConfig' -> FilePath -> FilePath -> DirResult Full -> IO ExitCode
writeAdjustedRepository config inPath outPath result
| resultIsFatal config result =
pure (ExitFailure 1)
@@ -36,7 +34,7 @@ writeAdjustedRepository config inPath outPath result
createDirectoryIfMissing True outPath
-- write out all maps
- forM_ (toList $ dirresultMaps result) $ \(path,out) -> do
+ forM_ (toPairs $ dirresultMaps result) $ \(path,out) -> do
createDirectoryIfMissing True (takeDirectory (outPath </> path))
encodeFile (outPath </> path) $ mapresultAdjusted out
@@ -51,7 +49,7 @@ writeAdjustedRepository config inPath outPath result
Local path -> Just . normalise mapdir $ path
_ -> Nothing)
$ mapresultDepends mapresult)
- . toList $ dirresultMaps result
+ . toPairs $ dirresultMaps result
-- copy all assets
forM_ localdeps $ \path ->
diff --git a/package.yaml b/package.yaml
index 7cd6130..4acf9e8 100644
--- a/package.yaml
+++ b/package.yaml
@@ -6,9 +6,11 @@ author: stuebinm
maintainer: stuebinm@disroot.org
copyright: 2022 stuebinm
ghc-options: -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors
+default-extensions: NoImplicitPrelude
dependencies:
- base
+ - universum
- aeson
- bytestring
- mtl
@@ -54,8 +56,7 @@ executables:
walint-server:
main: Main.hs
source-dirs: 'server'
- default-extensions:
- - NoImplicitPrelude
+ ghc-options: -rtsopts -threaded
dependencies:
- walint
- universum
diff --git a/server/HtmlOrphans.hs b/server/HtmlOrphans.hs
index ebe65aa..9b09f1d 100644
--- a/server/HtmlOrphans.hs
+++ b/server/HtmlOrphans.hs
@@ -108,7 +108,7 @@ headerText = \case
-- | The fully monky
-instance ToHtml DirResult where
+instance ToHtml (DirResult a) where
toHtml res@DirResult { .. } = do
p_ $ do badge maxlevel "Linted:"; " "; headerText maxlevel
diff --git a/server/Server.hs b/server/Server.hs
index f2b286b..711da88 100644
--- a/server/Server.hs
+++ b/server/Server.hs
@@ -3,6 +3,8 @@
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
+{-# LANGUAGE ExistentialQuantification #-}
+{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
@@ -31,6 +33,7 @@ module Server ( loadConfig
import Universum
import CheckDir (DirResult)
+import CheckMap (ResultKind (Full, Shrunk))
import Control.Arrow ((>>>))
import Control.Concurrent (modifyMVar_, withMVar)
import Crypto.Hash.SHA1 (hash)
@@ -162,7 +165,7 @@ configCodec = Config
-- | a job status (of a specific uuid)
data JobStatus =
- Pending | Linted !DirResult Text | Failed Text
+ Pending | Linted !(DirResult Shrunk) Text | Failed Text
deriving (Generic, ToJSON, NFData)
instance TS.Show JobStatus where
diff --git a/server/Worker.hs b/server/Worker.hs
index 91fa8e2..af07904 100644
--- a/server/Worker.hs
+++ b/server/Worker.hs
@@ -9,7 +9,8 @@ module Worker (linterThread, Job(..)) where
import Universum
-import CheckDir (recursiveCheckDir)
+import CheckDir (recursiveCheckDir,
+ shrinkDirResult)
import Control.Concurrent.Async (async, link)
import Control.Concurrent.STM.TQueue
import Control.Exception (IOException, handle)
@@ -66,7 +67,7 @@ runJob config Job {..} done = do
callgit gitdir [ "worktree", "add", "--force", workdir, toString ref ]
res <- recursiveCheckDir (orgLintconfig jobOrg) workdir (orgEntrypoint jobOrg)
- >>= evaluateNF
+ >>= evaluateNF . shrinkDirResult
setJobStatus done jobOrg jobRef $
Linted res rev
diff --git a/src/Main.hs b/src/Main.hs
index beee091..d2546f5 100644
--- a/src/Main.hs
+++ b/src/Main.hs
@@ -6,18 +6,15 @@
module Main where
-import Control.Monad (unless, when)
-import Control.Monad.Identity (Identity)
+import Universum
+
import Data.Aeson (eitherDecode, encode)
import Data.Aeson.Encode.Pretty (encodePretty)
import Data.Aeson.KeyMap (coercionToHashMap)
import qualified Data.ByteString.Lazy as LB
-import Data.Maybe (fromMaybe)
import qualified Data.Text.Encoding as T
import qualified Data.Text.IO as T
-import System.Exit (ExitCode (..), exitWith)
-import System.IO (hPutStrLn, stderr)
-import WithCli (Generic, HasArguments, withCli)
+import WithCli (HasArguments, withCli)
import CheckDir (recursiveCheckDir, resultIsFatal)
import LintConfig (LintConfig (..), patchConfig)
@@ -25,6 +22,7 @@ import Types (Level (..))
import Util (printPretty)
import WriteRepo (writeAdjustedRepository)
+import System.Exit (ExitCode (ExitFailure, ExitSuccess))
import qualified Version as V (version)
-- | the options this cli tool can take
@@ -58,7 +56,7 @@ run options = do
when (version options) $ do
putStrLn V.version
- exitWith ExitSuccess
+ exitSuccess
let repo = fromMaybe "." (repository options)
let entry = fromMaybe "main.json" (entrypoint options)
@@ -68,7 +66,7 @@ run options = do
Nothing -> error "Need a config file!"
Just path -> LB.readFile path >>= \res ->
case eitherDecode res :: Either String (LintConfig Identity) of
- Left err -> error $ "config file invalid: " <> err
+ Left err -> error $ "config file invalid: " <> toText err
Right file -> pure (patchConfig file (config options))
lints <- recursiveCheckDir lintconfig repo entry
@@ -79,16 +77,14 @@ run options = do
else printPretty (level, lints)
case out options of
- Nothing -> exitWith $ case resultIsFatal lintconfig lints of
- False -> ExitSuccess
- True -> ExitFailure 1
+ Nothing -> exitWith $ if resultIsFatal lintconfig lints then ExitFailure 1 else ExitSuccess
Just outpath -> do
c <- writeAdjustedRepository lintconfig repo outpath lints
unless (json options) $
case c of
- ExitFailure 1 -> putStrLn "\nMap failed linting!"
- ExitFailure 2 -> putStrLn "\nOutpath already exists, not writing anything."
- _ -> pure ()
+ ExitFailure 1 -> putTextLn "\nMap failed linting!"
+ ExitFailure 2 -> putTextLn "\nOutpath already exists, not writing anything."
+ _ -> pass
exitWith c
@@ -107,10 +103,10 @@ printLB a = T.putStrLn $ T.decodeUtf8 $ LB.toStrict a
aesonWarning :: IO ()
aesonWarning = case coercionToHashMap of
Just _ -> hPutStrLn stderr
- "Warning: this program was compiled using an older version of the Aeson Library\n\
+ ("Warning: this program was compiled using an older version of the Aeson Library\n\
\used for parsing JSON, which is susceptible to hash flooding attacks.\n\
\n\
\Recompiling with a newer version is recommended when handling untrusted inputs.\n\
\n\
- \See https://cs-syd.eu/posts/2021-09-11-json-vulnerability for details."
- _ -> pure ()
+ \See https://cs-syd.eu/posts/2021-09-11-json-vulnerability for details." :: Text)
+ _ -> pass
diff --git a/src/Version.hs b/src/Version.hs
index c0f7edf..2ec1537 100644
--- a/src/Version.hs
+++ b/src/Version.hs
@@ -3,10 +3,10 @@
module Version ( version ) where
-import Control.Monad.Trans (liftIO)
+import Universum
+
import qualified Language.Haskell.TH as TH
import System.Process (readProcess)
-import GHC.IO (catchAny)
version :: String
version = "walint rc3 2021 (" <>
diff --git a/walint.cabal b/walint.cabal
index caf3a4f..ae7f6aa 100644
--- a/walint.cabal
+++ b/walint.cabal
@@ -34,6 +34,8 @@ library
Paths_walint
hs-source-dirs:
lib
+ default-extensions:
+ NoImplicitPrelude
ghc-options: -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors
build-depends:
HList
@@ -53,6 +55,7 @@ library
, text
, text-metrics
, transformers
+ , universum
, uri-encode
, vector
, witherable
@@ -65,6 +68,8 @@ executable walint
Paths_walint
hs-source-dirs:
src
+ default-extensions:
+ NoImplicitPrelude
ghc-options: -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors
build-depends:
aeson
@@ -76,6 +81,7 @@ executable walint
, process
, template-haskell
, text
+ , universum
, walint
default-language: Haskell2010
@@ -91,7 +97,7 @@ executable walint-server
server
default-extensions:
NoImplicitPrelude
- ghc-options: -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors
+ ghc-options: -Wall -Wno-name-shadowing -Wno-unticked-promoted-constructors -rtsopts -threaded
build-depends:
aeson
, async