shell-monad-0.6.9: Control/Monad/Shell/Quote.hs
-- | Shell quoting
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Control.Monad.Shell.Quote (
Quoted(..),
Quotable(..),
Val(..),
) where
import qualified Data.Text.Lazy as L
import Data.String
import Data.Char
-- | A value that is safely quoted so that it can be exposed to the shell.
--
-- While the constructor is exposed, you should avoid directly constucting
-- Quoted values. Instead, use 'quote'.
newtype Quoted a = Q { getQ :: a }
deriving (Eq, Ord, Show, Semigroup, Monoid)
-- | Quotes a value to allow it to be safely exposed to the shell.
--
-- The method used is to replace ' with '"'"' and wrap the value inside
-- single quotes. This works for POSIX shells, as well as other shells
-- like csh.
--
-- The single quotes are omitted for simple values that do not need
-- any quoting.
class Quotable t where
quote :: t -> Quoted L.Text
instance Quotable L.Text where
quote t
| L.all bareable t && L.any bareable t = Q t
| otherwise = Q $ q <> L.intercalate "'\"'\"'" (L.splitOn q t) <> q
where
q = "'"
bareable c = isAlphaNum c || c == '_'
instance Quotable String where
quote = quote . L.pack
instance (Show v) => Quotable (Val v) where
quote (Val v) = quote $ show v
-- To avoid double-quoting Text and String, override the above instance.
-- This needs OverlappingInstances
instance Quotable (Val L.Text) where
quote (Val s) = quote s
instance Quotable (Val String) where
quote (Val s) = quote s
-- | An arbitrary value.
newtype Val v = Val v
instance IsString (Quoted L.Text) where
fromString = quote