web-inv-route-0.1: Web/Route/Invertible/Path.hs
-- |
-- Single-route path parsers (specialization of "Web.Route.Invertible.Sequence").
-- The most important type here is 'Path', which can be used to represent a single path endpoint within your application, including placeholders.
-- For example, the following represents a path of @\/item\/$id@ where @$id@ is an integer placeholder:
--
-- > Path ("item" *< parameter) :: Path Int
--
{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving, FlexibleInstances, FlexibleContexts #-}
module Web.Route.Invertible.Path
( PathString
, normalizePath
, Path(..)
, pathValues
, renderPath
, urlPathBuilder
) where
import Prelude hiding (lookup)
import Control.Invertible.Monoidal
import qualified Data.ByteString.Builder as B
import Data.Monoid ((<>))
import qualified Data.Invertible as I
import Data.String (IsString(..))
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Network.HTTP.Types.URI as H
import Web.Route.Invertible.Parameter
import Web.Route.Invertible.Placeholder
import Web.Route.Invertible.Sequence
-- |A component of a path, such that paths are represented by @['PathString']@ (after splitting on \'/\').
-- Paths can be created by 'H.decodePath'.
type PathString = T.Text
-- |Remove double- and trailing-slashes (i.e., empty path segments).
normalizePath :: [PathString] -> [PathString]
normalizePath = filter (not . T.null)
-- |A URL path parser/generator.
-- These should typically be constructed using the 'IsString' and 'Parameterized' instances.
-- Note that the individual components are /decoded/ path segments, so a literal slash in a component (e.g., as produced with 'fromString') will match \"%2F\".
-- Example:
--
-- > "get" *< parameter >*< "value" *< parameter :: Path (String, Int)
--
-- matches (or generates) @\/get\/$x\/value\/$y@ for any string @$x@ and any int @$y@ and returns those values.
newtype Path a = Path { pathSequence :: Sequence PathString a }
deriving (I.Functor, Monoidal, MonoidalAlt, Parameterized PathString, Show)
deriving instance IsString (Path ())
-- |Render a 'Path' as instantiated by a value to a list of placeholder values.
pathValues :: Path a -> a -> [PlaceholderValue PathString]
pathValues (Path p) = sequenceValues p
-- |Render a 'Path' as instantiated by a value to a list of string segments.
renderPath :: Path a -> a -> [PathString]
renderPath (Path p) = renderSequence p
-- |Build a 'Path' as applied to a value into a bytestring 'B.Builder' by encoding the segments with 'urlEncodePath' and joining them with \"/\".
urlPathBuilder :: Path a -> a -> B.Builder
urlPathBuilder p a = foldMap es $ renderPath p a where
es s = B.char7 '/' <> H.urlEncodeBuilder False (TE.encodeUtf8 s)