packages feed

shikensu 0.1.3 → 0.2.0

raw patch · 15 files changed

+755/−554 lines, 15 filesdep ~basedep ~textPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, text

API changes (from Hackage documentation)

- Shikensu.Utilities: cleanPath :: FilePath -> FilePath
- Shikensu.Utilities: commonDirectory :: Pattern -> FilePath
- Shikensu.Utilities: compileParentPath :: FilePath -> Maybe FilePath
- Shikensu.Utilities: compilePathToRoot :: FilePath -> FilePath
- Shikensu.Utilities: compilePatterns :: [Pattern] -> [Pattern]
- Shikensu.Utilities: globDir :: FilePath -> [Pattern] -> IO [[FilePath]]
- Shikensu.Utilities: replaceSingleDot :: String -> String
- Shikensu.Utilities: stripPrefix :: String -> String -> String
- Shikensu.Utilities: takeDirName :: FilePath -> FilePath
+ Shikensu: listF :: [Pattern] -> FilePath -> IO Dictionary
+ Shikensu.Internal.Utilities: cleanPath :: FilePath -> FilePath
+ Shikensu.Internal.Utilities: commonDirectory :: Pattern -> FilePath
+ Shikensu.Internal.Utilities: compileParentPath :: FilePath -> Maybe FilePath
+ Shikensu.Internal.Utilities: compilePathToRoot :: FilePath -> FilePath
+ Shikensu.Internal.Utilities: compilePatterns :: [Pattern] -> [Pattern]
+ Shikensu.Internal.Utilities: globDir :: FilePath -> [Pattern] -> IO [[FilePath]]
+ Shikensu.Internal.Utilities: replaceSingleDot :: String -> String
+ Shikensu.Internal.Utilities: stripPrefix :: String -> String -> String
+ Shikensu.Internal.Utilities: takeDirName :: FilePath -> FilePath
+ Shikensu.Utilities: (⚡) :: (FromJSON a, ToJSON a) => Metadata -> Text -> Maybe a
+ Shikensu.Utilities: (⚡⚡) :: (FromJSON a, ToJSON a) => Metadata -> Text -> a
+ Shikensu.Utilities: lsequence :: Monad m => [(String, m a)] -> m [(String, a)]
- Shikensu: list :: [Pattern] -> FilePath -> IO Dictionary
+ Shikensu: list :: FilePath -> [Pattern] -> IO Dictionary

Files

