miss-0: src/Data/Git/Formats.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
-- | This module provides some standard formats used for correctness across miss.
-- In many cases git only allows specific subsets of bytestrings due to parsing ambiguity
-- or system restrictions.
module Data.Git.Formats (
SafeString()
, safeString
, getSS
, LfFree
, lfFree
, isLfFree
, getLfFree
, module System.Filesystem.PathComponent
) where
import Control.Monad.Fail
import qualified Data.ByteString as B
import Data.Maybe (fromMaybe)
import Data.Semigroup
import Data.String
import Prelude hiding (fail)
import System.Filesystem.PathComponent
-- | Strings allowed in email addresses.
newtype SafeString = SS {
getSS :: B.ByteString -- ^ Get a 'B.ByteString' out of a 'SafeString'
} deriving (Eq, Ord, Show, Semigroup, Monoid)
instance IsString SafeString where
fromString = fromMaybe (error "Not a SafeString.") . safeString . fromString
-- | Try to make a 'SafeString'.
safeString :: MonadFail m => B.ByteString -> m SafeString
safeString b | isSafeString b = pure (SS b)
| otherwise = fail "Not a valid SafeString."
-- | True when 'safeString' will give back a 'Just'
isSafeString :: B.ByteString -> Bool
isSafeString "" = True
isSafeString b = safe (B.head b) && safe (B.last b) && safeInBetween
where safe = (`B.notElem` " .,:;<>\"'")
safeInBetween = B.any (`B.notElem` "<>\x0a\x00") b
-- | The restricted subset of 'B.ByteString's that do not include a linefeed.
newtype LfFree = LfFree {
getLfFree :: B.ByteString -- ^ Get the 'B.ByteString' out of a 'LfFree'
} deriving (Eq, Ord, Show, Monoid, Semigroup)
instance IsString LfFree where
fromString = fromMaybe (error "Not a LfFree, contains a linefeed.") . lfFree . fromString
-- | True when 'lfFree' will result in a Just
isLfFree :: B.ByteString -> Bool
isLfFree = B.notElem 0x0a
-- | Safe constructor for 'LfFree'
lfFree :: MonadFail m => B.ByteString -> m LfFree
lfFree b | isLfFree b = pure (LfFree b)
| otherwise = fail "Not a valid LfFree; contains a linefeed."