-- | -- The internal representation for sequences of placeholders, such as paths. -- For example, the following represents a sequence of @['PlaceholderFixed' "item", 'PlaceholderParameter']@: -- -- >>> :set -XOverloadedStrings -- >>> import Control.Invertible.Monoidal -- >>> import Web.Route.Invertible.Parameter -- >>> let p = "item" *< parameter :: Sequence String Int -- >>> parseSequence p ["item", "123"] -- 123 -- >>> renderSequence p 123 -- ["item","123"] -- -- These are used as the basis for path routers and other sequential/nested types. {-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses, FlexibleInstances, ScopedTypeVariables, GADTs #-} module Web.Route.Invertible.Sequence ( Sequence(..) , placeholderSequence , wildcard , sequenceValues , renderSequence , readsSequence , parseSequence , reverseSequence ) where import Control.Invertible.Monoidal import Control.Invertible.Monoidal.Free import Control.Monad (MonadPlus, mzero, guard) import qualified Data.Invertible as I import Data.String (IsString(..)) import Web.Route.Invertible.Parameter import Web.Route.Invertible.Placeholder -- |A parser/reverse-router isomorphism between sequences of strings (represented as @[s]@) and a value @a@. -- These can be constructed using: -- -- * @'fromString' s@ (or simply @s@ with OverloadedStrings), which matches a single literal component. -- * @'parameter'@ (or @'param' (undefined :: T)@ for an explicit type), which matches a place-holder component for a 'Parameter' type. -- -- Sequence values can then be composed using 'Monoidal' and 'MonoidalAlt'. newtype Sequence s a = Sequence { freeSequence :: Free (Placeholder s) a } deriving (I.Functor, Monoidal, MonoidalAlt) instance Show s => Show (Sequence s a) where showsPrec d (Sequence s) = showParen (d > 10) $ showString "Sequence " . showsFree (showsPrec 11) s -- |Make a singleton 'Sequence' out of a 'Placeholder'. placeholderSequence :: Placeholder s a -> Sequence s a placeholderSequence = Sequence . Free instance Parameterized s (Sequence s) where parameter = placeholderSequence parameter instance IsString s => IsString (Sequence s ()) where fromString = placeholderSequence . fromString -- |Ignore an arbitrary sequence of parameters (usually as a tail), always generating the same thing. wildcard :: (Parameterized s f, MonoidalAlt f, Parameter s a) => [a] -> f () wildcard d = d >$ manyI parameter -- |Realize a 'Sequence' as instantiated by a value to a sequence of 'PlaceholderValue's. sequenceValues :: Sequence s a -> a -> [PlaceholderValue s] sequenceValues = produceFree f . freeSequence where f :: Placeholder s a' -> a' -> PlaceholderValue s f (PlaceholderFixed t) () = PlaceholderValueFixed t f PlaceholderParameter a = PlaceholderValueParameter a -- |Render a 'Sequence' as instantiated by a value to a list of string segments. renderSequence :: Sequence s a -> a -> [s] renderSequence p = map renderPlaceholderValue . sequenceValues p -- |Attempt to parse sequence segments into a value and remaining (unparsed) segments, ala 'reads'. readsSequence :: forall m s a . (MonadPlus m, Eq s) => Sequence s a -> [s] -> m (a, [s]) readsSequence = parseFree f . freeSequence where f :: Placeholder s a' -> s -> m a' f (PlaceholderFixed t) a = guard (a == t) f PlaceholderParameter a = maybe mzero return (parseParameter a) -- |Parse a sequence into possible values. Can return all possible values as a list or (usually) a single value as 'Maybe'. parseSequence :: (MonadPlus m, Eq s) => Sequence s a -> [s] -> m a parseSequence p l = do (a, []) <- readsSequence p l return a -- |Reverse the order of a sequence, such that @reverseSequence p@ parses/produces @reverse l@ iff @p@ parses/produces @l@. -- Since sequences are matched left-to-right, this lets you match them right-to-left. -- It probably goes without saying, but this won't work for infinite sequences, such as those produced by 'while'. reverseSequence :: Sequence s a -> Sequence s a reverseSequence = Sequence . reverseFree . freeSequence