shikensu.cabal view
@@ -1,5 +1,5 @@ name: shikensu-version: 0.1.3+version: 0.2.0 category: File IO  synopsis:@@ -40,6 +40,7 @@     Shikensu,     Shikensu.Contrib,     Shikensu.Contrib.IO,+    Shikensu.Internal.Utilities,     Shikensu.Metadata,     Shikensu.Sorting,     Shikensu.Types,@@ -53,13 +54,17 @@     filepath == 1.4.*,     flow == 1.0.*,     Glob == 0.7.*,-    unordered-containers == 0.2.*+    unordered-containers == 0.2.*,+    text >= 1.2 && < 2   test-suite tests   default-language:     Haskell2010 +  default-extensions:+    OverloadedStrings+   ghc-options:     -Wall -threaded -rtsopts -with-rtsopts=-N @@ -76,7 +81,8 @@     Test.Contrib,     Test.Example,     Test.Helpers,-    Test.Shikensu+    Test.Shikensu,+    Test.Utilities    build-depends:     aeson,@@ -88,5 +94,5 @@     shikensu,     tasty == 0.11.*,     tasty-hunit == 0.9.*,-    text >= 1.2 && < 2,+    text,     unordered-containers
src/Shikensu.hs view
@@ -4,16 +4,17 @@  -} module Shikensu-  ( list+    ( list+    , listF -  , forkDefinition-  , makeDefinition-  , makeDictionary-  ) where+    , forkDefinition+    , makeDefinition+    , makeDictionary+    ) where  import Flow+import Shikensu.Internal.Utilities import Shikensu.Types-import Shikensu.Utilities import System.FilePath  import qualified Data.HashMap.Strict as HashMap (empty)@@ -24,7 +25,7 @@ -- IO  -{-| Make a single dictionary based on multiple glob patterns and a path to a directory.+{-| Make a single dictionary based on a path to a directory and multiple glob patterns.  1. Compile patterns so `globDir` can use them. 2. Run `globDir` function on the given (root) path.@@ -34,18 +35,23 @@ 5. Merge the dictionaries into one dictionary.  -}-list :: [Pattern] -> FilePath -> IO Dictionary-list patterns rootDir =-  patterns-    |> compilePatterns-    |> globDir rootDir-    |> fmap (List.zip patterns)-    |> fmap (List.map (uncurry . makeDictionary $ rootDir))-    |> fmap (List.concat)+list :: FilePath -> [Pattern] -> IO Dictionary+list rootDir patterns =+    patterns+        |> compilePatterns+        |> globDir rootDir+        |> fmap (List.zip patterns)+        |> fmap (List.map (uncurry . makeDictionary $ rootDir))+        |> fmap (List.concat)  +listF :: [Pattern] -> FilePath -> IO Dictionary+listF patterns rootDir =+    list rootDir patterns  ++ -- PURE  @@ -53,20 +59,20 @@ -} forkDefinition :: FilePath -> Definition -> Definition forkDefinition newLocalPath def =-  Definition-    { basename        = takeBaseName newLocalPath-    , dirname         = takeDirName newLocalPath-    , extname         = takeExtension newLocalPath-    , pattern         = (pattern def)-    , rootDirname     = (rootDirname def)-    , workingDirname  = (workingDirname def)+    Definition+        { basename        = takeBaseName newLocalPath+        , dirname         = takeDirName newLocalPath+        , extname         = takeExtension newLocalPath+        , pattern         = (pattern def)+        , rootDirname     = (rootDirname def)+        , workingDirname  = (workingDirname def) -    -- Additional properties-    , content         = (content def)-    , metadata        = (metadata def)-    , parentPath      = compileParentPath $ takeDirName newLocalPath-    , pathToRoot      = compilePathToRoot $ takeDirName newLocalPath-    }+        -- Additional properties+        , content         = (content def)+        , metadata        = (metadata def)+        , parentPath      = compileParentPath $ takeDirName newLocalPath+        , pathToRoot      = compilePathToRoot $ takeDirName newLocalPath+        }   @@ -79,44 +85,45 @@ - the workspace path `example/test/hello.md`  > Definition->   { basename = "hello"->   , dirname = "test"->   , extname = ".md"->   , pattern = "example/**/*.md"->   , rootDirname = "/Users/icidasset/Projects/shikensu"->   , workingDirname = "example"+>     { basename = "hello"+>     , dirname = "test"+>     , extname = ".md"+>     , pattern = "example/**/*.md"+>     , rootDirname = "/Users/icidasset/Projects/shikensu"+>     , workingDirname = "example" >->   , content = Nothing->   , metadata = HashMap.empty->   , parentPath = "../"->   , pathToRoot = "../../"->   }+>     , content = Nothing+>     , metadata = HashMap.empty+>     , parentPath = "../"+>     , pathToRoot = "../../"+>     }  -} makeDefinition :: FilePath -> Pattern -> FilePath -> Definition makeDefinition _rootDirname _pattern _workspacePath =-  let-    workingDir  = cleanPath . (commonDirectory) $ _pattern-    localPath   = cleanPath . (stripPrefix workingDir) . cleanPath $ _workspacePath-  in-    Definition-      { basename        = takeBaseName localPath-      , dirname         = takeDirName localPath-      , extname         = takeExtension localPath-      , pattern         = _pattern-      , rootDirname     = dropTrailingPathSeparator _rootDirname-      , workingDirname  = workingDir+    let+        workingDir  = cleanPath . (commonDirectory) $ _pattern+        localPath   = cleanPath . (stripPrefix workingDir) . cleanPath $ _workspacePath+    in+        Definition+            { basename        = takeBaseName localPath+            , dirname         = takeDirName localPath+            , extname         = takeExtension localPath+            , pattern         = _pattern+            , rootDirname     = dropTrailingPathSeparator _rootDirname+            , workingDirname  = workingDir -      -- Additional properties-      , content         = Nothing-      , metadata        = HashMap.empty-      , parentPath      = compileParentPath $ takeDirName localPath-      , pathToRoot      = compilePathToRoot $ takeDirName localPath-      }+            -- Additional properties+            , content         = Nothing+            , metadata        = HashMap.empty+            , parentPath      = compileParentPath $ takeDirName localPath+            , pathToRoot      = compilePathToRoot $ takeDirName localPath+            }    {-| Make a Dictionary. -} makeDictionary :: FilePath -> Pattern -> [FilePath] -> Dictionary-makeDictionary _rootDirname _pattern = List.map (makeDefinition _rootDirname _pattern)+makeDictionary _rootDirname _pattern =+    List.map (makeDefinition _rootDirname _pattern)
src/Shikensu/Contrib.hs view
@@ -2,34 +2,34 @@  -} module Shikensu.Contrib-  ( clearMetadata-  , clearMetadataDef-  , clone-  , copyPropsToMetadata-  , copyPropsToMetadataDef-  , exclude-  , insertMetadata-  , insertMetadataDef-  , permalink-  , permalinkDef-  , prefixDirname-  , prefixDirnameDef-  , rename-  , renameDef-  , renameExt-  , renameExtDef-  , renderContent-  , renderContentDef-  , replaceMetadata-  , replaceMetadataDef-  ) where+    ( clearMetadata+    , clearMetadataDef+    , clone+    , copyPropsToMetadata+    , copyPropsToMetadataDef+    , exclude+    , insertMetadata+    , insertMetadataDef+    , permalink+    , permalinkDef+    , prefixDirname+    , prefixDirnameDef+    , rename+    , renameDef+    , renameExt+    , renameExtDef+    , renderContent+    , renderContentDef+    , replaceMetadata+    , replaceMetadataDef+    ) where  import Data.ByteString (ByteString) import Flow import Shikensu (forkDefinition)+import Shikensu.Internal.Utilities (cleanPath, compileParentPath, compilePathToRoot) import Shikensu.Metadata (transposeToMetadata) import Shikensu.Types-import Shikensu.Utilities (cleanPath, compileParentPath, compilePathToRoot) import System.FilePath (FilePath, combine)  import qualified Data.HashMap.Strict as HashMap (empty, union)@@ -40,11 +40,13 @@ Replace the current hash map with an empty one. -} clearMetadata :: Dictionary -> Dictionary-clearMetadata = fmap (clearMetadataDef)+clearMetadata =+    fmap (clearMetadataDef)   clearMetadataDef :: Definition -> Definition-clearMetadataDef def = def { metadata = HashMap.empty }+clearMetadataDef def =+    def { metadata = HashMap.empty }   @@ -58,13 +60,13 @@ -} clone :: FilePath -> FilePath -> Dictionary -> Dictionary clone existingPath newPath dict =-  let-    makeNew = \def acc ->-      if (localPath def) == existingPath-        then acc ++ [forkDefinition newPath def]-        else acc-  in-    dict ++ (foldr makeNew [] dict)+    let+        makeNew = \def acc ->+            if (localPath def) == existingPath+                then acc ++ [forkDefinition newPath def]+                else acc+    in+        dict ++ (foldr makeNew [] dict)   @@ -74,13 +76,14 @@ to see what properties get put in here. -} copyPropsToMetadata :: Dictionary -> Dictionary-copyPropsToMetadata = fmap (copyPropsToMetadataDef)+copyPropsToMetadata =+    fmap (copyPropsToMetadataDef)   copyPropsToMetadataDef :: Definition -> Definition-copyPropsToMetadataDef def = def {-    metadata = HashMap.union (transposeToMetadata def) (metadata def)-  }+copyPropsToMetadataDef def =+    def+        { metadata = HashMap.union (transposeToMetadata def) (metadata def) }   @@ -89,7 +92,8 @@ Filter out the definitions that have the given `localPath`. -} exclude :: FilePath -> Dictionary -> Dictionary-exclude path = filter (\def -> (localPath def) /= path)+exclude path =+    filter (\def -> (localPath def) /= path)   @@ -98,11 +102,13 @@ Merge the current hash map with another one. -} insertMetadata :: Metadata -> Dictionary -> Dictionary-insertMetadata a = fmap (insertMetadataDef a)+insertMetadata a =+    fmap (insertMetadataDef a)   insertMetadataDef :: Metadata -> Definition -> Definition-insertMetadataDef given def = def { metadata = HashMap.union given (metadata def) }+insertMetadataDef given def =+    def { metadata = HashMap.union given (metadata def) }   @@ -115,26 +121,24 @@ > permalink "index" dictionary -} permalink :: String -> Dictionary -> Dictionary-permalink a = fmap (permalinkDef a)+permalink a =+    fmap (permalinkDef a)   permalinkDef :: String -> Definition -> Definition permalinkDef newBasename def =-  if (basename def) /= newBasename-    then-      let-        newDirname = cleanPath $ combine (dirname def) (basename def)-      in-        def {-          basename    = newBasename-        , dirname     = newDirname--        , parentPath  = compileParentPath $ newDirname-        , pathToRoot  = compilePathToRoot $ newDirname-        }-+    if (basename def) /= newBasename then+        let+            newDirname = cleanPath $ combine (dirname def) (basename def)+        in+            def+                { basename    = newBasename+                , dirname     = newDirname+                , parentPath  = compileParentPath $ newDirname+                , pathToRoot  = compilePathToRoot $ newDirname+                }     else-      def+        def   @@ -148,15 +152,14 @@  prefixDirnameDef :: String -> Definition -> Definition prefixDirnameDef prefix def =-  let-    newDirname = prefix ++ (dirname def)-  in-    def {-      dirname     = newDirname--    , parentPath  = compileParentPath $ newDirname-    , pathToRoot  = compilePathToRoot $ newDirname-    }+    let+        newDirname = prefix ++ (dirname def)+    in+        def+            { dirname     = newDirname+            , parentPath  = compileParentPath $ newDirname+            , pathToRoot  = compilePathToRoot $ newDirname+            }   @@ -190,14 +193,15 @@ > -- now have the extname ".html" -} renameExt :: String -> String -> Dictionary -> Dictionary-renameExt a b = fmap (renameExtDef a b)+renameExt a b =+    fmap (renameExtDef a b)   renameExtDef :: String -> String -> Definition -> Definition renameExtDef oldExtname newExtname def =-  if (extname def) == oldExtname-    then def { extname = newExtname }-    else def+    if (extname def) == oldExtname+        then def { extname = newExtname }+        else def   @@ -209,11 +213,13 @@ You can use this to render templates, markdown, etc. -} renderContent :: (Definition -> Maybe ByteString) -> Dictionary -> Dictionary-renderContent a = fmap (renderContentDef a)+renderContent a =+    fmap (renderContentDef a)   renderContentDef :: (Definition -> Maybe ByteString) -> Definition -> Definition-renderContentDef renderer def = def { content = renderer def }+renderContentDef renderer def =+    def { content = renderer def }   @@ -222,8 +228,10 @@ Replace the current hash map with another one. -} replaceMetadata :: Metadata -> Dictionary -> Dictionary-replaceMetadata a = fmap (replaceMetadataDef a)+replaceMetadata a =+    fmap (replaceMetadataDef a)   replaceMetadataDef :: Metadata -> Definition -> Definition-replaceMetadataDef given def = def { metadata = given }+replaceMetadataDef given def =+    def { metadata = given }
src/Shikensu/Contrib/IO.hs view
@@ -2,11 +2,11 @@  -} module Shikensu.Contrib.IO-  ( Shikensu.Contrib.IO.read-  , Shikensu.Contrib.IO.readDef-  , Shikensu.Contrib.IO.write-  , Shikensu.Contrib.IO.writeDef-  ) where+    ( Shikensu.Contrib.IO.read+    , Shikensu.Contrib.IO.readDef+    , Shikensu.Contrib.IO.write+    , Shikensu.Contrib.IO.writeDef+    ) where  import Data.Maybe (fromMaybe) import Flow@@ -24,22 +24,23 @@ put that content in the `content` property. -} read :: Dictionary -> IO Dictionary-read = Utilities.mapIO (readDef)+read =+    Utilities.mapIO (readDef)   readDef :: Definition -> IO Definition readDef def =-  let-    absPath = absolutePath def-  in-    doesFileExist absPath >>= \exists ->-      if exists then-        fmap-          (\c -> def { content = Just c })-          (B.readFile absPath)-      else-        return-          def { content = Nothing }+    let+        absPath = absolutePath def+    in+        doesFileExist absPath >>= \exists ->+            if exists then+                fmap+                    (\c -> def { content = Just c })+                    (B.readFile absPath)+            else+                return+                    def { content = Nothing }   @@ -49,15 +50,16 @@ The path of the new file is `joinPath [rootDirname, givenDirectoryName, localPath]`. -} write :: FilePath -> Dictionary -> IO Dictionary-write dest = Utilities.mapIO (writeDef dest)+write dest =+    Utilities.mapIO (writeDef dest)   writeDef :: FilePath -> Definition -> IO Definition writeDef dest def =-  let-    path = joinPath [rootDirname def, dest, localPath def]-    cont = fromMaybe B.empty (content def)-  in-    createDirectoryIfMissing True (takeDirectory path)-      >> B.writeFile path cont-      >> return def+    let+        path = joinPath [rootDirname def, dest, localPath def]+        cont = fromMaybe B.empty (content def)+    in+        createDirectoryIfMissing True (takeDirectory path)+            >> B.writeFile path cont+            >> return def
+ src/Shikensu/Internal/Utilities.hs view
@@ -0,0 +1,113 @@+{-| Internal utility functions.++-}+module Shikensu.Internal.Utilities+    ( cleanPath+    , commonDirectory+    , compilePatterns+    , compileParentPath+    , compilePathToRoot+    , globDir+    , replaceSingleDot+    , stripPrefix+    , takeDirName+    ) where++import Data.Maybe (fromMaybe)+import Flow+import Shikensu.Types (Pattern)+import System.FilePath++import qualified Data.List as List (map, stripPrefix)+import qualified System.FilePath.Glob as Glob+++{-| "Clean up" a path.++> `/directory/./nested/` -> `directory/nested`+> `./` -> ``+> `.` -> ``++-}+cleanPath :: FilePath -> FilePath+cleanPath =+    normalise .> dropTrailingPathSeparator .> dropDrive .> replaceSingleDot+++{-| Get the common directory from a Pattern.+-}+commonDirectory :: Pattern -> FilePath+commonDirectory pattern =+    (Glob.compile .> Glob.commonDirectory .> fst) pattern+++{-| Compile a list of `Pattern`s to `Glob.Pattern`s.+-}+compilePatterns :: [Pattern] -> [Glob.Pattern]+compilePatterns =+    List.map Glob.compile+++{-| Path to parent, when there is one.++> Just "../" or Nothing++-}+compileParentPath :: FilePath -> Maybe FilePath+compileParentPath dirname =+    case dirname of+        "" -> Nothing+        _  -> Just "../"+++{-| Path to root.++Example, if `dirname` is 'example/subdir',+then this will be `../../`.++If the `dirname` is empty,+then this will be empty as well.++-}+compilePathToRoot :: FilePath -> FilePath+compilePathToRoot dirname =+    if dirname == "" then+        ""+    else+        dirname+            |> splitDirectories+            |> fmap (\_ -> "..")+            |> joinPath+            |> addTrailingPathSeparator+++{-| List contents of a directory using a glob pattern.+Returns a relative path, based on the given rootDirname.+-}+globDir :: FilePath -> [Glob.Pattern] -> IO [[FilePath]]+globDir rootDir patterns =+    Glob.globDir patterns rootDir+        |> fmap (fst)+        |> fmap (List.map . List.map . makeRelative $ rootDir)+++{-| If the path is a single dot, return an empty string.+Otherwise return the path.+-}+replaceSingleDot :: String -> String+replaceSingleDot path =+    if path == "." then "" else path+++{-| Strip prefix.+-}+stripPrefix :: String -> String -> String+stripPrefix prefix target =+    fromMaybe target (List.stripPrefix prefix target)+++{-| Take dirname and replace single dot.+-}+takeDirName :: FilePath -> FilePath+takeDirName =+    replaceSingleDot . takeDirectory
src/Shikensu/Metadata.hs view
@@ -2,9 +2,9 @@  -} module Shikensu.Metadata-  ( transposeMetadata-  , transposeToMetadata-  ) where+    ( transposeMetadata+    , transposeToMetadata+    ) where  import Flow import Shikensu.Types@@ -20,35 +20,35 @@ which implements the Aeson.FromJSON instance.  > data Example =->   Example { some :: Text }->   deriving (Generic, FromJSON)+>     Example { some :: Text }+>     deriving (Generic, FromJSON) > > hashMap     = HashMap.fromList [ ("some", "metadata") ] > defaultEx   = Example { some = "default" }-> example     = transposeMetadata hashMap defaultExample :: Example+> example     = transposeMetadata hashMap defaultEx :: Example  -} transposeMetadata :: Aeson.FromJSON a => Metadata -> a -> a transposeMetadata hashMap fallback =-  let-    result = hashMap-      |> Aeson.toJSON-      |> Aeson.fromJSON :: Aeson.FromJSON b => Aeson.Result b-  in-    case result of-      Aeson.Success x -> x-      Aeson.Error _   -> fallback+    let+        result = hashMap+            |> Aeson.toJSON+            |> Aeson.fromJSON :: Aeson.FromJSON b => Aeson.Result b+    in+        case result of+            Aeson.Success x -> x+            Aeson.Error _   -> fallback   {-| Inverse of `transposeMetadata`. -} transposeToMetadata :: (Aeson.ToJSON a) => a -> Metadata transposeToMetadata generic =-  let-    result = generic-      |> Aeson.toJSON-      |> Aeson.fromJSON :: Aeson.FromJSON b => Aeson.Result b-  in-    case result of-      Aeson.Success x -> x-      Aeson.Error _   -> HashMap.empty+    let+        result = generic+            |> Aeson.toJSON+            |> Aeson.fromJSON :: Aeson.FromJSON b => Aeson.Result b+    in+        case result of+            Aeson.Success x -> x+            Aeson.Error _   -> HashMap.empty
src/Shikensu/Sorting.hs view
@@ -2,8 +2,8 @@  -} module Shikensu.Sorting-  ( sortByAbsolutePath-  ) where+    ( sortByAbsolutePath+    ) where  import Shikensu.Types (Definition, absolutePath) @@ -15,4 +15,4 @@ -} sortByAbsolutePath :: Definition -> Definition -> Ordering sortByAbsolutePath defA defB =-  compare (absolutePath defA) (absolutePath defB)+    compare (absolutePath defA) (absolutePath defB)
src/Shikensu/Types.hs view
@@ -11,33 +11,33 @@ {-| A file definition, along with some additional properties. -} data Definition =-  Definition-    { basename :: String-    , dirname :: FilePath-    , extname :: String-    , pattern :: Pattern-    , rootDirname :: FilePath-    , workingDirname :: FilePath+    Definition+        { basename :: String+        , dirname :: FilePath+        , extname :: String+        , pattern :: Pattern+        , rootDirname :: FilePath+        , workingDirname :: FilePath -    -- Additional properties-    , content :: Maybe ByteString-    , metadata :: Metadata-    , parentPath :: Maybe FilePath-    , pathToRoot :: FilePath-    } deriving (Eq, Show)+        -- Additional properties+        , content :: Maybe ByteString+        , metadata :: Metadata+        , parentPath :: Maybe FilePath+        , pathToRoot :: FilePath+        } deriving (Eq, Show)   instance ToJSON Definition where-  toJSON def =-    object-      [ "basename"        .= basename def-      , "dirname"         .= dirname def-      , "extname"         .= extname def-      , "pattern"         .= pattern def-      , "workingDirname"  .= workingDirname def-      , "parentPath"      .= parentPath def-      , "pathToRoot"      .= pathToRoot def-      ]+    toJSON def =+        object+            [ "basename"        .= basename def+            , "dirname"         .= dirname def+            , "extname"         .= extname def+            , "pattern"         .= pattern def+            , "workingDirname"  .= workingDirname def+            , "parentPath"      .= parentPath def+            , "pathToRoot"      .= pathToRoot def+            ]   @@ -57,14 +57,14 @@  absolutePath :: Definition -> String absolutePath def =-  joinPath [rootDirname def, workspacePath def]+    joinPath [rootDirname def, workspacePath def]   localPath :: Definition -> String localPath def =-  joinPath [dirname def, (basename def) ++ (extname def)]+    joinPath [dirname def, (basename def) ++ (extname def)]   workspacePath :: Definition -> String workspacePath def =-  joinPath [workingDirname def, localPath def]+    joinPath [workingDirname def, localPath def]
src/Shikensu/Utilities.hs view
@@ -1,120 +1,104 @@-{-| Utilities, mainly for internal use.---} module Shikensu.Utilities-  ( cleanPath-  , commonDirectory-  , compilePatterns-  , compileParentPath-  , compilePathToRoot-  , globDir-  , io-  , mapIO-  , replaceSingleDot-  , stripPrefix-  , takeDirName-  ) where+    ( io+    , mapIO+    , lsequence -import Data.Maybe (fromMaybe)-import Data.Tuple (fst)+    , (⚡)+    , (⚡⚡)+    ) where++import Data.Aeson (FromJSON, ToJSON, fromJSON)+import Data.Text (Text) import Flow import Shikensu.Types-import System.FilePath -import qualified Data.List as List (map, stripPrefix)-import qualified System.FilePath.Glob as Glob+import qualified Data.Aeson as Json (Object, Result(..), encode)+import qualified Data.HashMap.Strict as HashMap (lookup)+import qualified Data.List as List (unzip, zip)+import qualified Data.Tuple as Tuple (fst, snd)+import qualified Data.Text as Text (concat, unpack)+import qualified Data.Text.Lazy as Lazy.Text (unpack)+import qualified Data.Text.Lazy.Encoding as Lazy.Text (decodeUtf8)  -{-| "Clean up" a path.--> `/directory/./nested/` -> `directory/nested`-> `./` -> ``-> `.` -> ``---}-cleanPath :: FilePath -> FilePath-cleanPath = normalise .> dropTrailingPathSeparator .> dropDrive .> replaceSingleDot+-- IO  -{-| Get the common directory from a Pattern.+{-| IO Sequence helpers -}-commonDirectory :: Pattern -> FilePath-commonDirectory pattern = (Glob.compile .> Glob.commonDirectory .> fst) pattern+io :: ([Definition] -> [IO Definition]) -> Dictionary -> IO Dictionary+io fn =+    fn .> sequence  -{-| Compile a list of `Pattern`s to `Glob.Pattern`s.--}-compilePatterns :: [Pattern] -> [Glob.Pattern]-compilePatterns = List.map Glob.compile+mapIO :: (Definition -> IO Definition) -> Dictionary -> IO Dictionary+mapIO =+    fmap .> io  -{-| Path to parent, when there is one.+{-| One way to deal with multiple dictionaries. -> Just "../" or Nothing+> lsequence+>     [ ( "pages", Shikensu.list rootDir ["src/pages/**/*.html"]    )+>     , ( "js",    Shikensu.list rootDir ["src/javascript/**/*.js"] )+>     ] +From multiple IO monads to a single IO monad. -}-compileParentPath :: FilePath -> Maybe FilePath-compileParentPath dirname =-  case dirname of-    "" -> Nothing-    _  -> Just "../"+lsequence :: Monad m => [( String, m a )] -> m [( String, a )]+lsequence list =+    let+        unzippedList =+            List.unzip list +        identifiers =+            Tuple.fst unzippedList -{-| Path to root.+        dictionaries =+            Tuple.snd unzippedList+    in+        dictionaries+            |> sequence+            |> fmap (List.zip identifiers) -Example, if `dirname` is 'example/subdir',-then this will be `../../`. -If the `dirname` is empty,-then this will be empty as well. --}-compilePathToRoot :: FilePath -> FilePath-compilePathToRoot dirname =-  if dirname == "" then-    ""-  else-    dirname-      |> splitDirectories-      |> fmap (\_ -> "..")-      |> joinPath-      |> addTrailingPathSeparator+-- PURE  -{-| List contents of a directory using a glob pattern.-Returns a relative path, based on the given rootDirname.+{-| Get stuff out of the metadata.+    Return a `Maybe`. -}-globDir :: FilePath -> [Glob.Pattern] -> IO [[FilePath]]-globDir rootDir patterns =-  Glob.globDir patterns rootDir-    |> fmap (fst)-    |> fmap (List.map . List.map . makeRelative $ rootDir)+(⚡) :: (FromJSON a, ToJSON a) => Metadata -> Text -> Maybe a+(⚡) obj key =+    HashMap.lookup key obj+        |> fmap fromJSON+        |> fmap fromJSONResult  -{-| IO Sequence helpers+{-| Get stuff out of the metadata.+    Does NOT return a `Maybe`, but gives an error if it isn't found. -}-io :: ([Definition] -> [IO Definition]) -> Dictionary -> IO Dictionary-io fn = sequence . fn+(⚡⚡) :: (FromJSON a, ToJSON a) => Metadata -> Text -> a+(⚡⚡) obj key =+    case (obj ⚡ key) of+        Just x  -> x+        Nothing -> error <|+            "Could not find the key `" ++ (Text.unpack key)         ++ "` " +++            "on the metadata object `" ++ (aesonObjectToString obj) ++ "` using (⚡⚡)"  -mapIO :: (Definition -> IO Definition) -> Dictionary -> IO Dictionary-mapIO = io . fmap --{-| If the path is a single dot, return an empty string.-Otherwise return the path.--}-replaceSingleDot :: String -> String-replaceSingleDot path = if path == "." then "" else path+-- Private  -{-| Strip prefix.--}-stripPrefix :: String -> String -> String-stripPrefix prefix target = fromMaybe target (List.stripPrefix prefix target)+fromJSONResult :: ToJSON a => Json.Result a -> a+fromJSONResult result =+    case result of+        Json.Success x -> x+        Json.Error err -> error err  -{-| Take dirname.--}-takeDirName :: FilePath -> FilePath-takeDirName = replaceSingleDot . takeDirectory+aesonObjectToString :: Json.Object -> String+aesonObjectToString =+    Json.encode .> Lazy.Text.decodeUtf8 .> Lazy.Text.unpack
tests/Test.hs view
@@ -6,16 +6,18 @@ import Test.Helpers (ioErrorHandler, rootPath) import Test.Shikensu import Test.Tasty+import Test.Utilities   main :: IO () main =-  rootPath-    >>= \r -> catch-      (removeDirectoryRecursive (combine r "tests/build"))-      (ioErrorHandler)-    >> defaultMain tests+    rootPath+        >>= \r -> catch+            (removeDirectoryRecursive (combine r "tests/build"))+            (ioErrorHandler)+        >> defaultMain tests   tests :: TestTree-tests = testGroup "Tests" [shikensuTests, contribTests, exampleTests]+tests =+    testGroup "Tests" [shikensuTests, contribTests, exampleTests, utilityTests]
tests/Test/Contrib.hs view
@@ -23,18 +23,18 @@  contribTests :: TestTree contribTests = testGroup-  "Contrib tests"-  [ testClone-  , testExclude-  , testMetadata-  , testPermalink-  , testPrefixDirname-  , testRead-  , testRename-  , testRenameExt-  , testRenderContent-  , testWrite-  ]+    "Contrib tests"+    [ testClone+    , testExclude+    , testMetadata+    , testPermalink+    , testPrefixDirname+    , testRead+    , testRename+    , testRenameExt+    , testRenderContent+    , testWrite+    ]   @@ -43,22 +43,24 @@   list :: Shikensu.Pattern -> IO Shikensu.Dictionary-list pattern = rootPath >>= Shikensu.list [pattern]+list pattern =+    rootPath >>= Shikensu.listF [pattern]   example_md :: IO Shikensu.Dictionary-example_md = list "tests/fixtures/example.md"+example_md =+    list "tests/fixtures/example.md"   renderer :: Shikensu.Definition -> Maybe ByteString renderer def =-  let-    openingTag = BS.pack "<html>"-    closingTag = BS.pack "</html>"-  in-    def-      |> Shikensu.content-      |> fmap (\c -> BS.intercalate B.empty [openingTag, c, closingTag])+    let+        openingTag = BS.pack "<html>"+        closingTag = BS.pack "</html>"+    in+        def+            |> Shikensu.content+            |> fmap (\c -> BS.intercalate B.empty [openingTag, c, closingTag])   @@ -68,175 +70,175 @@  testClone :: TestTree testClone =-  let-    dictionary = fmap (Contrib.clone "example.md" "cloned.md") example_md-    definition = fmap (List.head . List.reverse) dictionary-  in-    testCase "Should `clone`"-      $ definition `rmap` Shikensu.localPath >>= assertEq "cloned.md"+    let+        dictionary = fmap (Contrib.clone "example.md" "cloned.md") example_md+        definition = fmap (List.head . List.reverse) dictionary+    in+        testCase "Should `clone`"+        $ definition `rmap` Shikensu.localPath >>= assertEq "cloned.md"    testExclude :: TestTree testExclude =-  let-    dictionary = fmap (Contrib.exclude "example.md") example_md-    length_ = fmap length dictionary-  in-    testCase "Should `exclude`"-      $ length_ >>= assertEq 0+    let+        dictionary = fmap (Contrib.exclude "example.md") example_md+        length_ = fmap length dictionary+    in+        testCase "Should `exclude`"+        $ length_ >>= assertEq 0    testMetadata :: TestTree testMetadata =-  let-    keyA        = Text.pack "title"-    valueA      = Aeson.String (Text.pack "Hello world!")+    let+        keyA        = Text.pack "title"+        valueA      = Aeson.String (Text.pack "Hello world!") -    keyB        = Text.pack "hello"-    valueB      = Aeson.String (Text.pack "Guardian.")+        keyB        = Text.pack "hello"+        valueB      = Aeson.String (Text.pack "Guardian.") -    keyC        = Text.pack "removed"-    valueC      = Aeson.String (Text.pack "Me.")+        keyC        = Text.pack "removed"+        valueC      = Aeson.String (Text.pack "Me.") -    keyBase     = Text.pack "basename"-    valueBase   = Aeson.String (Text.pack "example")+        keyBase     = Text.pack "basename"+        valueBase   = Aeson.String (Text.pack "example") -    -- 1. Insert C-    -- 2. Replace with A-    -- 3. Insert B-    dictionary = example_md-      <&> ( Contrib.insertMetadata (HashMap.fromList [ (keyC, valueC) ])-         .> Contrib.replaceMetadata (HashMap.fromList [ (keyA, valueA) ])-         .> Contrib.copyPropsToMetadata-         .> Contrib.insertMetadata (HashMap.fromList [ (keyB, valueB) ])-      )+        -- 1. Insert C+        -- 2. Replace with A+        -- 3. Insert B+        dictionary = example_md+            <&> ( Contrib.insertMetadata (HashMap.fromList [ (keyC, valueC) ])+               .> Contrib.replaceMetadata (HashMap.fromList [ (keyA, valueA) ])+               .> Contrib.copyPropsToMetadata+               .> Contrib.insertMetadata (HashMap.fromList [ (keyB, valueB) ])+            ) -    definition  = fmap (List.head . List.reverse) dictionary+        definition = fmap (List.head . List.reverse) dictionary -    lookupTitle = \def -> HashMap.lookup keyA (Shikensu.metadata def)-    lookupHello = \def -> HashMap.lookup keyB (Shikensu.metadata def)-    lookupRemoved = \def -> HashMap.lookup keyC (Shikensu.metadata def)-    lookupBasename = \def -> HashMap.lookup keyBase (Shikensu.metadata def)-  in-    testGroup-      "Metadata"-      [ testCase "Should no longer have `removed` key"-        $ definition `rmap` lookupRemoved >>= assertEq Nothing+        lookupTitle = \def -> HashMap.lookup keyA (Shikensu.metadata def)+        lookupHello = \def -> HashMap.lookup keyB (Shikensu.metadata def)+        lookupRemoved = \def -> HashMap.lookup keyC (Shikensu.metadata def)+        lookupBasename = \def -> HashMap.lookup keyBase (Shikensu.metadata def)+    in+        testGroup+            "Metadata"+            [ testCase "Should no longer have `removed` key"+            $ definition `rmap` lookupRemoved >>= assertEq Nothing -      , testCase "Should have `hello` key"-        $ definition `rmap` lookupHello >>= assertEq (Just valueB)+            , testCase "Should have `hello` key"+            $ definition `rmap` lookupHello >>= assertEq (Just valueB) -      , testCase "Should have `title` key"-        $ definition `rmap` lookupTitle >>= assertEq (Just valueA)+            , testCase "Should have `title` key"+            $ definition `rmap` lookupTitle >>= assertEq (Just valueA) -      , testCase "Should have `basename` key"-        $ definition `rmap` lookupBasename >>= assertEq (Just valueBase)-      ]+            , testCase "Should have `basename` key"+            $ definition `rmap` lookupBasename >>= assertEq (Just valueBase)+            ]    testPermalink :: TestTree testPermalink =-  let-    dictionary = fmap (Contrib.permalink "index") example_md-    definition = fmap (List.head) dictionary-  in-    testGroup-      "Permalink"-      [ testCase "Should have the correct `localPath`"-        $ definition `rmap` Shikensu.localPath >>= assertEq "example/index.md"+    let+        dictionary = fmap (Contrib.permalink "index") example_md+        definition = fmap (List.head) dictionary+    in+        testGroup+            "Permalink"+            [ testCase "Should have the correct `localPath`"+            $ definition `rmap` Shikensu.localPath >>= assertEq "example/index.md" -      , testCase "Should have the correct `parentPath`"-        $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")+            , testCase "Should have the correct `parentPath`"+            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../") -      , testCase "Should have the correct `pathToRoot`"-        $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../"-      ]+            , testCase "Should have the correct `pathToRoot`"+            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../"+            ]    testPrefixDirname :: TestTree testPrefixDirname =-  let-    dictionary = fmap (Contrib.prefixDirname "prefix/") (list "tests/**/example.md")-    definition = fmap List.head dictionary-  in-    testGroup-      "PrefixDirname"-      [ testCase "Should have the correct `dirname`"-        $ definition `rmap` Shikensu.dirname >>= assertEq "prefix/fixtures"+    let+        dictionary = fmap (Contrib.prefixDirname "prefix/") (list "tests/**/example.md")+        definition = fmap List.head dictionary+    in+        testGroup+            "PrefixDirname"+            [ testCase "Should have the correct `dirname`"+            $ definition `rmap` Shikensu.dirname >>= assertEq "prefix/fixtures" -      , testCase "Should have the correct `pathToRoot`"-        $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../../"+            , testCase "Should have the correct `pathToRoot`"+            $ definition `rmap` Shikensu.pathToRoot >>= assertEq "../../" -      , testCase "Should have the correct `parentPath`"-        $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")-      ]+            , testCase "Should have the correct `parentPath`"+            $ definition `rmap` Shikensu.parentPath >>= assertEq (Just "../")+            ]    testRead :: TestTree testRead =-  let-    dictionary = example_md >>= Contrib.IO.read-    definition = fmap List.head dictionary-  in-    testCase "Should `read`"-      $ definition-        <&> Shikensu.content-        <&> fmap Text.decodeUtf8-        >>= assertEq (Just (Text.pack "# Example\n"))+    let+        dictionary = example_md >>= Contrib.IO.read+        definition = fmap List.head dictionary+    in+        testCase "Should `read`"+        $ definition+            <&> Shikensu.content+            <&> fmap Text.decodeUtf8+            >>= assertEq (Just (Text.pack "# Example\n"))    testRename :: TestTree testRename =-  let-    dictionary = fmap (Contrib.rename "example.md" "renamed.md") example_md-    definition = fmap (List.head) dictionary-  in-    testCase "Should `rename`"-      $ definition `rmap` Shikensu.localPath >>= assertEq "renamed.md"+    let+        dictionary = fmap (Contrib.rename "example.md" "renamed.md") example_md+        definition = fmap (List.head) dictionary+    in+        testCase "Should `rename`"+        $ definition `rmap` Shikensu.localPath >>= assertEq "renamed.md"    testRenameExt :: TestTree testRenameExt =-  let-    dictionary = fmap (Contrib.renameExt ".md" ".html") example_md-    definition = fmap (List.head) dictionary-  in-    testCase "Should `renameExt`"-      $ definition `rmap` Shikensu.extname >>= assertEq ".html"+    let+        dictionary = fmap (Contrib.renameExt ".md" ".html") example_md+        definition = fmap (List.head) dictionary+    in+        testCase "Should `renameExt`"+        $ definition `rmap` Shikensu.extname >>= assertEq ".html"    testRenderContent :: TestTree testRenderContent =-  let-    dictionary = fmap (Contrib.renderContent renderer) (example_md >>= Contrib.IO.read)-    definition = fmap (List.head) dictionary-    expectedResult = Just (Text.pack "<html># Example\n</html>")-  in-    testCase "Should `renderContent`"-      $ definition-        <&> Shikensu.content-        <&> fmap Text.decodeUtf8-        >>= assertEq expectedResult+    let+        dictionary = fmap (Contrib.renderContent renderer) (example_md >>= Contrib.IO.read)+        definition = fmap (List.head) dictionary+        expectedResult = Just (Text.pack "<html># Example\n</html>")+    in+        testCase "Should `renderContent`"+        $ definition+            <&> Shikensu.content+            <&> fmap Text.decodeUtf8+            >>= assertEq expectedResult    testWrite :: TestTree testWrite =-  let-    destination = "tests/build/"-    dictionary = list "tests/**/example.md" >>= Contrib.IO.read >>= Contrib.IO.write destination-    definition = fmap List.head dictionary-  in-    testCase "Should `write`"-      $ definition-          <&> Shikensu.rootDirname-          >>= \r -> Text.readFile (joinPath [r, destination, "fixtures/example.md"])-          >>= \c -> assertEq "# Example\n" (Text.unpack c)+    let+        destination = "tests/build/"+        dictionary = list "tests/**/example.md" >>= Contrib.IO.read >>= Contrib.IO.write destination+        definition = fmap List.head dictionary+    in+        testCase "Should `write`"+        $ definition+            <&> Shikensu.rootDirname+            >>= \r -> Text.readFile (joinPath [r, destination, "fixtures/example.md"])+            >>= \c -> assertEq "# Example\n" (Text.unpack c)
tests/Test/Example.hs view
@@ -19,15 +19,15 @@  exampleTests :: TestTree exampleTests =-  let-    path = canonicalizePath "./tests"-    dict = path >>= dictionary_io-    test = path >>= \p ->-      read [Shikensu.makeDefinition p "fixtures/*.md" "fixtures/example.md"]-      >>= flow-  in-    testCase "Example test"-      $ dict >>= \d -> test >>= assertEq d+    let+        path = canonicalizePath "./tests"+        dict = path >>= dictionary_io+        test = path >>= \p ->+            read [Shikensu.makeDefinition p "fixtures/*.md" "fixtures/example.md"]+            >>= flow+    in+        testCase "Example test"+        $ dict >>= \d -> test >>= assertEq d   @@ -36,29 +36,30 @@  dictionary_io :: String -> IO Dictionary dictionary_io absolutePathToCwd =-  Shikensu.list ["fixtures/*.md"] absolutePathToCwd-    >>= read-    >>= flow-    >>= write "./build"+    Shikensu.list absolutePathToCwd ["fixtures/*.md"]+        >>= read+        >>= flow+        >>= write "./build"   flow :: Dictionary -> IO Dictionary flow =-     renameExt ".md" ".html"-  .> permalink "index"-  .> clone "index.html" "200.html"-  .> copyPropsToMetadata-  .> renderContent markdownRenderer-  .> return+       renameExt ".md" ".html"+    .> permalink "index"+    .> clone "index.html" "200.html"+    .> copyPropsToMetadata+    .> renderContent markdownRenderer+    .> return   markdownRenderer :: Definition -> Maybe ByteString markdownRenderer def =-  content def-    |> fmap Text.decodeUtf8-    |> fmap renderMarkdown-    |> fmap Text.encodeUtf8+    content def+        |> fmap Text.decodeUtf8+        |> fmap renderMarkdown+        |> fmap Text.encodeUtf8   renderMarkdown :: Text -> Text-renderMarkdown text = text+renderMarkdown text =+    text
tests/Test/Helpers.hs view
@@ -1,12 +1,12 @@ module Test.Helpers-  ( (<&>)-  , rmap-  , assertEq-  , ioErrorHandler-  , rootPath-  , sort-  , testsPath-  ) where+    ( (<&>)+    , rmap+    , assertEq+    , ioErrorHandler+    , rootPath+    , sort+    , testsPath+    ) where  import Shikensu.Sorting (sortByAbsolutePath) import Shikensu.Types (Dictionary)@@ -18,28 +18,35 @@   (<&>) :: Functor f => f a -> (a -> b) -> f b-(<&>) = flip fmap+(<&>) =+    flip fmap   rmap :: Functor f => f a -> (a -> b) -> f b-rmap = (<&>)+rmap =+    (<&>)   assertEq :: (Eq a, Show a) => a -> a -> Assertion-assertEq = assertEqual ""+assertEq =+    assertEqual ""   ioErrorHandler :: IOError -> IO ()-ioErrorHandler _ = putStrLn ""+ioErrorHandler _ =+    putStrLn ""   rootPath :: IO FilePath-rootPath = canonicalizePath "./"+rootPath =+    canonicalizePath "./"   sort :: Dictionary -> Dictionary-sort = List.sortBy sortByAbsolutePath+sort =+    List.sortBy sortByAbsolutePath   testsPath :: IO FilePath-testsPath = fmap ((flip combine) "tests") rootPath+testsPath =+    fmap ((flip combine) "tests") rootPath
tests/Test/Shikensu.hs view
@@ -11,8 +11,8 @@  shikensuTests :: TestTree shikensuTests = testGroup-  "Shikensu tests"-  [testRegular, testDot, testWithoutWd, testRootFile]+    "Shikensu tests"+    [testRegular, testDot, testWithoutWd, testRootFile]   @@ -22,119 +22,119 @@  testRegular :: TestTree testRegular =-  let-    pattern = "tests/**/*.md"-    dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-    definition = fmap List.head dictionary-    localPath = "fixtures/example.md"-  in-    testGroup "Test regular"-      [ testCase "Should have the correct basename"-        $ definition `rmap` Shikensu.basename >>= assertEq "example"+    let+        pattern = "tests/**/*.md"+        dictionary = fmap sort $ rootPath >>= Shikensu.listF [pattern]+        definition = fmap List.head dictionary+        localPath = "fixtures/example.md"+    in+        testGroup "Test regular"+            [ testCase "Should have the correct basename"+            $ definition `rmap` Shikensu.basename >>= assertEq "example" -      , testCase "Should have the correct dirname"-        $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            , testCase "Should have the correct dirname"+            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures" -      , testCase "Should have the correct extname"-        $ definition `rmap` Shikensu.extname >>= assertEq ".md"+            , testCase "Should have the correct extname"+            $ definition `rmap` Shikensu.extname >>= assertEq ".md" -      , testCase "Should have the correct localPath"-        $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            , testCase "Should have the correct localPath"+            $ definition `rmap` Shikensu.localPath >>= assertEq localPath -      , testCase "Should have the correct pattern"-        $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            , testCase "Should have the correct pattern"+            $ definition `rmap` Shikensu.pattern >>= assertEq pattern -      , testCase "Should have the correct workingDirname"-        $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"+            , testCase "Should have the correct workingDirname"+            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests" -      ]+            ]   testDot :: TestTree testDot =-  let-    pattern = "./tests/**/*.md"-    dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-    definition = fmap List.head dictionary-    localPath = "fixtures/example.md"-  in-    testGroup "Test dot"-      [ testCase "Should have the correct basename"-        $ definition `rmap` Shikensu.basename >>= assertEq "example"+    let+        pattern = "./tests/**/*.md"+        dictionary = fmap sort $ rootPath >>= Shikensu.listF [pattern]+        definition = fmap List.head dictionary+        localPath = "fixtures/example.md"+    in+        testGroup "Test dot"+            [ testCase "Should have the correct basename"+            $ definition `rmap` Shikensu.basename >>= assertEq "example" -      , testCase "Should have the correct dirname"-        $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            , testCase "Should have the correct dirname"+            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures" -      , testCase "Should have the correct extname"-        $ definition `rmap` Shikensu.extname >>= assertEq ".md"+            , testCase "Should have the correct extname"+            $ definition `rmap` Shikensu.extname >>= assertEq ".md" -      , testCase "Should have the correct localPath"-        $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            , testCase "Should have the correct localPath"+            $ definition `rmap` Shikensu.localPath >>= assertEq localPath -      , testCase "Should have the correct pattern"-        $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            , testCase "Should have the correct pattern"+            $ definition `rmap` Shikensu.pattern >>= assertEq pattern -      , testCase "Should have the correct workingDirname"-        $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests"+            , testCase "Should have the correct workingDirname"+            $ definition `rmap` Shikensu.workingDirname >>= assertEq "tests" -      ]+            ]   testWithoutWd :: TestTree testWithoutWd =-  let-    pattern = "**/*.md"-    dictionary = fmap sort $ testsPath >>= Shikensu.list [pattern]-    definition = fmap List.head dictionary-    localPath = "fixtures/example.md"-  in-    testGroup "Test without workingDirname"-      [ testCase "Should have the correct basename"-        $ definition `rmap` Shikensu.basename >>= assertEq "example"+    let+        pattern = "**/*.md"+        dictionary = fmap sort $ testsPath >>= Shikensu.listF [pattern]+        definition = fmap List.head dictionary+        localPath = "fixtures/example.md"+    in+        testGroup "Test without workingDirname"+            [ testCase "Should have the correct basename"+            $ definition `rmap` Shikensu.basename >>= assertEq "example" -      , testCase "Should have the correct dirname"-        $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures"+            , testCase "Should have the correct dirname"+            $ definition `rmap` Shikensu.dirname >>= assertEq "fixtures" -      , testCase "Should have the correct extname"-        $ definition `rmap` Shikensu.extname >>= assertEq ".md"+            , testCase "Should have the correct extname"+            $ definition `rmap` Shikensu.extname >>= assertEq ".md" -      , testCase "Should have the correct localPath"-        $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            , testCase "Should have the correct localPath"+            $ definition `rmap` Shikensu.localPath >>= assertEq localPath -      , testCase "Should have the correct pattern"-        $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            , testCase "Should have the correct pattern"+            $ definition `rmap` Shikensu.pattern >>= assertEq pattern -      , testCase "Should have the correct workingDirname"-        $ definition `rmap` Shikensu.workingDirname >>= assertEq ""+            , testCase "Should have the correct workingDirname"+            $ definition `rmap` Shikensu.workingDirname >>= assertEq "" -      ]+            ]   testRootFile :: TestTree testRootFile =-  let-    pattern = "*.md"-    dictionary = fmap sort $ rootPath >>= Shikensu.list [pattern]-    definition = fmap List.head dictionary-    localPath = "README.md"-  in-    testGroup "Test file in root path"-      [ testCase "Should have the correct basename"-        $ definition `rmap` Shikensu.basename >>= assertEq "README"+    let+        pattern = "*.md"+        dictionary = fmap sort $ rootPath >>= Shikensu.listF [pattern]+        definition = fmap List.head dictionary+        localPath = "CHANGELOG.md"+    in+        testGroup "Test file in root path"+            [ testCase "Should have the correct basename"+            $ definition `rmap` Shikensu.basename >>= assertEq "CHANGELOG" -      , testCase "Should have the correct dirname"-        $ definition `rmap` Shikensu.dirname >>= assertEq ""+            , testCase "Should have the correct dirname"+            $ definition `rmap` Shikensu.dirname >>= assertEq "" -      , testCase "Should have the correct extname"-        $ definition `rmap` Shikensu.extname >>= assertEq ".md"+            , testCase "Should have the correct extname"+            $ definition `rmap` Shikensu.extname >>= assertEq ".md" -      , testCase "Should have the correct localPath"-        $ definition `rmap` Shikensu.localPath >>= assertEq localPath+            , testCase "Should have the correct localPath"+            $ definition `rmap` Shikensu.localPath >>= assertEq localPath -      , testCase "Should have the correct pattern"-        $ definition `rmap` Shikensu.pattern >>= assertEq pattern+            , testCase "Should have the correct pattern"+            $ definition `rmap` Shikensu.pattern >>= assertEq pattern -      , testCase "Should have the correct workingDirname"-        $ definition `rmap` Shikensu.workingDirname >>= assertEq ""+            , testCase "Should have the correct workingDirname"+            $ definition `rmap` Shikensu.workingDirname >>= assertEq "" -      ]+            ]
+ tests/Test/Utilities.hs view
@@ -0,0 +1,69 @@+module Test.Utilities (utilityTests) where++import Flow+import Shikensu.Types+import Shikensu.Utilities as Utils+import Test.Helpers+import Test.Tasty+import Test.Tasty.HUnit++import qualified Data.HashMap.Strict as HashMap (singleton)+import qualified Data.List as List (head)+import qualified Data.Tuple as Tuple (fst)+import qualified Shikensu+import qualified Shikensu.Contrib as Contrib+++utilityTests :: TestTree+utilityTests = testGroup+    "Utility tests"+    [testSequencing, testThunder]+++++-- Tests+++testSequencing :: TestTree+testSequencing =+    let+        result =+            Utils.lsequence+                [ ( "a", rootPath >>= Shikensu.listF ["tests/fixtures/example.md"] )+                , ( "b", rootPath >>= Shikensu.listF ["tests/fixtures/example.md"] )+                ]+    in+        testCase "Test lsequence"+        $ (List.head .> Tuple.fst) <$> result >>= assertEq "a"+++testThunder :: TestTree+testThunder =+    let+        flow =+            Contrib.insertMetadata (HashMap.singleton "a" "Hi!")+            .> return++        dictionary =+            rootPath+                >>= Shikensu.listF ["tests/fixtures/example.md"]+                >>= flow++        def =+            fmap List.head dictionary++        resultExisting =+            fmap (\d -> metadata d ⚡ "a" :: Maybe String) def++        resultNonExisting =+            fmap (\d -> metadata d ⚡ "b" :: Maybe String) def+    in+        testGroup+            "Test (⚡)"+            [ testCase "Existing"+            $ resultExisting >>= assertEq (Just "Hi!")++            , testCase "Non-existing"+            $ resultNonExisting >>= assertEq (Nothing)+            ]