hydrogen-prelude 0.9 → 0.10
raw patch · 5 files changed
+168/−15 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Hydrogen.Prelude: firstJust :: [a -> Maybe b] -> a -> Maybe b
+ Hydrogen.Prelude: safeHeadAndTail :: a -> [a] -> (a, [a])
+ Hydrogen.Prelude: safeHeadAndTail2 :: a -> a -> [a] -> (a, a, [a])
+ Hydrogen.Prelude.System: escapeFileName :: String -> String
+ Hydrogen.Prelude.System: findFilesRecursively :: (FilePath -> IO Bool) -> FilePath -> IO [FilePath]
+ Hydrogen.Prelude.System: findFilesRecursivelyWithContext :: (c -> FilePath -> [FilePath] -> IO c) -> (FilePath -> IO Bool) -> c -> FilePath -> IO [(FilePath, c)]
+ Hydrogen.Prelude.System: unescapeFileName :: String -> Maybe String
Files
- CHANGELOG.md +5/−0
- README.md +46/−11
- hydrogen-prelude.cabal +1/−1
- src/Hydrogen/Prelude.hs +36/−3
- src/Hydrogen/Prelude/System.hs +80/−0
CHANGELOG.md view
@@ -61,3 +61,8 @@ + Added `class Container (Contained, (?))` + `fmap` is exported as `map` +## v0.10++ Added `safeHeadAndTail`, `safeHeadAndTail2`, `firstJust`++ Little documentation improvements++ Added `findFilesRecursively` and `findFilesRecursivelyWithContext`+ in `Hydrogen.Prelude.System`
README.md view
@@ -138,7 +138,7 @@ The Hydrogen Prelude offers you the functions and datatypes from these modules, all with one import: -+ from `base`++ from [`base`](http://hackage.haskell.org/package/base) + `module Prelude` + `module Control.Applicative` + `module Control.Arrow`@@ -168,44 +168,79 @@ + `module Numeric` + `module Text.Printf` -+ from `array`++ from [`array`](http://hackage.haskell.org/package/array) + `module Data.Array` -+ from `cereal`++ from [`cereal`](http://hackage.haskell.org/package/cereal) + `module Data.Serialize` -+ from `containers`++ from [`containers`](http://hackage.haskell.org/package/containers) + `Data.Set`, `Data.Map`, and `Data.Seq` -+ from `hashable`++ from [`hashable`](http://hackage.haskell.org/package/hashable) + `module Data.Hashable` -+ form `hydrogen-multimap`++ form [`hydrogen-multimap`](http://hackage.haskell.org/package/hydrogen-multimap) + `Hydrogen.MultiMap` -+ from `hydrogen-version`++ from [`hydrogen-version`](http://hackage.haskell.org/package/hydrogen-version) + `module Hydrogen.Version` -+ from `regex-tdfa`++ from [`regex-tdfa`](http://hackage.haskell.org/package/regex-tdfa) + `module Text.Regex.TDFA` -+ from `time`++ from [`time`](http://hackage.haskell.org/package/time) + `module Data.Time` -+ from `transformers`++ from [`transformers`](http://hackage.haskell.org/package/transformers) + `module Data.Functor.Identity` + `module Data.Functor.Reverse` -+ from `uuid`++ from [`uuid`](http://hackage.haskell.org/package/uuid) + `Data.UUID` + `Data.UUID.fromString` as `uuidFromString` + `Data.UUID.V4.nextRandom` as `randomUUID` + ### Hydrogen.Prelude.IO ++ from [`base`](http://hackage.haskell.org/package/base)+ + `module Data.IORef`+ + `module Control.Concurret`+ + `module Control.Exception`+ + `module System.IO`+ + `module System.Timeout`+++ from [`strict`](http://hackage.haskell.org/package/strict)+ + strict IO functions `hGetContents'`, `getContents'`, `readFile'`, `interact'`++ ### Hydrogen.Prelude.System ++ from [`base`](http://hackage.haskell.org/package/base)+ + `module System.CPUTime`+ + `module System.Environment`+ + `module System.Exit`+ + `module System.Info`+++ from [`directory`](http://hackage.haskell.org/package/directory)+ + `module System.Directory`+++ from [`filepath`](http://hackage.haskell.org/package/filepath)+ + `module System.FilePath`+++ from [`process`](http://hackage.haskell.org/package/process)+ + `module System.Process`+++ from [`random`](http://hackage.haskell.org/package/random)+ + `module System.Random`++ ### Hydrogen.Prelude.Network+++ from [`network`](http://hackage.haskell.org/package/network)+ + `module Network`+ FAQ ===
hydrogen-prelude.cabal view
@@ -1,5 +1,5 @@ name: hydrogen-prelude-version: 0.9+version: 0.10 homepage: http://scravy.de/hydrogen-prelude/ synopsis: Hydrogen Prelude license: MIT
src/Hydrogen/Prelude.hs view
@@ -27,6 +27,7 @@ , module Data.Serialize , module Data.String , module Data.Time+ , module Data.Time.Calendar.OrdinalDate , module Data.Traversable , module Data.Tuple , module Data.Typeable@@ -43,6 +44,9 @@ , uuidFromString , randomUUID , safeHead+ , safeHeadAndTail+ , safeHeadAndTail2+ , firstJust , map , UUID , Generic@@ -142,6 +146,7 @@ import "cereal" Data.Serialize import "base" Data.String import "time" Data.Time+import "time" Data.Time.Calendar.OrdinalDate import "base" Data.Traversable import "base" Data.Tuple import "base" Data.Typeable@@ -211,23 +216,51 @@ f .^ g = \x -> f x /= g x (|>) :: a -> (a -> b) -> b+-- ^ @flip ('$')@ (|>) = flip ($) __ :: a+-- ^ A shorthand for 'undefined'. __ = error "Hydrogen.Prelude.undefined" uuidFromString :: String -> Maybe UUID uuidFromString = Data.UUID.fromString randomUUID :: IO UUID+-- ^ Produces a random V4 UUID (alias for 'Data.UUID.V4.nextRandom'). randomUUID = Data.UUID.V4.nextRandom -safeHead :: a -> [a] -> a-safeHead d = \case+safeHead+ :: a -- ^ The default value for the case of the empty list.+ -> [a] -- ^ The list.+ -> a+-- ^ Returns the head of the list or the default value.+safeHead def = \case x : _ -> x- _ -> d+ _ -> def +safeHeadAndTail :: a -> [a] -> (a, [a])+safeHeadAndTail def = \case+ x : xs -> (x, xs)+ _ -> (def, [])++safeHeadAndTail2 :: a -> a -> [a] -> (a, a, [a])+safeHeadAndTail2 d1 d2 = \case+ x : y : xs -> (x, y, xs)+ x : xs -> (x, d2, xs)+ xs -> (d1, d2, xs)++firstJust :: [a -> Maybe b] -> a -> Maybe b+-- ^ Applies a bunch of functions on a given value,+-- returns the first result that is not Nothing+-- (or 'Nothing' if no 'Just' value was produced).+firstJust (f : fs) v = case f v of+ Nothing -> firstJust fs v+ x -> x+firstJust [] _ = Nothing+ map :: Functor f => (a -> b) -> f a -> f b+-- ^ map as it should be: 'fmap'. map = fmap class TMap a where
src/Hydrogen/Prelude/System.hs view
@@ -1,3 +1,5 @@+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}+ module Hydrogen.Prelude.System ( module Hydrogen.Prelude.IO , module System.CPUTime@@ -8,6 +10,10 @@ , module System.Info , module System.Process , module System.Random+ , findFilesRecursively+ , findFilesRecursivelyWithContext+ , escapeFileName+ , unescapeFileName ) where import Hydrogen.Prelude.IO@@ -20,4 +26,78 @@ import "base" System.Info import "process" System.Process import "random" System.Random++import qualified Data.Set as Set+++findFilesRecursively :: (FilePath -> IO Bool) -> FilePath -> IO [FilePath]+findFilesRecursively f dir =+ map fst <$> findFilesRecursivelyWithContext (\c _ _ -> return c) f () dir+++findFilesRecursivelyWithContext+ :: forall c.+ (c -> FilePath -> [FilePath] -> IO c) -- ^ update function for current context+ -> (FilePath -> IO Bool) -- ^ predicate to filter files+ -> c -- ^ current context+ -> FilePath -> IO [(FilePath, c)]+findFilesRecursivelyWithContext updater predicate context dir = do++ cwd <- getCurrentDirectory+ snd <$> find Set.empty context (cwd </> dir)++ where++ find :: Set FilePath -> c -> FilePath -> IO (Set FilePath, [(FilePath, c)])+ find visited context dir = do++ thisDirectory <- canonicalizePath dir+ if | Set.member thisDirectory visited -> return (Set.empty, [])+ | otherwise -> do++ allFiles <- map (dir </>) <$> getDirectoryContents dir+ theFiles <- filterFiles allFiles+ theDirs <- filterM isDir allFiles+ context' <- updater context dir theFiles++ let visited' = Set.insert thisDirectory visited+ f (visited, files) dir = do+ (visited', files') <- find visited context' dir+ return (visited', files' : files)++ (visited'', files') <- foldM f (visited', []) theDirs+ + return (visited'', concat (zip theFiles (repeat context') : files'))++ filterFiles = filterM (\x -> liftM2 (&&) (doesFileExist x) (predicate x))+ isDir x = liftM2 (&&) (doesDirectoryExist x) (return (head (takeFileName x) /= '.'))+++escapeFileName :: String -> String+escapeFileName s = case s of+ ('/' : xs)+ -> '_' : escapeFileName xs+ (x : xs) ->+ if | isSafeChar x -> x : escapeFileName xs+ | ord x <= 255 -> '$' : printf "%02X" (ord x) ++ escapeFileName xs+ | otherwise -> "$$" ++ printf "%04X" (ord x) ++ escapeFileName xs+ [] -> []+ where+ isSafeChar x = isAscii x && isAlphaNum x || x `elem` ".-"+++unescapeFileName :: String -> Maybe String+unescapeFileName s = case s of+ ('$' : '$' : a : b : c : d : xs)+ -> (chr <$> hexnum (a : b : c : [d])) `cons` unescapeFileName xs+ ('$' : a : b : xs)+ -> (chr <$> hexnum (a : [b])) `cons` unescapeFileName xs+ ('_' : xs)+ -> pure '/' `cons` unescapeFileName xs+ (x : xs)+ -> pure x `cons` unescapeFileName xs+ [] -> return []+ where+ cons = liftA2 (:)+ hexnum = fmap fst . listToMaybe . readHex