{-# LANGUAGE OverloadedStrings, LambdaCase #-}
module Hledger.Utils (
module Hledger.Utils,
module Hledger.Utils.Debug,
module Hledger.Utils.Parse,
module Hledger.Utils.Regex,
module Hledger.Utils.String,
module Hledger.Utils.Text,
module Hledger.Utils.Test,
module Hledger.Utils.Color,
module Hledger.Utils.Tree,
error',userError',usageError,
)
where
import Control.Monad (liftM, when)
import Data.FileEmbed (makeRelativeToProject, embedStringFile)
import Data.List
import Data.Text (Text)
import qualified Data.Text.IO as T
import Data.Time.Clock
import Data.Time.LocalTime
import Language.Haskell.TH.Syntax (Q, Exp)
import System.Directory (getHomeDirectory)
import System.FilePath((</>), isRelative)
import System.IO
import Hledger.Utils.Debug
import Hledger.Utils.Parse
import Hledger.Utils.Regex
import Hledger.Utils.String
import Hledger.Utils.Text
import Hledger.Utils.Test
import Hledger.Utils.Color
import Hledger.Utils.Tree
import Hledger.Utils.UTF8IOCompat (error',userError',usageError)
first3 :: (a, b, c) -> a
first3 (a
x,b
_,c
_) = a
x
second3 :: (a, b, c) -> b
second3 (a
_,b
x,c
_) = b
x
third3 :: (a, b, c) -> c
third3 (a
_,b
_,c
x) = c
x
first4 :: (a, b, c, d) -> a
first4 (a
x,b
_,c
_,d
_) = a
x
second4 :: (a, b, c, d) -> b
second4 (a
_,b
x,c
_,d
_) = b
x
third4 :: (a, b, c, d) -> c
third4 (a
_,b
_,c
x,d
_) = c
x
fourth4 :: (a, b, c, d) -> d
fourth4 (a
_,b
_,c
_,d
x) = d
x
first5 :: (a, b, c, d, e) -> a
first5 (a
x,b
_,c
_,d
_,e
_) = a
x
second5 :: (a, b, c, d, e) -> b
second5 (a
_,b
x,c
_,d
_,e
_) = b
x
third5 :: (a, b, c, d, e) -> c
third5 (a
_,b
_,c
x,d
_,e
_) = c
x
fourth5 :: (a, b, c, d, e) -> d
fourth5 (a
_,b
_,c
_,d
x,e
_) = d
x
fifth5 :: (a, b, c, d, e) -> e
fifth5 (a
_,b
_,c
_,d
_,e
x) = e
x
first6 :: (a, b, c, d, e, f) -> a
first6 (a
x,b
_,c
_,d
_,e
_,f
_) = a
x
second6 :: (a, b, c, d, e, f) -> b
second6 (a
_,b
x,c
_,d
_,e
_,f
_) = b
x
third6 :: (a, b, c, d, e, f) -> c
third6 (a
_,b
_,c
x,d
_,e
_,f
_) = c
x
fourth6 :: (a, b, c, d, e, f) -> d
fourth6 (a
_,b
_,c
_,d
x,e
_,f
_) = d
x
fifth6 :: (a, b, c, d, e, f) -> e
fifth6 (a
_,b
_,c
_,d
_,e
x,f
_) = e
x
sixth6 :: (a, b, c, d, e, f) -> f
sixth6 (a
_,b
_,c
_,d
_,e
_,f
x) = f
x
curry2 :: ((a, b) -> c) -> a -> b -> c
curry2 :: ((a, b) -> c) -> a -> b -> c
curry2 (a, b) -> c
f a
x b
y = (a, b) -> c
f (a
x, b
y)
uncurry2 :: (a -> b -> c) -> (a, b) -> c
uncurry2 :: (a -> b -> c) -> (a, b) -> c
uncurry2 a -> b -> c
f (a
x, b
y) = a -> b -> c
f a
x b
y
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 :: ((a, b, c) -> d) -> a -> b -> c -> d
curry3 (a, b, c) -> d
f a
x b
y c
z = (a, b, c) -> d
f (a
x, b
y, c
z)
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 :: (a -> b -> c -> d) -> (a, b, c) -> d
uncurry3 a -> b -> c -> d
f (a
x, b
y, c
z) = a -> b -> c -> d
f a
x b
y c
z
curry4 :: ((a, b, c, d) -> e) -> a -> b -> c -> d -> e
curry4 :: ((a, b, c, d) -> e) -> a -> b -> c -> d -> e
curry4 (a, b, c, d) -> e
f a
w b
x c
y d
z = (a, b, c, d) -> e
f (a
w, b
x, c
y, d
z)
uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
uncurry4 :: (a -> b -> c -> d -> e) -> (a, b, c, d) -> e
uncurry4 a -> b -> c -> d -> e
f (a
w, b
x, c
y, d
z) = a -> b -> c -> d -> e
f a
w b
x c
y d
z
splitAtElement :: Eq a => a -> [a] -> [[a]]
splitAtElement :: a -> [a] -> [[a]]
splitAtElement a
x [a]
l =
case [a]
l of
[] -> []
a
e:[a]
es | a
ea -> a -> Bool
forall a. Eq a => a -> a -> Bool
==a
x -> [a] -> [[a]]
split [a]
es
[a]
es -> [a] -> [[a]]
split [a]
es
where
split :: [a] -> [[a]]
split [a]
es = let ([a]
first,[a]
rest) = (a -> Bool) -> [a] -> ([a], [a])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (a
xa -> a -> Bool
forall a. Eq a => a -> a -> Bool
==) [a]
es
in [a]
first [a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: a -> [a] -> [[a]]
forall a. Eq a => a -> [a] -> [[a]]
splitAtElement a
x [a]
rest
getCurrentLocalTime :: IO LocalTime
getCurrentLocalTime :: IO LocalTime
getCurrentLocalTime = do
UTCTime
t <- IO UTCTime
getCurrentTime
TimeZone
tz <- IO TimeZone
getCurrentTimeZone
LocalTime -> IO LocalTime
forall (m :: * -> *) a. Monad m => a -> m a
return (LocalTime -> IO LocalTime) -> LocalTime -> IO LocalTime
forall a b. (a -> b) -> a -> b
$ TimeZone -> UTCTime -> LocalTime
utcToLocalTime TimeZone
tz UTCTime
t
getCurrentZonedTime :: IO ZonedTime
getCurrentZonedTime :: IO ZonedTime
getCurrentZonedTime = do
UTCTime
t <- IO UTCTime
getCurrentTime
TimeZone
tz <- IO TimeZone
getCurrentTimeZone
ZonedTime -> IO ZonedTime
forall (m :: * -> *) a. Monad m => a -> m a
return (ZonedTime -> IO ZonedTime) -> ZonedTime -> IO ZonedTime
forall a b. (a -> b) -> a -> b
$ TimeZone -> UTCTime -> ZonedTime
utcToZonedTime TimeZone
tz UTCTime
t
applyN :: Int -> (a -> a) -> a -> a
applyN :: Int -> (a -> a) -> a -> a
applyN Int
n a -> a
f | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
1 = a -> a
forall a. a -> a
id
| Bool
otherwise = ([a] -> Int -> a
forall a. [a] -> Int -> a
!! Int
n) ([a] -> a) -> (a -> [a]) -> a -> a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> a) -> a -> [a]
forall a. (a -> a) -> a -> [a]
iterate a -> a
f
expandPath :: FilePath -> FilePath -> IO FilePath
expandPath :: FilePath -> FilePath -> IO FilePath
expandPath FilePath
_ FilePath
"-" = FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
"-"
expandPath FilePath
curdir FilePath
p = (if FilePath -> Bool
isRelative FilePath
p then (FilePath
curdir FilePath -> FilePath -> FilePath
</>) else FilePath -> FilePath
forall a. a -> a
id) (FilePath -> FilePath) -> IO FilePath -> IO FilePath
forall (m :: * -> *) a1 r. Monad m => (a1 -> r) -> m a1 -> m r
`liftM` FilePath -> IO FilePath
expandHomePath FilePath
p
expandHomePath :: FilePath -> IO FilePath
expandHomePath :: FilePath -> IO FilePath
expandHomePath = \case
(Char
'~':Char
'/':FilePath
p) -> (FilePath -> FilePath -> FilePath
</> FilePath
p) (FilePath -> FilePath) -> IO FilePath -> IO FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO FilePath
getHomeDirectory
(Char
'~':Char
'\\':FilePath
p) -> (FilePath -> FilePath -> FilePath
</> FilePath
p) (FilePath -> FilePath) -> IO FilePath -> IO FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO FilePath
getHomeDirectory
(Char
'~':FilePath
_) -> IOError -> IO FilePath
forall a. IOError -> IO a
ioError (IOError -> IO FilePath) -> IOError -> IO FilePath
forall a b. (a -> b) -> a -> b
$ FilePath -> IOError
userError FilePath
"~USERNAME in paths is not supported"
FilePath
p -> FilePath -> IO FilePath
forall (m :: * -> *) a. Monad m => a -> m a
return FilePath
p
readFilePortably :: FilePath -> IO Text
readFilePortably :: FilePath -> IO Text
readFilePortably FilePath
f = FilePath -> IOMode -> IO Handle
openFile FilePath
f IOMode
ReadMode IO Handle -> (Handle -> IO Text) -> IO Text
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Handle -> IO Text
readHandlePortably
readFileOrStdinPortably :: String -> IO Text
readFileOrStdinPortably :: FilePath -> IO Text
readFileOrStdinPortably FilePath
f = FilePath -> IOMode -> IO Handle
openFileOrStdin FilePath
f IOMode
ReadMode IO Handle -> (Handle -> IO Text) -> IO Text
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Handle -> IO Text
readHandlePortably
where
openFileOrStdin :: String -> IOMode -> IO Handle
openFileOrStdin :: FilePath -> IOMode -> IO Handle
openFileOrStdin FilePath
"-" IOMode
_ = Handle -> IO Handle
forall (m :: * -> *) a. Monad m => a -> m a
return Handle
stdin
openFileOrStdin FilePath
f IOMode
m = FilePath -> IOMode -> IO Handle
openFile FilePath
f IOMode
m
readHandlePortably :: Handle -> IO Text
readHandlePortably :: Handle -> IO Text
readHandlePortably Handle
h = do
Handle -> NewlineMode -> IO ()
hSetNewlineMode Handle
h NewlineMode
universalNewlineMode
Maybe TextEncoding
menc <- Handle -> IO (Maybe TextEncoding)
hGetEncoding Handle
h
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ((TextEncoding -> FilePath) -> Maybe TextEncoding -> Maybe FilePath
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap TextEncoding -> FilePath
forall a. Show a => a -> FilePath
show Maybe TextEncoding
menc Maybe FilePath -> Maybe FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
"UTF-8") (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
Handle -> TextEncoding -> IO ()
hSetEncoding Handle
h TextEncoding
utf8_bom
Handle -> IO Text
T.hGetContents Handle
h
maximum' :: Integral a => [a] -> a
maximum' :: [a] -> a
maximum' [] = a
0
maximum' [a]
xs = [a] -> a
forall a. Ord a => [a] -> a
maximumStrict [a]
xs
{-# INLINABLE sumStrict #-}
sumStrict :: Num a => [a] -> a
sumStrict :: [a] -> a
sumStrict = (a -> a -> a) -> a -> [a] -> a
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' a -> a -> a
forall a. Num a => a -> a -> a
(+) a
0
{-# INLINABLE maximumStrict #-}
maximumStrict :: Ord a => [a] -> a
maximumStrict :: [a] -> a
maximumStrict = (a -> a -> a) -> [a] -> a
forall a. (a -> a -> a) -> [a] -> a
foldl1' a -> a -> a
forall a. Ord a => a -> a -> a
max
{-# INLINABLE minimumStrict #-}
minimumStrict :: Ord a => [a] -> a
minimumStrict :: [a] -> a
minimumStrict = (a -> a -> a) -> [a] -> a
forall a. (a -> a -> a) -> [a] -> a
foldl1' a -> a -> a
forall a. Ord a => a -> a -> a
min
{-# INLINABLE sequence' #-}
sequence' :: Monad f => [f a] -> f [a]
sequence' :: [f a] -> f [a]
sequence' [f a]
ms = do
[a] -> [a]
h <- ([a] -> [a]) -> [f a] -> f ([a] -> [a])
forall (m :: * -> *) a c.
Monad m =>
([a] -> c) -> [m a] -> m ([a] -> c)
go [a] -> [a]
forall a. a -> a
id [f a]
ms
[a] -> f [a]
forall (m :: * -> *) a. Monad m => a -> m a
return ([a] -> [a]
h [])
where
go :: ([a] -> c) -> [m a] -> m ([a] -> c)
go [a] -> c
h [] = ([a] -> c) -> m ([a] -> c)
forall (m :: * -> *) a. Monad m => a -> m a
return [a] -> c
h
go [a] -> c
h (m a
m:[m a]
ms) = do
a
x <- m a
m
([a] -> c) -> [m a] -> m ([a] -> c)
go ([a] -> c
h ([a] -> c) -> ([a] -> [a]) -> [a] -> c
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a
x a -> [a] -> [a]
forall a. a -> [a] -> [a]
:)) [m a]
ms
{-# INLINABLE mapM' #-}
mapM' :: Monad f => (a -> f b) -> [a] -> f [b]
mapM' :: (a -> f b) -> [a] -> f [b]
mapM' a -> f b
f = [f b] -> f [b]
forall (f :: * -> *) a. Monad f => [f a] -> f [a]
sequence' ([f b] -> f [b]) -> ([a] -> [f b]) -> [a] -> f [b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> f b) -> [a] -> [f b]
forall a b. (a -> b) -> [a] -> [b]
map a -> f b
f
embedFileRelative :: FilePath -> Q Exp
embedFileRelative :: FilePath -> Q Exp
embedFileRelative FilePath
f = FilePath -> Q FilePath
makeRelativeToProject FilePath
f Q FilePath -> (FilePath -> Q Exp) -> Q Exp
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FilePath -> Q Exp
embedStringFile
tests_Utils :: TestTree
tests_Utils = FilePath -> [TestTree] -> TestTree
tests FilePath
"Utils" [
TestTree
tests_Text
]