-- |
-- Module      : AOC.Util
-- Copyright   : (c) Justin Le 2018
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- Assorted utility functions and orphans used for solutions.
--

module AOC.Util (
    strip
  , stripNewline
  , eitherToMaybe
  , firstJust
  , maybeToEither
  , maybeAlt
  , traceShowIdMsg
  , traceShowMsg
  ) where

import           Control.Applicative
import           Control.Monad.Except
import           Data.Foldable
import           Debug.Trace
import qualified Data.Text            as T

-- | Strip trailing and leading whitespace.
strip :: String -> String
strip :: String -> String
strip = Text -> String
T.unpack (Text -> String) -> (String -> Text) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Text
T.strip (Text -> Text) -> (String -> Text) -> String -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack

-- | Strip trailing newline
stripNewline :: String -> String
stripNewline :: String -> String
stripNewline = String -> String
forall a. [a] -> [a]
reverse (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
dropWhile (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'\n') (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. [a] -> [a]
reverse

-- | Convert an 'Either' into a 'Maybe', or any 'Alternative' instance,
-- forgetting the error value.
eitherToMaybe :: Alternative m => Either e a -> m a
eitherToMaybe :: forall (m :: * -> *) e a. Alternative m => Either e a -> m a
eitherToMaybe = (e -> m a) -> (a -> m a) -> Either e a -> m a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (m a -> e -> m a
forall a b. a -> b -> a
const m a
forall (f :: * -> *) a. Alternative f => f a
empty) a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

-- | Convert a 'Maybe' into an 'Either', or any 'MonadError' instance, by
-- providing an error value in case 'Nothing' was given.
maybeToEither :: MonadError e m => e -> Maybe a -> m a
maybeToEither :: forall e (m :: * -> *) a. MonadError e m => e -> Maybe a -> m a
maybeToEither e
e = m a -> (a -> m a) -> Maybe a -> m a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (e -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError e
e) a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

-- | Like 'find', but instead of taking an @a -> Bool@, takes an @a ->
-- Maybe b@ and returns the first success.
firstJust
    :: Foldable t
    => (a -> Maybe b)
    -> t a
    -> Maybe b
firstJust :: forall (t :: * -> *) a b.
Foldable t =>
(a -> Maybe b) -> t a -> Maybe b
firstJust a -> Maybe b
p = [Maybe b] -> Maybe b
forall (t :: * -> *) (f :: * -> *) a.
(Foldable t, Alternative f) =>
t (f a) -> f a
asum ([Maybe b] -> Maybe b) -> (t a -> [Maybe b]) -> t a -> Maybe b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a -> Maybe b) -> [a] -> [Maybe b]
forall a b. (a -> b) -> [a] -> [b]
map a -> Maybe b
p ([a] -> [Maybe b]) -> (t a -> [a]) -> t a -> [Maybe b]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. t a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList

-- | Generalize a 'Maybe' to any 'Alternative'
maybeAlt :: Alternative m => Maybe a -> m a
maybeAlt :: forall (m :: * -> *) a. Alternative m => Maybe a -> m a
maybeAlt = m a -> (a -> m a) -> Maybe a -> m a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe m a
forall (f :: * -> *) a. Alternative f => f a
empty a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

-- | Like 'traceShowId' but with an extra message
traceShowIdMsg :: Show a => String -> a -> a
traceShowIdMsg :: forall a. Show a => String -> a -> a
traceShowIdMsg String
msg a
x = String -> a -> a
forall a. String -> a -> a
trace (String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
x) a
x

-- | Like 'traceShow' but with an extra message
traceShowMsg :: Show a => String -> a -> b -> b
traceShowMsg :: forall a b. Show a => String -> a -> b -> b
traceShowMsg String
msg a
x = String -> b -> b
forall a. String -> a -> a
trace (String
msg String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
x)