{-# LANGUAGE ViewPatterns #-}
module Shrun.Utils
(
breakStripPoint,
truncateIfNeeded,
stripControlAll,
stripControlSmart,
escapeDoubleQuotes,
diffTime,
timeSpecToRelTime,
foldMap1,
parseByteText,
whileM_,
whenLeft,
untilJust,
unsafeListToNESeq,
(∸),
readStripUnderscores,
)
where
import Data.Bytes (Conversion (convert), SomeSize, parse)
import Data.Char (isControl, isLetter)
import Data.Either (either)
import Data.Sequence.NonEmpty qualified as NESeq
import Data.Text qualified as T
import Data.Text.Lazy qualified as TL
import Data.Text.Lazy.Builder (Builder)
import Data.Text.Lazy.Builder qualified as TLB
import Data.Time.Relative (RelativeTime, fromSeconds)
import Effects.Time (TimeSpec, diffTimeSpec)
import GHC.Exts (IsList (fromList))
import Shrun.Data.Text (UnlinedText)
import Shrun.Data.Text qualified as ShrunText
import Shrun.Prelude
import Text.Read (Read)
import Text.Read qualified as TR
diffTime :: TimeSpec -> TimeSpec -> Natural
diffTime :: TimeSpec -> TimeSpec -> Natural
diffTime TimeSpec
t1 TimeSpec
t2 = Optic' A_Lens NoIx TimeSpec Natural -> TimeSpec -> Natural
forall k (is :: IxList) s a.
Is k A_Getter =>
Optic' k is s a -> s -> a
view Optic' A_Lens NoIx TimeSpec Natural
#sec (TimeSpec -> Natural) -> TimeSpec -> Natural
forall a b. (a -> b) -> a -> b
$ TimeSpec -> TimeSpec -> TimeSpec
diffTimeSpec TimeSpec
t1 TimeSpec
t2
timeSpecToRelTime :: TimeSpec -> RelativeTime
timeSpecToRelTime :: TimeSpec -> RelativeTime
timeSpecToRelTime = Natural -> RelativeTime
fromSeconds (Natural -> RelativeTime)
-> (TimeSpec -> Natural) -> TimeSpec -> RelativeTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Optic' A_Lens NoIx TimeSpec Natural -> TimeSpec -> Natural
forall k (is :: IxList) s a.
Is k A_Getter =>
Optic' k is s a -> s -> a
view Optic' A_Lens NoIx TimeSpec Natural
#sec
foldMap1 :: (Foldable f, Semigroup s) => (a -> s) -> a -> f a -> s
foldMap1 :: forall (f :: Type -> Type) s a.
(Foldable f, Semigroup s) =>
(a -> s) -> a -> f a -> s
foldMap1 a -> s
f a
x f a
xs = (a -> (a -> s) -> a -> s) -> (a -> s) -> f a -> a -> s
forall a b. (a -> b -> b) -> b -> f a -> b
forall (t :: Type -> Type) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\a
b a -> s
g a
y -> a -> s
f a
y s -> s -> s
forall a. Semigroup a => a -> a -> a
<> a -> s
g a
b) a -> s
f f a
xs a
x
breakStripPoint :: Text -> Text -> Tuple2 Text Text
breakStripPoint :: Text -> Text -> Tuple2 Text Text
breakStripPoint Text
point Text
txt = case HasCallStack => Text -> Text -> Tuple2 Text Text
Text -> Text -> Tuple2 Text Text
T.breakOn Text
point Text
txt of
(Text
x, Text -> Text -> Maybe Text
T.stripPrefix Text
point -> Just Text
y) -> (Text
x, Text
y)
Tuple2 Text Text
pair -> Tuple2 Text Text
pair
truncateIfNeeded :: Natural -> Text -> Text
truncateIfNeeded :: Natural -> Text -> Text
truncateIfNeeded Natural
n Text
txt
| Text -> Int
T.length Text
txt Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
n' = Text
txt
| Bool
otherwise = Text
txt'
where
txt' :: Text
txt' = Int -> Text -> Text
T.take (Int
n' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
3) Text
txt Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"..."
n' :: Int
n' = Natural -> Int
n2i Natural
n
n2i :: Natural -> Int
n2i :: Natural -> Int
n2i = Natural -> Int
forall a b.
(Bits a, Bits b, HasCallStack, Integral a, Integral b, Show a,
Typeable a, Typeable b) =>
a -> b
unsafeConvertIntegral
stripControlAll :: UnlinedText -> UnlinedText
stripControlAll :: UnlinedText -> UnlinedText
stripControlAll =
(Text -> Text) -> UnlinedText -> UnlinedText
ShrunText.reallyUnsafeLiftUnlined ((Char -> Bool) -> Text -> Text
T.filter (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Char -> Bool
isControl) (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
stripAnsiAll)
stripControlSmart :: UnlinedText -> UnlinedText
stripControlSmart :: UnlinedText -> UnlinedText
stripControlSmart =
(Text -> Text) -> UnlinedText -> UnlinedText
ShrunText.reallyUnsafeLiftUnlined ((Char -> Bool) -> Text -> Text
T.filter Char -> Bool
ctrlToFilter (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
stripAnsiControl)
where
ctrlToFilter :: Char -> Bool
ctrlToFilter Char
c
| Char -> Bool
isControl Char
c = Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\ESC'
| Bool
otherwise = Bool
True
stripAnsiAll :: Text -> Text
stripAnsiAll :: Text -> Text
stripAnsiAll = [Text] -> Text
T.concat ([Text] -> Text) -> (Text -> [Text]) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Text, Text, Text) -> Text) -> [(Text, Text, Text)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
fmap (Optic' A_Lens NoIx (Text, Text, Text) Text
-> (Text, Text, Text) -> Text
forall k (is :: IxList) s a.
Is k A_Getter =>
Optic' k is s a -> s -> a
view Optic' A_Lens NoIx (Text, Text, Text) Text
forall s t a b. Field1 s t a b => Lens s t a b
_1) ([(Text, Text, Text)] -> [Text])
-> (Text -> [(Text, Text, Text)]) -> Text -> [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> [(Text, Text, Text)]
splitAnsi
stripAnsiControl :: Text -> Text
stripAnsiControl :: Text -> Text
stripAnsiControl Text
txt =
(Text -> (Text, Text, Text) -> Text)
-> Text -> [(Text, Text, Text)] -> Text
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: Type -> Type) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Text -> (Text, Text, Text) -> Text
forall {a}. Semigroup a => a -> (a, Text, a) -> a
f Text
"" [(Text, Text, Text)]
splitTxt
where
splitTxt :: [(Text, Text, Text)]
splitTxt = Text -> [(Text, Text, Text)]
splitAnsi Text
txt
f :: a -> (a, Text, a) -> a
f a
acc (a
preAnsi, Text
code, a
withAnsi)
| Text -> Bool
nonControlAnsi Text
code = a
acc a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
withAnsi
| Bool
otherwise = a
acc a -> a -> a
forall a. Semigroup a => a -> a -> a
<> a
preAnsi
nonControlAnsi :: Text -> Bool
nonControlAnsi :: Text -> Bool
nonControlAnsi Text
ansi = case Text -> Maybe (Text, Char)
T.unsnoc Text
ansi of
Just (Text
_, Char
'm') -> Bool
True
Maybe (Text, Char)
_ -> Bool
False
splitAnsi :: Text -> [(Text, Text, Text)]
splitAnsi :: Text -> [(Text, Text, Text)]
splitAnsi Text
"" = []
splitAnsi Text
t =
(Text
preAnsi, Text
ansiCode, Text
preAnsi Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
ansiCode) (Text, Text, Text) -> [(Text, Text, Text)] -> [(Text, Text, Text)]
forall a. a -> [a] -> [a]
: [(Text, Text, Text)]
rest
where
(!Text
preAnsi, !Text
withAnsiFull) = HasCallStack => Text -> Text -> Tuple2 Text Text
Text -> Text -> Tuple2 Text Text
T.breakOn Text
"\ESC[" Text
t
(!Text
ansiCodeNoChar, !Text
withAnsiChar) = (Char -> Bool) -> Text -> Tuple2 Text Text
T.break Char -> Bool
isLetter Text
withAnsiFull
(!Text
ansiCode, ![(Text, Text, Text)]
rest) = case Text -> Maybe (Char, Text)
T.uncons Text
withAnsiChar of
Just (!Char
ansiChar, !Text
rest') -> (Text -> Char -> Text
T.snoc Text
ansiCodeNoChar Char
ansiChar, Text -> [(Text, Text, Text)]
splitAnsi Text
rest')
Maybe (Char, Text)
Nothing -> (Text
ansiCodeNoChar, [])
parseByteText :: Text -> Either Text (Bytes B Natural)
parseByteText :: Text -> Either Text (Bytes 'B Natural)
parseByteText Text
txt =
case forall a. Parser a => Text -> Either Text a
parse @(SomeSize Natural) Text
txt of
Right SomeSize Natural
b -> Bytes 'B Natural -> Either Text (Bytes 'B Natural)
forall a b. b -> Either a b
Right (Bytes 'B Natural -> Either Text (Bytes 'B Natural))
-> Bytes 'B Natural -> Either Text (Bytes 'B Natural)
forall a b. (a -> b) -> a -> b
$ Proxy 'B -> SomeSize Natural -> Converted 'B (SomeSize Natural)
forall a (t :: Size).
(Conversion a, SingSize t) =>
Proxy t -> a -> Converted t a
forall (t :: Size).
SingSize t =>
Proxy t -> SomeSize Natural -> Converted t (SomeSize Natural)
convert (forall {k} (t :: k). Proxy t
forall (t :: Size). Proxy t
Proxy @B) SomeSize Natural
b
Left Text
_ -> case forall a. Parser a => Text -> Either Text a
parse @(SomeSize Double) Text
txt of
Right SomeSize Double
b -> Bytes 'B Natural -> Either Text (Bytes 'B Natural)
forall a b. b -> Either a b
Right (Double -> Natural
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
truncate (Double -> Natural) -> Bytes 'B Double -> Bytes 'B Natural
forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> Proxy 'B -> SomeSize Double -> Converted 'B (SomeSize Double)
forall a (t :: Size).
(Conversion a, SingSize t) =>
Proxy t -> a -> Converted t a
forall (t :: Size).
SingSize t =>
Proxy t -> SomeSize Double -> Converted t (SomeSize Double)
convert (forall {k} (t :: k). Proxy t
forall (t :: Size). Proxy t
Proxy @B) SomeSize Double
b)
Left Text
err -> Text -> Either Text (Bytes 'B Natural)
forall a b. a -> Either a b
Left Text
err
whenLeft :: (Applicative f) => Either a b -> (a -> f ()) -> f ()
whenLeft :: forall (f :: Type -> Type) a b.
Applicative f =>
Either a b -> (a -> f ()) -> f ()
whenLeft Either a b
e a -> f ()
action = (a -> f ()) -> (b -> f ()) -> Either a b -> f ()
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either a -> f ()
action (f () -> b -> f ()
forall a b. a -> b -> a
const (() -> f ()
forall a. a -> f a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure ())) Either a b
e
whileM_ :: (Monad m) => m Bool -> m a -> m ()
whileM_ :: forall (m :: Type -> Type) a. Monad m => m Bool -> m a -> m ()
whileM_ m Bool
mb m a
ma = m ()
go
where
go :: m ()
go =
m Bool
mb m Bool -> (Bool -> m ()) -> m ()
forall a b. m a -> (a -> m b) -> m b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Bool
True -> m a
ma m a -> m () -> m ()
forall a b. m a -> m b -> m b
forall (f :: Type -> Type) a b. Applicative f => f a -> f b -> f b
*> m ()
go
Bool
False -> () -> m ()
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure ()
untilJust :: (Monad m) => m (Maybe b) -> m b
untilJust :: forall (m :: Type -> Type) b. Monad m => m (Maybe b) -> m b
untilJust m (Maybe b)
m = m b
go
where
go :: m b
go =
m (Maybe b)
m m (Maybe b) -> (Maybe b -> m b) -> m b
forall a b. m a -> (a -> m b) -> m b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
Maybe b
Nothing -> m b
go
Just b
x -> b -> m b
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure b
x
unsafeListToNESeq :: (HasCallStack) => List a -> NESeq a
unsafeListToNESeq :: forall a. HasCallStack => List a -> NESeq a
unsafeListToNESeq [] = [Char] -> NESeq a
forall a. HasCallStack => [Char] -> a
error [Char]
"[Shrun.Utils]: empty list"
unsafeListToNESeq [a]
xs = NonEmpty a -> NESeq a
forall a. NonEmpty a -> NESeq a
NESeq.fromList (NonEmpty a -> NESeq a) -> NonEmpty a -> NESeq a
forall a b. (a -> b) -> a -> b
$ [Item (NonEmpty a)] -> NonEmpty a
forall l. IsList l => [Item l] -> l
fromList [a]
[Item (NonEmpty a)]
xs
escapeDoubleQuotes :: Text -> Text
escapeDoubleQuotes :: Text -> Text
escapeDoubleQuotes = LazyText -> Text
TL.toStrict (LazyText -> Text) -> (Text -> LazyText) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyText
TLB.toLazyText (Builder -> LazyText) -> (Text -> Builder) -> Text -> LazyText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Builder -> Char -> Builder) -> Builder -> Text -> Builder
forall a. (a -> Char -> a) -> a -> Text -> a
T.foldl' Builder -> Char -> Builder
go Builder
""
where
go :: Builder -> Char -> Builder
go :: Builder -> Char -> Builder
go Builder
acc Char
'"' = Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"\\\""
go Builder
acc Char
c = Builder
acc Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
TLB.singleton Char
c
(∸) :: (Ord a, Num a) => a -> a -> a
a
x ∸ :: forall a. (Ord a, Num a) => a -> a -> a
∸ a
y =
if a
y a -> a -> Bool
forall a. Ord a => a -> a -> Bool
> a
x
then a
0
else a
x a -> a -> a
forall a. Num a => a -> a -> a
- a
y
infixl 6 ∸
readStripUnderscores :: (MonadFail m, Read a) => Text -> m a
readStripUnderscores :: forall (m :: Type -> Type) a. (MonadFail m, Read a) => Text -> m a
readStripUnderscores Text
t = case [Char] -> Either [Char] a
forall a. Read a => [Char] -> Either [Char] a
TR.readEither [Char]
s of
Left [Char]
err -> [Char] -> m a
forall a. [Char] -> m a
forall (m :: Type -> Type) a. MonadFail m => [Char] -> m a
fail ([Char] -> m a) -> [Char] -> m a
forall a b. (a -> b) -> a -> b
$ [Char]
"Could not read '" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
s [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"': " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
err
Right a
x -> a -> m a
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure a
x
where
noUnderscores :: Text
noUnderscores = HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
T.replace Text
"_" Text
"" Text
t
s :: [Char]
s = Text -> [Char]
T.unpack Text
noUnderscores