shrun
Safe HaskellNone
LanguageGHC2021

Shrun.Utils

Description

Provides utilities.

Synopsis

Text Utils

breakStripPoint :: Text -> Text -> (Text, Text) Source #

Wrapper for Text's breakOn that differs in that:

  1. If the needle is found within the haystack, we do not include it in the second part of the pair.

Examples

Expand
>>> -- Data.Text
>>> T.breakOn "=" "HEY=LISTEN"
("HEY","=LISTEN")
>>> -- Shrun.Utils.Text
>>> breakStripPoint "=" "HEY=LISTEN"
("HEY","LISTEN")

Other examples:

>>> breakStripPoint "=" "HEYLISTEN"
("HEYLISTEN","")
>>> breakStripPoint "=" "=HEYLISTEN"
("","HEYLISTEN")
>>> breakStripPoint "=" "HEYLISTEN="
("HEYLISTEN","")
>>> breakStripPoint "=" "HEY==LISTEN"
("HEY","=LISTEN")

truncateIfNeeded :: Int -> Text -> Text Source #

For Natural \(n\) and Text \(t = t_0 t_1 \ldots t_m\), truncates \(t\) if \(m > n\). In this case, \(t\) is truncated to \(n - 3\), and an ellipsis ( \(\ldots\) ) is appended. We are left with a string with length exactly \(n\):

\[ t_0 t_1 \ldots t_{n-3} \text{...} \quad \text{-- 3 literal } `\text{.' chars appended} \]

Examples

Expand
>>> truncateIfNeeded 7 "hi"
"hi"
>>> truncateIfNeeded 10 "This is 21 chars long"
"This is..."

stripControlAll :: Text -> Text Source #

Strips all control chars, including ansi escape sequences.

Examples

Expand
>>> stripControlAll "foo\ESC[0;3Abar \n baz"
"foobar  baz"

stripControlSmart :: Text -> Text Source #

Strips control chars, including most ansi escape sequences. We leave behind SGR ansi escape sequences e.g. text coloring. See https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters.

Examples

Expand
>>> stripControlSmart "foo\ESC[0;3Abar \n baz"
"foobar  baz"
>>> stripControlSmart "foo\ESC[0;3mbar \n baz"
"foo\ESC[0;3mbar  baz"

escapeDoubleQuotes :: Text -> Text Source #

Escape double quotes in strings.

MonadTime Utils

diffTime :: TimeSpec -> TimeSpec -> Natural Source #

For given \(x, y\), returns the absolute difference \(|x - y|\) in seconds.

Examples

Expand
>>> :{
  let t1 = MkTimeSpec 5 0
      -- 20 s + 1 billion ns = 21 s
      t2 = MkTimeSpec 20 1_000_000_000
  in diffTime t1 t2
:}
16

Terminal input

hWithHidden :: forall (p :: HandleMode) m a. (CanRead p, CanWrite p, MonadMask m, MonadHandleReader m, MonadHandleWriter m) => Handle p -> m a -> m a Source #

withHiddenInput :: (MonadMask m, MonadHandleReader m, MonadHandleWriter m) => m a -> m a Source #

Hides stdin input. Some caveats:

  • Tragically, this does not prevent sudo from hijacking stdin e.g.

    shrun "sudo ls && sleep 10"

will launch the prompt which will overwrite the terminal. Oh well.

  • It does not swallow stdin e.g. read stdin and throw it away. For example, if the user types in a bunch of stuff, it will be buffered in memory then printed / executed (e.g. enter key) after shrun finishes.

For the main console, this is handled by drainStdin.

hHide :: forall (p :: HandleMode) m. (CanWrite p, MonadHandleWriter m) => Handle p -> m () Source #

drainStdin :: (MonadCatch m, MonadHandleReader m) => m () Source #

Drains stdin.

Text parsing

inverseMap Source #

Arguments

:: (Bounded a, Enum a, Ord k) 
=> (a -> k)

Injection.

-> k

Key.

-> Maybe a 

Parses a finite type from an injective function.

inverseMapFail Source #

Arguments

:: (Bounded a, Enum a, MonadFail m) 
=> (a -> Text)

Text injection.

-> Text

Field name.

-> (Bool, [Text])

Field metavar.

-> Text

Key.

-> m a 

inverseMap with a Text injection that fails via MonadFail and fmtUnrecognizedError. Intended for parsing config values.

inversePretty Source #

Arguments

:: (Bounded a, Enum a, Pretty a) 
=> Text

Key.

-> Maybe a 

inverseMap with text keys given by a Pretty instance.

inversePrettyFail Source #

Arguments

:: (Bounded a, Enum a, MonadFail m, Pretty a) 
=> Text

Field name.

-> (Bool, [Text])

Field metavar.

-> Text

Key.

-> m a 

inverseMapFail with Pretty instance.

Misc Utils

atomicReadWrite Source #

Arguments

:: (HasCallStack, MonadAtomic m, MonadMask m) 
=> TBQueue a

Queue from which to read.

-> (a -> m b)

Function to apply.

-> m () 

Reads from a queue and applies the function, if we receive a value. Atomic in the sense that if a read is successful, then we will apply the given function, even if an async exception is raised.

fmtUnrecognizedError Source #

Arguments

:: (IsString a, Monoid a) 
=> a

Field name.

-> (Bool, [a])

(Include off?, Valid values).

-> a

Bad unrecognized value or error message.

-> a

Error message.

Provides a standard format for "unrecognized param" failures.

mkMetaStr :: (IsString a, Monoid a) => (Bool, [a]) -> a Source #

parseByteText :: Text -> Either Text (Bytes 'B Natural) Source #

Parses bytes with arbitrary units and converts to bytes. First attempts to parse as a Natural so we do not lose precision. If that fails, falls back to Double.

Examples

Expand
>>> parseByteText "120 mb"
Right (MkBytes 120000000)
>>> parseByteText "4.5 terabytes"
Right (MkBytes 4500000000000)

surroundJust :: forall k m l (ks :: IxList) u v a b. (JoinKinds k A_Prism m, JoinKinds A_Prism l k) => Optic l ks u v (Maybe a) (Maybe b) -> Optic m ks (Maybe u) (Maybe v) a b Source #

whileM_ :: Monad m => m Bool -> m a -> m () Source #

whileM_ mb ma executes ma as long as mb returns True.

whenLeft :: Applicative f => Either a b -> (a -> f ()) -> f () Source #

Runs the action when it is Left.

untilJust :: Monad m => m (Maybe b) -> m b Source #

Executes the monadic action until we receive a Just, returning the value.

(∸) :: (Ord a, Num a) => a -> a -> a infixl 6 Source #

"monus" i.e. subtraction clamped to zero