content: -- Copyright (c) Microsoft. All rights reserved.
  -- Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  {-# LANGUAGE OverloadedStrings #-}
  
  {-|
  Copyright   : (c) Microsoft
  License     : MIT
  Maintainer  : adamsap@microsoft.com
  Stability   : provisional
  Portability : portable
  
  Helper functions for combining elements into common constructs. These functions
  can be used in code generation to lazily combine 'Text' elements but they are
  more generic and work for any 'Monoid'.
  -}
  
  module Language.Bond.Util
      ( sepBy
      , sepEndBy
      , sepBeginBy
      , optional
      , ifThenElse
      , angles
      , brackets
      , braces
      , parens
      , between
      ) where
  
  import Data.Monoid
  import Data.String (IsString)
  import Prelude
  
  sepEndBy, sepBeginBy, sepBy :: (Monoid a, Eq a) => a -> (b -> a) -> [b] -> a
  
  -- | Maps elements of a list and combines them with 'mappend' using given
  -- separator, ending with a separator.
  sepEndBy _ _ [] = mempty
  sepEndBy s f (x:xs)
      | next == mempty = rest
      | otherwise = next <> s <> rest
          where
              next = f x
              rest = sepEndBy s f xs
  
  -- | Maps elements of a list and combines them with 'mappend' using given
  -- separator, starting with a separator.
  sepBeginBy _ _ [] = mempty
  sepBeginBy s f (x:xs)
      | next == mempty = rest
      | otherwise = s <> next <> rest
      where
          next = f x
          rest = sepBeginBy s f xs
  
  -- | Maps elements of a list and combines them with 'mappend' using given
  -- separator.
  sepBy _ _ [] = mempty
  sepBy s f (x:xs)
      | null xs = next
      | next == mempty = rest
      | otherwise = next <> sepBeginBy s f xs
          where
              next = f x
              rest = sepBy s f xs
  
  -- | The function takes a function and a Maybe value. If the Maybe value is
  -- Nothing, the function returns 'mempty', otherwise, it applies the function
  -- to the value inside 'Just' and returns the result.
  optional :: (Monoid m) => (a -> m) -> Maybe a -> m
  optional = maybe mempty
  
  -- if-then-else as a function
  ifThenElse :: Bool -> a -> a -> a
  ifThenElse True thenCondition _ = thenCondition
  ifThenElse False _ elseCondition = elseCondition
  
  -- | If the 3rd argument is not 'mempty' the function wraps it between the
  -- first and second argument using 'mappend', otherwise it return 'mempty'.
  between :: (Monoid a, Eq a) => a -> a -> a -> a
  between l r m
      | m == mempty = mempty
      | otherwise = l <> m <> r
  
  angles, brackets, braces, parens :: (Monoid a, IsString a, Eq a) => a -> a
  -- | Wraps the string argument between @<@ and @>@, unless the argument is
  -- 'mempty' in which case the function returns 'mempty'.
  angles m = between "<" ">" m
  
  -- | Wraps the string argument between @[@ and @]@, unless the argument is
  -- 'mempty' in which case the function returns 'mempty'.
  brackets m = between "[" "]" m
  
  -- | Wraps the string argument between @{@ and @}@, unless the argument is
  -- 'mempty' in which case the function returns 'mempty'.
  braces m = between "{" "}" m
  
  -- | Wraps the string argument between @(@ and @)@, unless the argument is
  -- 'mempty' in which case the function returns 'mempty'.
  parens m = between "(" ")" m
  
  