burrito 2.0.1.2 → 2.0.1.3
raw patch · 24 files changed
+1823/−1813 lines, 24 filesdep ~basedep ~bytestringdep ~containersPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base, bytestring, containers, template-haskell, text
API changes (from Hackage documentation)
Files
- LICENSE.markdown +1/−1
- burrito.cabal +12/−15
- source/library/Burrito.hs +14/−13
- source/library/Burrito/Internal/Expand.hs +117/−118
- source/library/Burrito/Internal/Match.hs +44/−46
- source/library/Burrito/Internal/Parse.hs +55/−52
- source/library/Burrito/Internal/Render.hs +5/−5
- source/library/Burrito/Internal/TH.hs +17/−16
- source/library/Burrito/Internal/Type/Case.hs +3/−2
- source/library/Burrito/Internal/Type/Character.hs +3/−2
- source/library/Burrito/Internal/Type/Digit.hs +45/−46
- source/library/Burrito/Internal/Type/Expression.hs +5/−4
- source/library/Burrito/Internal/Type/Field.hs +5/−3
- source/library/Burrito/Internal/Type/Literal.hs +5/−3
- source/library/Burrito/Internal/Type/Match.hs +3/−2
- source/library/Burrito/Internal/Type/MaxLength.hs +5/−3
- source/library/Burrito/Internal/Type/Modifier.hs +3/−2
- source/library/Burrito/Internal/Type/Name.hs +5/−3
- source/library/Burrito/Internal/Type/Operator.hs +3/−2
- source/library/Burrito/Internal/Type/Template.hs +6/−4
- source/library/Burrito/Internal/Type/Token.hs +3/−2
- source/library/Burrito/Internal/Type/Value.hs +3/−2
- source/library/Burrito/Internal/Type/Variable.hs +5/−4
- source/test-suite/Main.hs +1456/−1463
LICENSE.markdown view
@@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Taylor Fausak+Copyright (c) 2023 Taylor Fausak Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal
burrito.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.2 name: burrito-version: 2.0.1.2+version: 2.0.1.3 synopsis: Parse and render URI templates. description:@@ -39,12 +39,12 @@ common library build-depends:- , base >= 4.13.0 && < 4.18- , bytestring >= 0.10.10 && < 0.12- , containers >= 0.6.2 && < 0.7+ , base >= 4.15.0 && < 4.18+ , bytestring >= 0.10.12 && < 0.12+ , containers >= 0.6.4 && < 0.7 , parsec >= 3.1.14 && < 3.2- , template-haskell >= 2.15.0 && < 2.20- , text >= 1.2.4 && < 1.3 || >= 2.0 && < 2.1+ , template-haskell >= 2.17.0 && < 2.20+ , text >= 1.2.5 && < 1.3 || >= 2.0 && < 2.1 , transformers >= 0.5.6 && < 0.6 default-language: Haskell2010 ghc-options:@@ -54,21 +54,18 @@ -Wno-missed-specialisations -Wno-missing-deriving-strategies -Wno-missing-exported-signatures+ -Wno-missing-safe-haskell-mode+ -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe - if flag(pedantic)- ghc-options: -Werror-- if impl(ghc >= 8.10)- ghc-options:- -Wno-missing-safe-haskell-mode- -Wno-prepositive-qualified-module- if impl(ghc >= 9.2) ghc-options: -Wno-missing-kind-signatures + if flag(pedantic)+ ghc-options: -Werror+ common executable import: library @@ -105,7 +102,7 @@ Burrito.Internal.Type.Variable hs-source-dirs: source/library -test-suite test+test-suite burrito-test-suite import: executable build-depends:
source/library/Burrito.hs view
@@ -27,19 +27,20 @@ -- -- In short, use @parse@ to parse templates and @expand@ to render them. module Burrito- ( Parse.parse- , Template.render- , Expand.expand- , Expand.expandWith- , Match.match- , TH.uriTemplate- , TH.expandTH- , Template.Template- , Value.Value- , stringValue- , listValue- , dictionaryValue- ) where+ ( Parse.parse,+ Template.render,+ Expand.expand,+ Expand.expandWith,+ Match.match,+ TH.uriTemplate,+ TH.expandTH,+ Template.Template,+ Value.Value,+ stringValue,+ listValue,+ dictionaryValue,+ )+where import qualified Burrito.Internal.Expand as Expand import qualified Burrito.Internal.Match as Match
source/library/Burrito/Internal/Expand.hs view
@@ -43,9 +43,10 @@ expand :: [(String, Value.Value)] -> Template.Template -> String expand values = let m = Map.mapKeys Text.pack $ Map.fromList values- in- Render.builderToString . Identity.runIdentity . expandWith- (pure . flip Map.lookup m)+ in Render.builderToString+ . Identity.runIdentity+ . expandWith+ (pure . flip Map.lookup m) -- | This is like @expand@ except that it gives you more control over how -- variables are expanded. If you can, use @expand@. It's simpler.@@ -68,20 +69,20 @@ -- >>> expandWith (\ x -> do { putStrLn $ "-- expanding " <> show x; pure . Just $ Burrito.stringValue "A" }) template -- -- expanding "a" -- "AA"-expandWith- :: Monad m- => (Text.Text -> m (Maybe Value.Value))- -> Template.Template- -> m Builder.Builder+expandWith ::+ (Monad m) =>+ (Text.Text -> m (Maybe Value.Value)) ->+ Template.Template ->+ m Builder.Builder expandWith f = flip State.evalStateT Map.empty . template (cached f) type CacheT = State.StateT (Map.Map Text.Text (Maybe Value.Value)) -cached- :: Monad m- => (Text.Text -> m (Maybe Value.Value))- -> Name.Name- -> CacheT m (Maybe Value.Value)+cached ::+ (Monad m) =>+ (Text.Text -> m (Maybe Value.Value)) ->+ Name.Name ->+ CacheT m (Maybe Value.Value) cached f x = do let key = LazyText.toStrict . Builder.toLazyText $ name x cache <- State.get@@ -92,39 +93,38 @@ State.modify $ Map.insert key result pure result -template- :: Monad m- => (Name.Name -> CacheT m (Maybe Value.Value))- -> Template.Template- -> CacheT m Builder.Builder+template ::+ (Monad m) =>+ (Name.Name -> CacheT m (Maybe Value.Value)) ->+ Template.Template ->+ CacheT m Builder.Builder template f = fmap mconcat . traverse (token f) . Template.tokens -token- :: Monad m- => (Name.Name -> CacheT m (Maybe Value.Value))- -> Token.Token- -> CacheT m Builder.Builder+token ::+ (Monad m) =>+ (Name.Name -> CacheT m (Maybe Value.Value)) ->+ Token.Token ->+ CacheT m Builder.Builder token f x = case x of Token.Expression y -> expression f y Token.Literal y -> pure $ literal y -expression- :: Monad m- => (Name.Name -> CacheT m (Maybe Value.Value))- -> Expression.Expression- -> CacheT m Builder.Builder+expression ::+ (Monad m) =>+ (Name.Name -> CacheT m (Maybe Value.Value)) ->+ Expression.Expression ->+ CacheT m Builder.Builder expression f ex = let op = Expression.operator ex- in- fmap- (mconcat- . (\xs -> if null xs then xs else prefix op : xs)- . List.intersperse (separator op)- . Maybe.catMaybes- )- . traverse (variable f op)- . NonEmpty.toList- $ Expression.variables ex+ in fmap+ ( mconcat+ . (\xs -> if null xs then xs else prefix op : xs)+ . List.intersperse (separator op)+ . Maybe.catMaybes+ )+ . traverse (variable f op)+ . NonEmpty.toList+ $ Expression.variables ex separator :: Operator.Operator -> Builder.Builder separator op = Builder.singleton $ case op of@@ -148,79 +148,79 @@ Operator.Semicolon -> Builder.singleton ';' Operator.Solidus -> Builder.singleton '/' -variable- :: Monad m- => (Name.Name -> CacheT m (Maybe Value.Value))- -> Operator.Operator- -> Variable.Variable- -> CacheT m (Maybe Builder.Builder)+variable ::+ (Monad m) =>+ (Name.Name -> CacheT m (Maybe Value.Value)) ->+ Operator.Operator ->+ Variable.Variable ->+ CacheT m (Maybe Builder.Builder) variable f op var = do res <- f $ Variable.name var pure $ case res of Nothing -> Nothing Just val -> value op var val -value- :: Operator.Operator- -> Variable.Variable- -> Value.Value- -> Maybe Builder.Builder+value ::+ Operator.Operator ->+ Variable.Variable ->+ Value.Value ->+ Maybe Builder.Builder value op var val = case val of Value.Dictionary xs -> dictionaryValue op var $ Map.toAscList xs Value.List xs -> listValue op var xs Value.String x -> Just $ stringValue op var x -dictionaryValue- :: Operator.Operator- -> Variable.Variable- -> [(Text.Text, Text.Text)]- -> Maybe Builder.Builder+dictionaryValue ::+ Operator.Operator ->+ Variable.Variable ->+ [(Text.Text, Text.Text)] ->+ Maybe Builder.Builder dictionaryValue = items $ \op var (k, v) -> let f = string op Modifier.None- in- case Variable.modifier var of- Modifier.Asterisk -> [f k <> Builder.singleton '=' <> f v]- _ -> [f k, f v]+ in case Variable.modifier var of+ Modifier.Asterisk -> [f k <> Builder.singleton '=' <> f v]+ _ -> [f k, f v] -listValue- :: Operator.Operator- -> Variable.Variable- -> [Text.Text]- -> Maybe Builder.Builder-listValue = items $ \op var -> pure . stringValue- (case Variable.modifier var of- Modifier.Asterisk -> op- _ -> Operator.None- )- var { Variable.modifier = Modifier.None }+listValue ::+ Operator.Operator ->+ Variable.Variable ->+ [Text.Text] ->+ Maybe Builder.Builder+listValue = items $ \op var ->+ pure+ . stringValue+ ( case Variable.modifier var of+ Modifier.Asterisk -> op+ _ -> Operator.None+ )+ var {Variable.modifier = Modifier.None} -items- :: (Operator.Operator -> Variable.Variable -> a -> [Builder.Builder])- -> Operator.Operator- -> Variable.Variable- -> [a]- -> Maybe Builder.Builder+items ::+ (Operator.Operator -> Variable.Variable -> a -> [Builder.Builder]) ->+ Operator.Operator ->+ Variable.Variable ->+ [a] ->+ Maybe Builder.Builder items f op var xs =- let- md = Variable.modifier var- sep = case md of- Modifier.Asterisk -> separator op- _ -> Builder.singleton ','- p = case md of- Modifier.Asterisk -> False- _ -> case op of- Operator.Ampersand -> True- Operator.QuestionMark -> True- Operator.Semicolon -> True- _ -> False- in if null xs- then Nothing- else- Just- . mconcat- . (if p then (label True var :) else id)- . List.intersperse sep- $ concatMap (f op var) xs+ let md = Variable.modifier var+ sep = case md of+ Modifier.Asterisk -> separator op+ _ -> Builder.singleton ','+ p = case md of+ Modifier.Asterisk -> False+ _ -> case op of+ Operator.Ampersand -> True+ Operator.QuestionMark -> True+ Operator.Semicolon -> True+ _ -> False+ in if null xs+ then Nothing+ else+ Just+ . mconcat+ . (if p then (label True var :) else id)+ . List.intersperse sep+ $ concatMap (f op var) xs label :: Bool -> Variable.Variable -> Builder.Builder label p v =@@ -242,29 +242,27 @@ Character.Encoded y z -> Render.encodedCharacter y z Character.Unencoded y -> unencodedCharacter f y -stringValue- :: Operator.Operator -> Variable.Variable -> Text.Text -> Builder.Builder+stringValue ::+ Operator.Operator -> Variable.Variable -> Text.Text -> Builder.Builder stringValue op var str =- let- pre = case op of- Operator.Ampersand -> label True var- Operator.QuestionMark -> label True var- Operator.Semicolon -> label (not $ Text.null str) var- _ -> mempty- in pre <> string op (Variable.modifier var) str+ let pre = case op of+ Operator.Ampersand -> label True var+ Operator.QuestionMark -> label True var+ Operator.Semicolon -> label (not $ Text.null str) var+ _ -> mempty+ in pre <> string op (Variable.modifier var) str -string- :: Operator.Operator -> Modifier.Modifier -> Text.Text -> Builder.Builder+string ::+ Operator.Operator -> Modifier.Modifier -> Text.Text -> Builder.Builder string op md =- let- allowed x = case op of- Operator.NumberSign -> isAllowed x- Operator.PlusSign -> isAllowed x- _ -> isUnreserved x- trim = case md of- Modifier.Colon ml -> Text.take $ MaxLength.count ml- _ -> id- in foldMap (unencodedCharacter allowed) . Text.unpack . trim+ let allowed x = case op of+ Operator.NumberSign -> isAllowed x+ Operator.PlusSign -> isAllowed x+ _ -> isUnreserved x+ trim = case md of+ Modifier.Colon ml -> Text.take $ MaxLength.count ml+ _ -> id+ in foldMap (unencodedCharacter allowed) . Text.unpack . trim isAllowed :: Char -> Bool isAllowed x = isUnreserved x || isReserved x@@ -300,9 +298,10 @@ _ -> False unencodedCharacter :: (Char -> Bool) -> Char -> Builder.Builder-unencodedCharacter f x = if f x- then Builder.singleton x- else foldMap (uncurry Render.encodedCharacter) $ encodeCharacter x+unencodedCharacter f x =+ if f x+ then Builder.singleton x+ else foldMap (uncurry Render.encodedCharacter) $ encodeCharacter x encodeCharacter :: Char -> [(Digit.Digit, Digit.Digit)] encodeCharacter =
source/library/Burrito/Internal/Match.hs view
@@ -70,8 +70,8 @@ Just (Render.builderToString $ Render.name n, Value.String v) Match.Undefined -> Nothing -keepConsistent- :: [(Name.Name, Match.Match)] -> Maybe [(Name.Name, Match.Match)]+keepConsistent ::+ [(Name.Name, Match.Match)] -> Maybe [(Name.Name, Match.Match)] keepConsistent xs = case xs of [] -> Just xs (k, v) : ys -> do@@ -90,8 +90,8 @@ Match.Prefix n t -> case y of Match.Defined u | t == Text.take (MaxLength.count n) u -> combine y zs Match.Prefix m u- | let c = MaxLength.count (min n m) in Text.take c t == Text.take c u- -> combine (if m > n then y else x) zs+ | let c = MaxLength.count (min n m) in Text.take c t == Text.take c u ->+ combine (if m > n then y else x) zs _ -> Nothing Match.Undefined -> case y of Match.Undefined -> combine x zs@@ -111,10 +111,10 @@ expression :: Expression.Expression -> ReadP.ReadP [(Name.Name, Match.Match)] expression x = variables (Expression.operator x) (Expression.variables x) -variables- :: Operator.Operator- -> NonEmpty.NonEmpty Variable.Variable- -> ReadP.ReadP [(Name.Name, Match.Match)]+variables ::+ Operator.Operator ->+ NonEmpty.NonEmpty Variable.Variable ->+ ReadP.ReadP [(Name.Name, Match.Match)] variables op vs = case op of Operator.Ampersand -> vars vs (Just '&') '&' varEq Operator.FullStop -> vars vs (Just '.') '.' $ variable Expand.isUnreserved@@ -130,43 +130,41 @@ variable Expand.isUnreserved v Operator.Solidus -> vars vs (Just '/') '/' $ variable Expand.isUnreserved -vars- :: NonEmpty.NonEmpty Variable.Variable- -> Maybe Char- -> Char- -> (Variable.Variable -> ReadP.ReadP [(Name.Name, Match.Match)])- -> ReadP.ReadP [(Name.Name, Match.Match)]+vars ::+ NonEmpty.NonEmpty Variable.Variable ->+ Maybe Char ->+ Char ->+ (Variable.Variable -> ReadP.ReadP [(Name.Name, Match.Match)]) ->+ ReadP.ReadP [(Name.Name, Match.Match)] vars vs m c f = do- let- ctx = case m of- Nothing -> id- Just o -> \p -> ReadP.option (undef <$> NonEmpty.toList vs) $ do- char_ o- xs <- p- Monad.guard . not $ all isUndefined xs- pure xs+ let ctx = case m of+ Nothing -> id+ Just o -> \p -> ReadP.option (undef <$> NonEmpty.toList vs) $ do+ char_ o+ xs <- p+ Monad.guard . not $ all isUndefined xs+ pure xs ctx . vars' c f $ NonEmpty.toList vs isUndefined :: (Name.Name, Match.Match) -> Bool isUndefined = (== Match.Undefined) . snd -vars'- :: Char- -> (Variable.Variable -> ReadP.ReadP [(Name.Name, Match.Match)])- -> [Variable.Variable]- -> ReadP.ReadP [(Name.Name, Match.Match)]+vars' ::+ Char ->+ (Variable.Variable -> ReadP.ReadP [(Name.Name, Match.Match)]) ->+ [Variable.Variable] ->+ ReadP.ReadP [(Name.Name, Match.Match)] vars' c f vs = case vs of [] -> pure [] v : ws ->- let- this = do- x <- f v- xs <- ReadP.option (undef <$> ws) $ do- char_ c- vars' c f ws- pure $ x <> xs- that = (undef v :) <$> vars' c f ws- in this ReadP.+++ that+ let this = do+ x <- f v+ xs <- ReadP.option (undef <$> ws) $ do+ char_ c+ vars' c f ws+ pure $ x <> xs+ that = (undef v :) <$> vars' c f ws+ in this ReadP.+++ that undef :: Variable.Variable -> (Name.Name, Match.Match) undef v = (Variable.name v, Match.Undefined)@@ -183,10 +181,10 @@ name :: Name.Name -> ReadP.ReadP () name = Monad.void . ReadP.string . Render.builderToString . Render.name -variable- :: (Char -> Bool)- -> Variable.Variable- -> ReadP.ReadP [(Name.Name, Match.Match)]+variable ::+ (Char -> Bool) ->+ Variable.Variable ->+ ReadP.ReadP [(Name.Name, Match.Match)] variable f x = do v <- case Variable.modifier x of Modifier.Asterisk -> ReadP.pfail@@ -196,9 +194,8 @@ manyCharacters :: (Char -> Bool) -> ReadP.ReadP Text.Text manyCharacters f = do- let- f1 = (:) <$> someEncodedCharacters <*> ReadP.option [] f2- f2 = (:) <$> someUnencodedCharacters f <*> ReadP.option [] f1+ let f1 = (:) <$> someEncodedCharacters <*> ReadP.option [] f2+ f2 = (:) <$> someUnencodedCharacters f <*> ReadP.option [] f1 fmap mconcat . ReadP.option [] $ f1 ReadP.<++ f2 someEncodedCharacters :: ReadP.ReadP Text.Text@@ -268,6 +265,7 @@ Digit.OxF Case.Lower -> 'f' unencodedCharacter :: (Char -> Bool) -> Char -> ReadP.ReadP ()-unencodedCharacter f x = if f x- then char_ x- else mapM_ (uncurry encodedCharacter) $ Expand.encodeCharacter x+unencodedCharacter f x =+ if f x+ then char_ x+ else mapM_ (uncurry encodedCharacter) $ Expand.encodeCharacter x
source/library/Burrito/Internal/Parse.hs view
@@ -1,6 +1,5 @@-{-# OPTIONS_GHC -Wno-missing-export-lists #-}- {-# LANGUAGE FlexibleContexts #-}+{-# OPTIONS_GHC -Wno-missing-export-lists #-} module Burrito.Internal.Parse where @@ -37,72 +36,74 @@ parse :: String -> Maybe Template.Template parse = either (const Nothing) Just . Parsec.parse template "" -template :: Parsec.Stream s m Char => Parsec.ParsecT s u m Template.Template+template :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Template.Template template = Template.Template <$> Parsec.many token <* Parsec.eof -token :: Parsec.Stream s m Char => Parsec.ParsecT s u m Token.Token+token :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Token.Token token = choice (Token.Expression <$> expression) (Token.Literal <$> literal) -choice- :: Parsec.ParsecT s u m a -> Parsec.ParsecT s u m a -> Parsec.ParsecT s u m a+choice ::+ Parsec.ParsecT s u m a -> Parsec.ParsecT s u m a -> Parsec.ParsecT s u m a choice = (Parsec.<|>) -expression- :: Parsec.Stream s m Char => Parsec.ParsecT s u m Expression.Expression+expression ::+ (Parsec.Stream s m Char) => Parsec.ParsecT s u m Expression.Expression expression =- Parsec.between (Parsec.char '{') (Parsec.char '}')- $ Expression.Expression- <$> operator- <*> sepBy1 variable (Parsec.char ',')+ Parsec.between (Parsec.char '{') (Parsec.char '}') $+ Expression.Expression+ <$> operator+ <*> sepBy1 variable (Parsec.char ',') -operator :: Parsec.Stream s m Char => Parsec.ParsecT s u m Operator.Operator-operator = Parsec.option Operator.None $ Parsec.choice- [ Operator.Ampersand <$ Parsec.char '&'- , Operator.FullStop <$ Parsec.char '.'- , Operator.NumberSign <$ Parsec.char '#'- , Operator.PlusSign <$ Parsec.char '+'- , Operator.QuestionMark <$ Parsec.char '?'- , Operator.Semicolon <$ Parsec.char ';'- , Operator.Solidus <$ Parsec.char '/'- ]+operator :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Operator.Operator+operator =+ Parsec.option Operator.None $+ Parsec.choice+ [ Operator.Ampersand <$ Parsec.char '&',+ Operator.FullStop <$ Parsec.char '.',+ Operator.NumberSign <$ Parsec.char '#',+ Operator.PlusSign <$ Parsec.char '+',+ Operator.QuestionMark <$ Parsec.char '?',+ Operator.Semicolon <$ Parsec.char ';',+ Operator.Solidus <$ Parsec.char '/'+ ] -sepBy1- :: Parsec.ParsecT s u m a- -> Parsec.ParsecT s u m x- -> Parsec.ParsecT s u m (NonEmpty.NonEmpty a)+sepBy1 ::+ Parsec.ParsecT s u m a ->+ Parsec.ParsecT s u m x ->+ Parsec.ParsecT s u m (NonEmpty.NonEmpty a) sepBy1 p s = (NonEmpty.:|) <$> p <*> Parsec.many (s *> p) -variable :: Parsec.Stream s m Char => Parsec.ParsecT s u m Variable.Variable+variable :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Variable.Variable variable = Variable.Variable <$> name <*> modifier -name :: Parsec.Stream s m Char => Parsec.ParsecT s u m Name.Name+name :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Name.Name name = Name.Name <$> sepBy1 field (Parsec.char '.') -field :: Parsec.Stream s m Char => Parsec.ParsecT s u m Field.Field+field :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Field.Field field = Field.Field <$> nonEmpty fieldCharacter -nonEmpty- :: Parsec.ParsecT s u m a -> Parsec.ParsecT s u m (NonEmpty.NonEmpty a)+nonEmpty ::+ Parsec.ParsecT s u m a -> Parsec.ParsecT s u m (NonEmpty.NonEmpty a) nonEmpty p = (NonEmpty.:|) <$> p <*> Parsec.many p -fieldCharacter- :: Parsec.Stream s m Char- => Parsec.ParsecT s u m (Character.Character Field.Field)+fieldCharacter ::+ (Parsec.Stream s m Char) =>+ Parsec.ParsecT s u m (Character.Character Field.Field) fieldCharacter = choice encodedCharacter (unencodedCharacter isFieldCharacter) -encodedCharacter- :: Parsec.Stream s m Char => Parsec.ParsecT s u m (Character.Character tag)+encodedCharacter ::+ (Parsec.Stream s m Char) => Parsec.ParsecT s u m (Character.Character tag) encodedCharacter = Parsec.char '%' >> Character.Encoded <$> digit <*> digit -digit :: Parsec.Stream s m Char => Parsec.ParsecT s u m Digit.Digit+digit :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Digit.Digit digit = do x <- Parsec.satisfy Char.isHexDigit maybe (fail "invalid Digit") pure $ Digit.fromChar x -unencodedCharacter- :: Parsec.Stream s m Char- => (Char -> Bool)- -> Parsec.ParsecT s u m (Character.Character tag)+unencodedCharacter ::+ (Parsec.Stream s m Char) =>+ (Char -> Bool) ->+ Parsec.ParsecT s u m (Character.Character tag) unencodedCharacter = fmap Character.Unencoded . Parsec.satisfy isFieldCharacter :: Char -> Bool@@ -110,14 +111,16 @@ '_' -> True _ -> Char.isAsciiUpper x || Char.isAsciiLower x || Char.isDigit x -modifier :: Parsec.Stream s m Char => Parsec.ParsecT s u m Modifier.Modifier-modifier = Parsec.option Modifier.None $ Parsec.choice- [ Modifier.Asterisk <$ Parsec.char '*'- , Parsec.char ':' >> Modifier.Colon <$> maxLength- ]+modifier :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Modifier.Modifier+modifier =+ Parsec.option Modifier.None $+ Parsec.choice+ [ Modifier.Asterisk <$ Parsec.char '*',+ Parsec.char ':' >> Modifier.Colon <$> maxLength+ ] -maxLength- :: Parsec.Stream s m Char => Parsec.ParsecT s u m MaxLength.MaxLength+maxLength ::+ (Parsec.Stream s m Char) => Parsec.ParsecT s u m MaxLength.MaxLength maxLength = do x <- Parsec.satisfy $ Ix.inRange ('1', '9') xs <- Parsec.many $ Parsec.satisfy Char.isDigit@@ -129,12 +132,12 @@ isMaxLength :: Int -> Bool isMaxLength = Ix.inRange (1, 9999) -literal :: Parsec.Stream s m Char => Parsec.ParsecT s u m Literal.Literal+literal :: (Parsec.Stream s m Char) => Parsec.ParsecT s u m Literal.Literal literal = Literal.Literal <$> nonEmpty literalCharacter -literalCharacter- :: Parsec.Stream s m Char- => Parsec.ParsecT s u m (Character.Character Literal.Literal)+literalCharacter ::+ (Parsec.Stream s m Char) =>+ Parsec.ParsecT s u m (Character.Character Literal.Literal) literalCharacter = choice encodedCharacter (unencodedCharacter isLiteralCharacter)
source/library/Burrito/Internal/Render.hs view
@@ -45,11 +45,11 @@ Operator.Semicolon -> Builder.singleton ';' Operator.Solidus -> Builder.singleton '/' -sepBy1- :: (a -> Builder.Builder)- -> Builder.Builder- -> NonEmpty.NonEmpty a- -> Builder.Builder+sepBy1 ::+ (a -> Builder.Builder) ->+ Builder.Builder ->+ NonEmpty.NonEmpty a ->+ Builder.Builder sepBy1 f x = mconcat . List.intersperse x . fmap f . NonEmpty.toList variable :: Variable.Variable -> Builder.Builder
source/library/Burrito/Internal/TH.hs view
@@ -1,7 +1,8 @@ module Burrito.Internal.TH- ( expandTH- , uriTemplate- ) where+ ( expandTH,+ uriTemplate,+ )+where import qualified Burrito.Internal.Expand as Expand import qualified Burrito.Internal.Parse as Parse@@ -27,14 +28,13 @@ -- "l-bar-r" expandTH :: [(String, Value.Value)] -> Template.Template -> TH.Q TH.Exp expandTH xs t = do- let- m = Map.fromList $ fmap (Bifunctor.first Text.pack) xs- f k =- maybe (Left $ "missing variable: " <> show k) (Right . Just)- $ Map.lookup k m+ let m = Map.fromList $ fmap (Bifunctor.first Text.pack) xs+ f k =+ maybe (Left $ "missing variable: " <> show k) (Right . Just) $+ Map.lookup k m x <-- either fail (pure . LazyText.unpack . Builder.toLazyText)- $ Expand.expandWith f t+ either fail (pure . LazyText.unpack . Builder.toLazyText) $+ Expand.expandWith f t TH.lift x -- | This can be used together with the @QuasiQuotes@ language extension to@@ -53,9 +53,10 @@ -- this is invalid: @[uriTemplate|\\xa0|]@. You can however use percent encoded -- triples as normal. So this is valid: @[uriTemplate|%c2%a0|]@. uriTemplate :: TH.QuasiQuoter-uriTemplate = TH.QuasiQuoter- { TH.quoteDec = const $ fail "cannot be used as a declaration"- , TH.quoteExp = maybe (fail "invalid URI template") TH.liftData . Parse.parse- , TH.quotePat = const $ fail "cannot be used as a pattern"- , TH.quoteType = const $ fail "cannot be used as a type"- }+uriTemplate =+ TH.QuasiQuoter+ { TH.quoteDec = const $ fail "cannot be used as a declaration",+ TH.quoteExp = maybe (fail "invalid URI template") TH.liftData . Parse.parse,+ TH.quotePat = const $ fail "cannot be used as a pattern",+ TH.quoteType = const $ fail "cannot be used as a type"+ }
source/library/Burrito/Internal/Type/Case.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Case- ( Case(..)- ) where+ ( Case (..),+ )+where import qualified Data.Data as Data
source/library/Burrito/Internal/Type/Character.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Character- ( Character(..)- ) where+ ( Character (..),+ )+where import qualified Burrito.Internal.Type.Digit as Digit import qualified Data.Data as Data
source/library/Burrito/Internal/Type/Digit.hs view
@@ -1,11 +1,12 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Digit- ( Digit(..)- , fromChar- , fromWord8- , toWord8- ) where+ ( Digit (..),+ fromChar,+ fromWord8,+ toWord8,+ )+where import qualified Burrito.Internal.Type.Case as Case import qualified Data.Bits as Bits@@ -59,47 +60,45 @@ fromWord8 :: Word.Word8 -> (Digit, Digit) fromWord8 x =- let- f :: Word.Word8 -> Digit- f y = case y of- 0x0 -> Ox0- 0x1 -> Ox1- 0x2 -> Ox2- 0x3 -> Ox3- 0x4 -> Ox4- 0x5 -> Ox5- 0x6 -> Ox6- 0x7 -> Ox7- 0x8 -> Ox8- 0x9 -> Ox9- 0xA -> OxA Case.Upper- 0xB -> OxB Case.Upper- 0xC -> OxC Case.Upper- 0xD -> OxD Case.Upper- 0xE -> OxE Case.Upper- 0xF -> OxF Case.Upper- _ -> error $ "invalid nibble: " <> show y- in (f $ Bits.shiftR x 4, f $ x Bits..&. 0x0F)+ let f :: Word.Word8 -> Digit+ f y = case y of+ 0x0 -> Ox0+ 0x1 -> Ox1+ 0x2 -> Ox2+ 0x3 -> Ox3+ 0x4 -> Ox4+ 0x5 -> Ox5+ 0x6 -> Ox6+ 0x7 -> Ox7+ 0x8 -> Ox8+ 0x9 -> Ox9+ 0xA -> OxA Case.Upper+ 0xB -> OxB Case.Upper+ 0xC -> OxC Case.Upper+ 0xD -> OxD Case.Upper+ 0xE -> OxE Case.Upper+ 0xF -> OxF Case.Upper+ _ -> error $ "invalid nibble: " <> show y+ in (f $ Bits.shiftR x 4, f $ x Bits..&. 0x0F) toWord8 :: Digit -> Digit -> Word.Word8 toWord8 x y =- let- f :: Digit -> Word.Word8- f z = case z of- Ox0 -> 0x0- Ox1 -> 0x1- Ox2 -> 0x2- Ox3 -> 0x3- Ox4 -> 0x4- Ox5 -> 0x5- Ox6 -> 0x6- Ox7 -> 0x7- Ox8 -> 0x8- Ox9 -> 0x9- OxA _ -> 0xA- OxB _ -> 0xB- OxC _ -> 0xC- OxD _ -> 0xD- OxE _ -> 0xE- OxF _ -> 0xF- in Bits.shiftL (f x) 4 Bits..|. f y+ let f :: Digit -> Word.Word8+ f z = case z of+ Ox0 -> 0x0+ Ox1 -> 0x1+ Ox2 -> 0x2+ Ox3 -> 0x3+ Ox4 -> 0x4+ Ox5 -> 0x5+ Ox6 -> 0x6+ Ox7 -> 0x7+ Ox8 -> 0x8+ Ox9 -> 0x9+ OxA _ -> 0xA+ OxB _ -> 0xB+ OxC _ -> 0xC+ OxD _ -> 0xD+ OxE _ -> 0xE+ OxF _ -> 0xF+ in Bits.shiftL (f x) 4 Bits..|. f y
source/library/Burrito/Internal/Type/Expression.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Expression- ( Expression(..)- ) where+ ( Expression (..),+ )+where import qualified Burrito.Internal.Type.Operator as Operator import qualified Burrito.Internal.Type.Variable as Variable@@ -10,7 +11,7 @@ import qualified Data.List.NonEmpty as NonEmpty data Expression = Expression- { operator :: Operator.Operator- , variables :: NonEmpty.NonEmpty Variable.Variable+ { operator :: Operator.Operator,+ variables :: NonEmpty.NonEmpty Variable.Variable } deriving (Data.Data, Eq, Ord, Show)
source/library/Burrito/Internal/Type/Field.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Field- ( Field(..)- ) where+ ( Field (..),+ )+where import qualified Burrito.Internal.Type.Character as Character import qualified Data.Data as Data@@ -10,4 +11,5 @@ newtype Field = Field { characters :: NonEmpty.NonEmpty (Character.Character Field)- } deriving (Data.Data, Eq, Ord, Show)+ }+ deriving (Data.Data, Eq, Ord, Show)
source/library/Burrito/Internal/Type/Literal.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Literal- ( Literal(..)- ) where+ ( Literal (..),+ )+where import qualified Burrito.Internal.Type.Character as Character import qualified Data.Data as Data@@ -10,4 +11,5 @@ newtype Literal = Literal { characters :: NonEmpty.NonEmpty (Character.Character Literal)- } deriving (Data.Data, Eq, Ord, Show)+ }+ deriving (Data.Data, Eq, Ord, Show)
source/library/Burrito/Internal/Type/Match.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Match- ( Match(..)- ) where+ ( Match (..),+ )+where import qualified Burrito.Internal.Type.MaxLength as MaxLength import qualified Data.Data as Data
source/library/Burrito/Internal/Type/MaxLength.hs view
@@ -1,11 +1,13 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.MaxLength- ( MaxLength(..)- ) where+ ( MaxLength (..),+ )+where import qualified Data.Data as Data newtype MaxLength = MaxLength { count :: Int- } deriving (Data.Data, Eq, Ord, Show)+ }+ deriving (Data.Data, Eq, Ord, Show)
source/library/Burrito/Internal/Type/Modifier.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Modifier- ( Modifier(..)- ) where+ ( Modifier (..),+ )+where import qualified Burrito.Internal.Type.MaxLength as MaxLength import qualified Data.Data as Data
source/library/Burrito/Internal/Type/Name.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Name- ( Name(..)- ) where+ ( Name (..),+ )+where import qualified Burrito.Internal.Type.Field as Field import qualified Data.Data as Data@@ -10,4 +11,5 @@ newtype Name = Name { fields :: NonEmpty.NonEmpty Field.Field- } deriving (Data.Data, Eq, Ord, Show)+ }+ deriving (Data.Data, Eq, Ord, Show)
source/library/Burrito/Internal/Type/Operator.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Operator- ( Operator(..)- ) where+ ( Operator (..),+ )+where import qualified Data.Data as Data
source/library/Burrito/Internal/Type/Template.hs view
@@ -1,9 +1,10 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Template- ( Template(..)- , render- ) where+ ( Template (..),+ render,+ )+where import qualified Burrito.Internal.Render as Render import qualified Burrito.Internal.Type.Token as Token@@ -13,7 +14,8 @@ -- | Represents a URI template. newtype Template = Template { tokens :: [Token.Token]- } deriving (Data.Data, Eq, Ord)+ }+ deriving (Data.Data, Eq, Ord) instance Show Template where show = render
source/library/Burrito/Internal/Type/Token.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Token- ( Token(..)- ) where+ ( Token (..),+ )+where import qualified Burrito.Internal.Type.Expression as Expression import qualified Burrito.Internal.Type.Literal as Literal
source/library/Burrito/Internal/Type/Value.hs view
@@ -1,8 +1,9 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Value- ( Value(..)- ) where+ ( Value (..),+ )+where import qualified Data.Data as Data import qualified Data.Map as Map
source/library/Burrito/Internal/Type/Variable.hs view
@@ -1,15 +1,16 @@ {-# LANGUAGE DeriveDataTypeable #-} module Burrito.Internal.Type.Variable- ( Variable(..)- ) where+ ( Variable (..),+ )+where import qualified Burrito.Internal.Type.Modifier as Modifier import qualified Burrito.Internal.Type.Name as Name import qualified Data.Data as Data data Variable = Variable- { name :: Name.Name- , modifier :: Modifier.Modifier+ { name :: Name.Name,+ modifier :: Modifier.Modifier } deriving (Data.Data, Eq, Ord, Show)
source/test-suite/Main.hs view
@@ -1,1466 +1,1459 @@-{-# OPTIONS_GHC -Wno-monomorphism-restriction #-}--{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE QuasiQuotes #-}--module Main- ( main- ) where--import qualified Burrito-import qualified Burrito.Internal.Parse as Parse-import qualified Burrito.Internal.Render as Render-import qualified Burrito.Internal.Type.Case as Case-import qualified Burrito.Internal.Type.Character as Character-import qualified Burrito.Internal.Type.Digit as Digit-import qualified Burrito.Internal.Type.Expression as Expression-import qualified Burrito.Internal.Type.Field as Field-import qualified Burrito.Internal.Type.Literal as Literal-import qualified Burrito.Internal.Type.MaxLength as MaxLength-import qualified Burrito.Internal.Type.Modifier as Modifier-import qualified Burrito.Internal.Type.Name as Name-import qualified Burrito.Internal.Type.Operator as Operator-import qualified Burrito.Internal.Type.Template as Template-import qualified Burrito.Internal.Type.Token as Token-import qualified Burrito.Internal.Type.Value as Value-import qualified Burrito.Internal.Type.Variable as Variable-import qualified Control.Monad as Monad-import qualified Data.List as List-import qualified Data.List.NonEmpty as NonEmpty-import qualified Data.Map as Map-import qualified Data.Maybe as Maybe-import qualified Data.Set as Set-import qualified Data.String as String-import qualified Data.Text as Text-import qualified GHC.Stack as Stack-import qualified Test.Hspec as Hspec-import qualified Test.Hspec.QuickCheck as Hspec-import qualified Test.QuickCheck as QC--main :: IO ()-main = Hspec.hspec . Hspec.describe "Burrito" $ do-- Monad.forM_ tests $ \test ->- Hspec.it (show (testInput test, unwrapOutput $ testOutput test))- $ runTest test-- Hspec.describe "match" $ do- let- matchTest- :: Stack.HasCallStack- => String- -> [(String, Burrito.Value)]- -> String- -> Hspec.Spec- matchTest input values output = Hspec.it (show (input, output)) $ do- template <- maybe (fail "invalid Template") pure $ Burrito.parse input- let matches = Burrito.match output template- matches `Hspec.shouldSatisfy` elem values- Monad.forM_ matches $ \match -> do- let it = Burrito.expand match template- Monad.when (it /= output) . fail $ show- (input, values, output, matches, match, it)-- matchTest "" [] ""- matchTest "a" [] "a"- matchTest "!" [] "!"- matchTest "\xa0" [] "%C2%A0"-- matchTest "{a}" ["a" =: s ""] ""- matchTest "{a}" ["a" =: s "A"] "A"- matchTest "{a}" ["a" =: s "AB"] "AB"-- matchTest "a{b}" ["b" =: s ""] "a"- matchTest "a{b}" ["b" =: s "B"] "aB"- matchTest "a{b}" ["b" =: s "BC"] "aBC"-- matchTest "{a}b" ["a" =: s ""] "b"- matchTest "{a}b" ["a" =: s "A"] "Ab"- matchTest "{a}b" ["a" =: s "AB"] "ABb"-- matchTest "{a}{a}" ["a" =: s "A"] "AA"- matchTest "{a}{a}" ["a" =: s "AB"] "ABAB"-- matchTest "{a}" ["a" =: s "%"] "%25"- matchTest "{a}" ["a" =: s "/"] "%2F"- matchTest "{a}" ["a" =: s "\xa0"] "%C2%A0"- matchTest "{a}" ["a" =: s "\xd7ff"] "%ED%9F%BF"- matchTest "{a}" ["a" =: s "\x10000"] "%F0%90%80%80"- matchTest "{a}" ["a" =: s "A/B"] "A%2FB"- matchTest "{a}" ["a" =: s "WX\xa0\xa1YZ"] "WX%C2%A0%C2%A1YZ"-- matchTest "{+a}" ["a" =: s "%"] "%25"- matchTest "{+a}" ["a" =: s "/"] "/"-- matchTest "{#a}" [] ""- matchTest "{#a}" ["a" =: s ""] "#"- matchTest "{#a}" ["a" =: s "A"] "#A"- matchTest "{#a}" ["a" =: s "%"] "#%25"- matchTest "{#a}" ["a" =: s "/"] "#/"-- matchTest "{.a}" [] ""- matchTest "{.a}" ["a" =: s ""] "."- matchTest "{.a}" ["a" =: s "A"] ".A"- matchTest "{.a}" ["a" =: s "%"] ".%25"- matchTest "{.a}" ["a" =: s "/"] ".%2F"-- matchTest "{/a}" [] ""- matchTest "{/a}" ["a" =: s ""] "/"- matchTest "{/a}" ["a" =: s "A"] "/A"- matchTest "{/a}" ["a" =: s "%"] "/%25"- matchTest "{/a}" ["a" =: s "/"] "/%2F"-- matchTest "{;a}" [] ""- matchTest "{;a}" ["a" =: s ""] ";a"- matchTest "{;a}" ["a" =: s "A"] ";a=A"- matchTest "{;a}" ["a" =: s "%"] ";a=%25"- matchTest "{;a}" ["a" =: s "/"] ";a=%2F"-- matchTest "{?a}" [] ""- matchTest "{?a}" ["a" =: s ""] "?a="- matchTest "{?a}" ["a" =: s "A"] "?a=A"- matchTest "{?a}" ["a" =: s "%"] "?a=%25"- matchTest "{?a}" ["a" =: s "/"] "?a=%2F"-- matchTest "{&a}" [] ""- matchTest "{&a}" ["a" =: s ""] "&a="- matchTest "{&a}" ["a" =: s "A"] "&a=A"- matchTest "{&a}" ["a" =: s "%"] "&a=%25"- matchTest "{&a}" ["a" =: s "/"] "&a=%2F"-- matchTest "{a,b}" ["a" =: s "A", "b" =: s "B"] "A,B"- matchTest "{+a,b}" ["a" =: s "A", "b" =: s "B"] "A,B"- matchTest "{#a,b}" ["a" =: s "A", "b" =: s "B"] "#A,B"- matchTest "{.a,b}" ["a" =: s "A", "b" =: s "B"] ".A.B"- matchTest "{/a,b}" ["a" =: s "A", "b" =: s "B"] "/A/B"- matchTest "{;a,b}" ["a" =: s "A", "b" =: s "B"] ";a=A;b=B"- matchTest "{?a,b}" ["a" =: s "A", "b" =: s "B"] "?a=A&b=B"- matchTest "{&a,b}" ["a" =: s "A", "b" =: s "B"] "&a=A&b=B"-- matchTest "{a,b}" ["a" =: s "A"] "A"- matchTest "{a,b,c}" ["a" =: s "A"] "A"- matchTest "{a,b,c}" ["a" =: s "A", "b" =: s "B"] "A,B"- matchTest "{a}{a,b}" ["b" =: s "B"] "B"- matchTest "{a}{a,b,c}" ["b" =: s "B"] "B"- matchTest "{a,b}{a,b,c}" ["c" =: s "C"] "C"- matchTest "{b}{a,b,c}" ["a" =: s "A", "c" =: s "C"] "A,C"- matchTest "{a}{a,b,c}" ["b" =: s "B", "c" =: s "C"] "B,C"-- matchTest "{a:1}/{a}" ["a" =: s "AB"] "A/AB"-- -- TODO: Test matching on explode modifier.- -- matchTest "{a*}" ["a" =: s "A"] "A"-- -- TODO: Test matching on lists.- -- matchTest "{a}" ["a" =: l ["A", "B"]] "A,B"-- -- TODO: Test matching on dictionaries.- -- matchTest "{a}" ["a" =: d ["k" =: "v"]] "k,v"-- Hspec.describe "uriTemplate"- . Hspec.it "works as an expression"- $ Just [Burrito.uriTemplate|a{b}c|]- `Hspec.shouldBe` Burrito.parse "a{b}c"-- Hspec.modifyMaxSize (const 10)- . Hspec.it "round trips"- . QC.property- $ \(Template template) -> Burrito.parse (show template) == Just template---- brittany-next-binding --columns 160-tests :: [Test]-tests = mconcat- [ [ Test "" [] ""- , Test "" ["a" =: s ""] ""- , Test "" ["a" =: l []] ""- , Test "" ["a" =: d []] ""- , Test "!" [] "!"- , Test "#" [] "#"- , Test "$" [] "$"- , Test "&" [] "&"- , Test "(" [] "("- , Test ")" [] ")"- , Test "*" [] "*"- , Test "+" [] "+"- , Test "," [] ","- , Test "-" [] "-"- , Test "." [] "."- , Test "/" [] "/"- , Test "0" [] "0"- , Test "9" [] "9"- , Test ":" [] ":"- , Test ";" [] ";"- , Test "=" [] "="- , Test "?" [] "?"- , Test "@" [] "@"- , Test "A" [] "A"- , Test "Z" [] "Z"- , Test "[" [] "["- , Test "]" [] "]"- , Test "_" [] "_"- , Test "a" [] "a"- , Test "z" [] "z"- , Test "~" [] "~"- , Test "\xa0" [] "%C2%A0"- , Test "\xd7ff" [] "%ED%9F%BF"- , Test "\xf900" [] "%EF%A4%80"- , Test "\xfdcf" [] "%EF%B7%8F"- , Test "\xfdf0" [] "%EF%B7%B0"- , Test "\xffef" [] "%EF%BF%AF"- , Test "\x10000" [] "%F0%90%80%80"- , Test "\x1fffd" [] "%F0%9F%BF%BD"- , Test "\x20000" [] "%F0%A0%80%80"- , Test "\x2fffd" [] "%F0%AF%BF%BD"- , Test "\x30000" [] "%F0%B0%80%80"- , Test "\x3fffd" [] "%F0%BF%BF%BD"- , Test "\x40000" [] "%F1%80%80%80"- , Test "\x4fffd" [] "%F1%8F%BF%BD"- , Test "\x50000" [] "%F1%90%80%80"- , Test "\x5fffd" [] "%F1%9F%BF%BD"- , Test "\x60000" [] "%F1%A0%80%80"- , Test "\x6fffd" [] "%F1%AF%BF%BD"- , Test "\x70000" [] "%F1%B0%80%80"- , Test "\x7fffd" [] "%F1%BF%BF%BD"- , Test "\x80000" [] "%F2%80%80%80"- , Test "\x8fffd" [] "%F2%8F%BF%BD"- , Test "\x90000" [] "%F2%90%80%80"- , Test "\x9fffd" [] "%F2%9F%BF%BD"- , Test "\xa0000" [] "%F2%A0%80%80"- , Test "\xafffd" [] "%F2%AF%BF%BD"- , Test "\xb0000" [] "%F2%B0%80%80"- , Test "\xbfffd" [] "%F2%BF%BF%BD"- , Test "\xc0000" [] "%F3%80%80%80"- , Test "\xcfffd" [] "%F3%8F%BF%BD"- , Test "\xd0000" [] "%F3%90%80%80"- , Test "\xdfffd" [] "%F3%9F%BF%BD"- , Test "\xe1000" [] "%F3%A1%80%80"- , Test "\xefffd" [] "%F3%AF%BF%BD"- , Test "\xe000" [] "%EE%80%80"- , Test "\xf8ff" [] "%EF%A3%BF"- , Test "\xf0000" [] "%F3%B0%80%80"- , Test "\xffffd" [] "%F3%BF%BF%BD"- , Test "\x100000" [] "%F4%80%80%80"- , Test "\x10fffd" [] "%F4%8F%BF%BD"- , Test "%00" [] "%00"- , Test "%AA" [] "%AA"- , Test "%Aa" [] "%Aa"- , Test "%aA" [] "%aA"- , Test "%aa" [] "%aa"- , Test "%" [] noParse- , Test "%0" [] noParse- , Test "%0z" [] noParse- , Test "%z" [] noParse- , Test "%30" [] "%30"- , Test " " [] noParse- , Test "\"" [] noParse- , Test "'" [] noParse- , Test "%" [] noParse- , Test "<" [] noParse- , Test ">" [] noParse- , Test "\\" [] noParse- , Test "^" [] noParse- , Test "`" [] noParse- , Test "{" [] noParse- , Test "|" [] noParse- , Test "}" [] noParse- , Test "{}" [] noParse- , Test "{,}" [] noParse- , Test "{a,,b}" [] noParse- , Test "{+}" [] noParse- , Test "{:1}" [] noParse- , Test "{*}" [] noParse- , Test "{AZ}" [] ""- , Test "{az}" [] ""- , Test "{09}" [] ""- , Test "{_a}" [] ""- , Test "{a_}" [] ""- , Test "{_}" [] ""- , Test "{A.A}" [] ""- , Test "{a.a}" [] ""- , Test "{0.0}" [] ""- , Test "{_._}" [] ""- , Test "{%aa.%aa}" [] ""- , Test "{.}" [] noParse- , Test "{a.}" [] noParse- , Test "{+.a}" [] noParse- , Test "{a..b}" [] noParse- , Test "{%00}" [] ""- , Test "{%}" [] noParse- , Test "{%0}" [] noParse- , Test "{%0z}" [] noParse- , Test "{%z}" [] noParse- , Test "{!}" [] noParse- , Test "{" [] noParse- , Test "{{}" [] noParse- , Test "}" [] noParse- , Test "{}}" [] noParse- , Test "{a,b}" [] ""- , Test "{a,b,c,d}" [] ""- , Test "{a,a}" [] ""- , Test "{a:5}" [] ""- , Test "{a:67}" [] ""- , Test "{a:801}" [] ""- , Test "{a:234}" [] ""- , Test "{a:9999}" [] ""- , Test "{a:123}" ["a" =: s (replicate 200 'a')] . Output . Just $ replicate 123 'a'- , Test "{a:}" [] noParse- , Test "{a:0}" [] noParse- , Test "{a:10000}" [] noParse- , Test "{a:-1}" [] noParse- , Test "{a*}" [] ""- , Test "{a:1*}" [] noParse- , Test "{a*:1}" [] noParse- , Test "{a,b:1,c*}" [] ""- , Test "{+a}" [] ""- , Test "{#a}" [] ""- , Test "{.a}" [] ""- , Test "{/a}" [] ""- , Test "{;a}" [] ""- , Test "{?a}" [] ""- , Test "{&a}" [] ""- , Test "{=a}" [] noParse- , Test "{,a}" [] noParse- , Test "{!a}" [] noParse- , Test "{@a}" [] noParse- , Test "{|a}" [] noParse- , Test "{+#a}" [] noParse- , Test "{+a,#b}" [] noParse- , Test "{+a:1}" [] ""- , Test "{#a*}" [] ""- , Test "{+a,b}" [] ""- , Test "{#a,b}" [] ""- , Test "{.a,b}" [] ""- , Test "{/a,b}" [] ""- , Test "{;a,b}" [] ""- , Test "{?a,b}" [] ""- , Test "{&a,b}" [] ""- , Test "{a}{b}" [] ""- , Test "{a}{b}{c}{d}" [] ""- , Test "{a}{a}" [] ""- , Test "{{}}" [] noParse- , Test "{a{b}}" [] noParse- , Test "{{a}b}" [] noParse- , Test "{a{b}c}" [] noParse- , Test "a{b}" [] "a"- , Test "{a}b" [] "b"- , Test "a{b}c" [] "ac"- , Test "{a}b{c}" [] "b"- , Test "{a}" [] ""- , Test "{+a}" [] ""- , Test "{#a}" [] ""- , Test "{.a}" [] ""- , Test "{/a}" [] ""- , Test "{;a}" [] ""- , Test "{?a}" [] ""- , Test "{&a}" [] ""- , Test "http://example.com/~{username}/" ["username" =: s "fred"] "http://example.com/~fred/"- , Test "http://example.com/~{username}/" ["username" =: s "mark"] "http://example.com/~mark/"- , Test "http://example.com/dictionary/{term:1}/{term}" ["term" =: s "cat"] "http://example.com/dictionary/c/cat"- , Test "http://example.com/dictionary/{term:1}/{term}" ["term" =: s "dog"] "http://example.com/dictionary/d/dog"- , Test "http://example.com/search{?q,lang}" ["q" =: s "cat", "lang" =: s "en"] "http://example.com/search?q=cat&lang=en"- , Test "http://example.com/search{?q,lang}" ["q" =: s "chien", "lang" =: s "fr"] "http://example.com/search?q=chien&lang=fr"- , Test "http://www.example.com/foo{?query,number}" ["query" =: s "mycelium", "number" =: s "100"] "http://www.example.com/foo?query=mycelium&number=100"- , Test "http://www.example.com/foo{?query,number}" ["number" =: s "100"] "http://www.example.com/foo?number=100"- , Test "http://www.example.com/foo{?query,number}" [] "http://www.example.com/foo"- , Test "{a}" [] ""- , Test "{a}" ["a" =: l []] ""- , Test "{a}" ["a" =: d []] ""- , Test "{a}" ["a" =: s ""] ""- , Test "{a}" ["a" =: s "A"] "A"- , Test "{a}" ["a" =: s "~"] "~"- , Test "{a}" ["a" =: s "%"] "%25"- , Test "{a}" ["a" =: s "?"] "%3F"- , Test "{a}" ["a" =: s "&"] "%26"- , Test "{a}" ["a" =: s "\xa0"] "%C2%A0"- , Test "{a}" ["a" =: s "\xd7ff"] "%ED%9F%BF"- , Test "{a}" ["a" =: s "\x10000"] "%F0%90%80%80"- , Test "{a}" ["a" =: l ["A"]] "A"- , Test "{a}" ["a" =: l ["A", "B"]] "A,B"- , Test "{a}" ["a" =: l ["%"]] "%25"- , Test "{a}" ["a" =: l ["\xa0"]] "%C2%A0"- , Test "{a}" ["a" =: d ["A" =: "1"]] "A,1"- , Test "{a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2"- , Test "{a}" ["a" =: d ["A" =: "%"]] "A,%25"- , Test "{a}" ["a" =: d ["A" =: "\xa0"]] "A,%C2%A0"- , Test "{a}" ["a" =: d ["%" =: "1"]] "%25,1"- , Test "{a*}" [] ""- , Test "{a*}" ["a" =: s ""] ""- , Test "{a*}" ["a" =: s "A"] "A"- , Test "{a*}" ["a" =: l []] ""- , Test "{a*}" ["a" =: l ["A"]] "A"- , Test "{a*}" ["a" =: l ["A", "B"]] "A,B"- , Test "{a*}" ["a" =: d []] ""- , Test "{a*}" ["a" =: d ["A" =: "1"]] "A=1"- , Test "{a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2"- , Test "{a:1}" [] ""- , Test "{a:1}" ["a" =: s ""] ""- , Test "{a:1}" ["a" =: s "A"] "A"- , Test "{a:1}" ["a" =: s "AB"] "A"- , Test "{a:1}" ["a" =: s "%B"] "%25"- , Test "{a:1}" ["a" =: s "\xa0\&B"] "%C2%A0"- , Test "{a:1}" ["a" =: s "\xd7ff\&B"] "%ED%9F%BF"- , Test "{a:1}" ["a" =: s "\x10000\&B"] "%F0%90%80%80"- , Test "{a:1}" ["a" =: l []] ""- , Test "{a:1}" ["a" =: l ["AB"]] "AB"- , Test "{a:1}" ["a" =: l ["AB", "CD"]] "AB,CD"- , Test "{a:1}" ["a" =: d []] ""- , Test "{a:1}" ["a" =: d ["AB" =: "12"]] "AB,12"- , Test "{a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34"- , Test "{a,a}" [] ""- , Test "{a,a}" ["a" =: l []] ""- , Test "{a,a}" ["a" =: d []] ""- , Test "{a,a}" ["a" =: s ""] ","- , Test "{a,b}" ["a" =: s ""] ""- , Test "{a,b}" ["b" =: s ""] ""- , Test "{%aa}" ["%aa" =: s "A"] "A"- , Test "{%aa}" ["%aa" =: l ["A", "B"]] "A,B"- , Test "{%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2"- , Test "{%aa*}" ["%aa" =: s "A"] "A"- , Test "{%aa*}" ["%aa" =: l ["A", "B"]] "A,B"- , Test "{%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2"- , Test "{+a}" [] ""- , Test "{+a}" ["a" =: l []] ""- , Test "{+a}" ["a" =: d []] ""- , Test "{+a}" ["a" =: s ""] ""- , Test "{+a}" ["a" =: s "A"] "A"- , Test "{+a}" ["a" =: s "~"] "~"- , Test "{+a}" ["a" =: s "%"] "%25"- , Test "{+a}" ["a" =: s "?"] "?"- , Test "{+a}" ["a" =: s "&"] "&"- , Test "{+a}" ["a" =: s "\xa0"] "%C2%A0"- , Test "{+a}" ["a" =: s "\xd7ff"] "%ED%9F%BF"- , Test "{+a}" ["a" =: s "\x10000"] "%F0%90%80%80"- , Test "{+a}" ["a" =: l ["A"]] "A"- , Test "{+a}" ["a" =: l ["A", "B"]] "A,B"- , Test "{+a}" ["a" =: l ["%"]] "%25"- , Test "{+a}" ["a" =: l ["\xa0"]] "%C2%A0"- , Test "{+a}" ["a" =: d ["A" =: "1"]] "A,1"- , Test "{+a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2"- , Test "{+a}" ["a" =: d ["A" =: "%"]] "A,%25"- , Test "{+a}" ["a" =: d ["A" =: "\xa0"]] "A,%C2%A0"- , Test "{+a}" ["a" =: d ["%" =: "1"]] "%25,1"- , Test "{+a*}" [] ""- , Test "{+a*}" ["a" =: s ""] ""- , Test "{+a*}" ["a" =: s "A"] "A"- , Test "{+a*}" ["a" =: l []] ""- , Test "{+a*}" ["a" =: l ["A"]] "A"- , Test "{+a*}" ["a" =: l ["A", "B"]] "A,B"- , Test "{+a*}" ["a" =: d []] ""- , Test "{+a*}" ["a" =: d ["A" =: "1"]] "A=1"- , Test "{+a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2"- , Test "{+a:1}" [] ""- , Test "{+a:1}" ["a" =: s ""] ""- , Test "{+a:1}" ["a" =: s "A"] "A"- , Test "{+a:1}" ["a" =: s "AB"] "A"- , Test "{+a:1}" ["a" =: s "%B"] "%25"- , Test "{+a:1}" ["a" =: s "\xa0\&B"] "%C2%A0"- , Test "{+a:1}" ["a" =: s "\xd7ff\&B"] "%ED%9F%BF"- , Test "{+a:1}" ["a" =: s "\x10000\&B"] "%F0%90%80%80"- , Test "{+a:1}" ["a" =: l []] ""- , Test "{+a:1}" ["a" =: l ["AB"]] "AB"- , Test "{+a:1}" ["a" =: l ["AB", "CD"]] "AB,CD"- , Test "{+a:1}" ["a" =: d []] ""- , Test "{+a:1}" ["a" =: d ["AB" =: "12"]] "AB,12"- , Test "{+a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34"- , Test "{+a,a}" [] ""- , Test "{+a,a}" ["a" =: l []] ""- , Test "{+a,a}" ["a" =: d []] ""- , Test "{+a,a}" ["a" =: s ""] ","- , Test "{+a,b}" ["a" =: s ""] ""- , Test "{+a,b}" ["b" =: s ""] ""- , Test "{+%aa}" ["%aa" =: s "A"] "A"- , Test "{+%aa}" ["%aa" =: l ["A", "B"]] "A,B"- , Test "{+%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2"- , Test "{+%aa*}" ["%aa" =: s "A"] "A"- , Test "{+%aa*}" ["%aa" =: l ["A", "B"]] "A,B"- , Test "{+%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2"- , Test "{#a}" [] ""- , Test "{#a}" ["a" =: l []] ""- , Test "{#a}" ["a" =: d []] ""- , Test "{#a}" ["a" =: s ""] "#"- , Test "{#a}" ["a" =: s "A"] "#A"- , Test "{#a}" ["a" =: s "~"] "#~"- , Test "{#a}" ["a" =: s "%"] "#%25"- , Test "{#a}" ["a" =: s "?"] "#?"- , Test "{#a}" ["a" =: s "&"] "#&"- , Test "{#a}" ["a" =: s "\xa0"] "#%C2%A0"- , Test "{#a}" ["a" =: s "\xd7ff"] "#%ED%9F%BF"- , Test "{#a}" ["a" =: s "\x10000"] "#%F0%90%80%80"- , Test "{#a}" ["a" =: l ["A"]] "#A"- , Test "{#a}" ["a" =: l ["A", "B"]] "#A,B"- , Test "{#a}" ["a" =: l ["%"]] "#%25"- , Test "{#a}" ["a" =: l ["\xa0"]] "#%C2%A0"- , Test "{#a}" ["a" =: d ["A" =: "1"]] "#A,1"- , Test "{#a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "#A,1,B,2"- , Test "{#a}" ["a" =: d ["A" =: "%"]] "#A,%25"- , Test "{#a}" ["a" =: d ["A" =: "\xa0"]] "#A,%C2%A0"- , Test "{#a}" ["a" =: d ["%" =: "1"]] "#%25,1"- , Test "{#a*}" [] ""- , Test "{#a*}" ["a" =: s ""] "#"- , Test "{#a*}" ["a" =: s "A"] "#A"- , Test "{#a*}" ["a" =: l []] ""- , Test "{#a*}" ["a" =: l ["A"]] "#A"- , Test "{#a*}" ["a" =: l ["A", "B"]] "#A,B"- , Test "{#a*}" ["a" =: d []] ""- , Test "{#a*}" ["a" =: d ["A" =: "1"]] "#A=1"- , Test "{#a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "#A=1,B=2"- , Test "{#a:1}" [] ""- , Test "{#a:1}" ["a" =: s ""] "#"- , Test "{#a:1}" ["a" =: s "A"] "#A"- , Test "{#a:1}" ["a" =: s "AB"] "#A"- , Test "{#a:1}" ["a" =: s "%B"] "#%25"- , Test "{#a:1}" ["a" =: s "\xa0\&B"] "#%C2%A0"- , Test "{#a:1}" ["a" =: s "\xd7ff\&B"] "#%ED%9F%BF"- , Test "{#a:1}" ["a" =: s "\x10000\&B"] "#%F0%90%80%80"- , Test "{#a:1}" ["a" =: l []] ""- , Test "{#a:1}" ["a" =: l ["AB"]] "#AB"- , Test "{#a:1}" ["a" =: l ["AB", "CD"]] "#AB,CD"- , Test "{#a:1}" ["a" =: d []] ""- , Test "{#a:1}" ["a" =: d ["AB" =: "12"]] "#AB,12"- , Test "{#a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "#AB,12,CD,34"- , Test "{#a,a}" [] ""- , Test "{#a,a}" ["a" =: l []] ""- , Test "{#a,a}" ["a" =: d []] ""- , Test "{#a,a}" ["a" =: s ""] "#,"- , Test "{#a,b}" ["a" =: s ""] "#"- , Test "{#a,b}" ["b" =: s ""] "#"- , Test "{#%aa}" ["%aa" =: s "A"] "#A"- , Test "{#%aa}" ["%aa" =: l ["A", "B"]] "#A,B"- , Test "{#%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "#A,1,B,2"- , Test "{#%aa*}" ["%aa" =: s "A"] "#A"- , Test "{#%aa*}" ["%aa" =: l ["A", "B"]] "#A,B"- , Test "{#%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "#A=1,B=2"- , Test "{.a}" [] ""- , Test "{.a}" ["a" =: l []] ""- , Test "{.a}" ["a" =: d []] ""- , Test "{.a}" ["a" =: s ""] "."- , Test "{.a}" ["a" =: s "A"] ".A"- , Test "{.a}" ["a" =: s "~"] ".~"- , Test "{.a}" ["a" =: s "%"] ".%25"- , Test "{.a}" ["a" =: s "?"] ".%3F"- , Test "{.a}" ["a" =: s "&"] ".%26"- , Test "{.a}" ["a" =: s "\xa0"] ".%C2%A0"- , Test "{.a}" ["a" =: s "\xd7ff"] ".%ED%9F%BF"- , Test "{.a}" ["a" =: s "\x10000"] ".%F0%90%80%80"- , Test "{.a}" ["a" =: l ["A"]] ".A"- , Test "{.a}" ["a" =: l ["A", "B"]] ".A,B"- , Test "{.a}" ["a" =: l ["%"]] ".%25"- , Test "{.a}" ["a" =: l ["\xa0"]] ".%C2%A0"- , Test "{.a}" ["a" =: d ["A" =: "1"]] ".A,1"- , Test "{.a}" ["a" =: d ["A" =: "1", "B" =: "2"]] ".A,1,B,2"- , Test "{.a}" ["a" =: d ["A" =: "%"]] ".A,%25"- , Test "{.a}" ["a" =: d ["A" =: "\xa0"]] ".A,%C2%A0"- , Test "{.a}" ["a" =: d ["%" =: "1"]] ".%25,1"- , Test "{.a*}" [] ""- , Test "{.a*}" ["a" =: s ""] "."- , Test "{.a*}" ["a" =: s "A"] ".A"- , Test "{.a*}" ["a" =: l []] ""- , Test "{.a*}" ["a" =: l ["A"]] ".A"- , Test "{.a*}" ["a" =: l ["A", "B"]] ".A.B"- , Test "{.a*}" ["a" =: d []] ""- , Test "{.a*}" ["a" =: d ["A" =: "1"]] ".A=1"- , Test "{.a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] ".A=1.B=2"- , Test "{.a:1}" [] ""- , Test "{.a:1}" ["a" =: s ""] "."- , Test "{.a:1}" ["a" =: s "A"] ".A"- , Test "{.a:1}" ["a" =: s "AB"] ".A"- , Test "{.a:1}" ["a" =: s "%B"] ".%25"- , Test "{.a:1}" ["a" =: s "\xa0\&B"] ".%C2%A0"- , Test "{.a:1}" ["a" =: s "\xd7ff\&B"] ".%ED%9F%BF"- , Test "{.a:1}" ["a" =: s "\x10000\&B"] ".%F0%90%80%80"- , Test "{.a:1}" ["a" =: l []] ""- , Test "{.a:1}" ["a" =: l ["AB"]] ".AB"- , Test "{.a:1}" ["a" =: l ["AB", "CD"]] ".AB,CD"- , Test "{.a:1}" ["a" =: d []] ""- , Test "{.a:1}" ["a" =: d ["AB" =: "12"]] ".AB,12"- , Test "{.a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] ".AB,12,CD,34"- , Test "{.a,a}" [] ""- , Test "{.a,a}" ["a" =: l []] ""- , Test "{.a,a}" ["a" =: d []] ""- , Test "{.a,a}" ["a" =: s ""] ".."- , Test "{.a,b}" ["a" =: s ""] "."- , Test "{.a,b}" ["b" =: s ""] "."- , Test "{.%aa}" ["%aa" =: s "A"] ".A"- , Test "{.%aa}" ["%aa" =: l ["A", "B"]] ".A,B"- , Test "{.%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ".A,1,B,2"- , Test "{.%aa*}" ["%aa" =: s "A"] ".A"- , Test "{.%aa*}" ["%aa" =: l ["A", "B"]] ".A.B"- , Test "{.%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ".A=1.B=2"- , Test "{/a}" [] ""- , Test "{/a}" ["a" =: l []] ""- , Test "{/a}" ["a" =: d []] ""- , Test "{/a}" ["a" =: s ""] "/"- , Test "{/a}" ["a" =: s "A"] "/A"- , Test "{/a}" ["a" =: s "~"] "/~"- , Test "{/a}" ["a" =: s "%"] "/%25"- , Test "{/a}" ["a" =: s "?"] "/%3F"- , Test "{/a}" ["a" =: s "&"] "/%26"- , Test "{/a}" ["a" =: s "\xa0"] "/%C2%A0"- , Test "{/a}" ["a" =: s "\xd7ff"] "/%ED%9F%BF"- , Test "{/a}" ["a" =: s "\x10000"] "/%F0%90%80%80"- , Test "{/a}" ["a" =: l ["A"]] "/A"- , Test "{/a}" ["a" =: l ["A", "B"]] "/A,B"- , Test "{/a}" ["a" =: l ["%"]] "/%25"- , Test "{/a}" ["a" =: l ["\xa0"]] "/%C2%A0"- , Test "{/a}" ["a" =: d ["A" =: "1"]] "/A,1"- , Test "{/a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "/A,1,B,2"- , Test "{/a}" ["a" =: d ["A" =: "%"]] "/A,%25"- , Test "{/a}" ["a" =: d ["A" =: "\xa0"]] "/A,%C2%A0"- , Test "{/a}" ["a" =: d ["%" =: "1"]] "/%25,1"- , Test "{/a*}" [] ""- , Test "{/a*}" ["a" =: s ""] "/"- , Test "{/a*}" ["a" =: s "A"] "/A"- , Test "{/a*}" ["a" =: l []] ""- , Test "{/a*}" ["a" =: l ["A"]] "/A"- , Test "{/a*}" ["a" =: l ["A", "B"]] "/A/B"- , Test "{/a*}" ["a" =: d []] ""- , Test "{/a*}" ["a" =: d ["A" =: "1"]] "/A=1"- , Test "{/a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "/A=1/B=2"- , Test "{/a:1}" [] ""- , Test "{/a:1}" ["a" =: s ""] "/"- , Test "{/a:1}" ["a" =: s "A"] "/A"- , Test "{/a:1}" ["a" =: s "AB"] "/A"- , Test "{/a:1}" ["a" =: s "%B"] "/%25"- , Test "{/a:1}" ["a" =: s "\xa0\&B"] "/%C2%A0"- , Test "{/a:1}" ["a" =: s "\xd7ff\&B"] "/%ED%9F%BF"- , Test "{/a:1}" ["a" =: s "\x10000\&B"] "/%F0%90%80%80"- , Test "{/a:1}" ["a" =: l []] ""- , Test "{/a:1}" ["a" =: l ["AB"]] "/AB"- , Test "{/a:1}" ["a" =: l ["AB", "CD"]] "/AB,CD"- , Test "{/a:1}" ["a" =: d []] ""- , Test "{/a:1}" ["a" =: d ["AB" =: "12"]] "/AB,12"- , Test "{/a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "/AB,12,CD,34"- , Test "{/a,a}" [] ""- , Test "{/a,a}" ["a" =: l []] ""- , Test "{/a,a}" ["a" =: d []] ""- , Test "{/a,a}" ["a" =: s ""] "//"- , Test "{/a,b}" ["a" =: s ""] "/"- , Test "{/a,b}" ["b" =: s ""] "/"- , Test "{/%aa}" ["%aa" =: s "A"] "/A"- , Test "{/%aa}" ["%aa" =: l ["A", "B"]] "/A,B"- , Test "{/%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "/A,1,B,2"- , Test "{/%aa*}" ["%aa" =: s "A"] "/A"- , Test "{/%aa*}" ["%aa" =: l ["A", "B"]] "/A/B"- , Test "{/%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "/A=1/B=2"- , Test "{;a}" [] ""- , Test "{;a}" ["a" =: l []] ""- , Test "{;a}" ["a" =: d []] ""- , Test "{;a}" ["a" =: s ""] ";a"- , Test "{;a}" ["a" =: s "A"] ";a=A"- , Test "{;a}" ["a" =: s "~"] ";a=~"- , Test "{;a}" ["a" =: s "%"] ";a=%25"- , Test "{;a}" ["a" =: s "?"] ";a=%3F"- , Test "{;a}" ["a" =: s "&"] ";a=%26"- , Test "{;a}" ["a" =: s "\xa0"] ";a=%C2%A0"- , Test "{;a}" ["a" =: s "\xd7ff"] ";a=%ED%9F%BF"- , Test "{;a}" ["a" =: s "\x10000"] ";a=%F0%90%80%80"- , Test "{;a}" ["a" =: l ["A"]] ";a=A"- , Test "{;a}" ["a" =: l ["A", "B"]] ";a=A,B"- , Test "{;a}" ["a" =: l ["%"]] ";a=%25"- , Test "{;a}" ["a" =: l ["\xa0"]] ";a=%C2%A0"- , Test "{;a}" ["a" =: d ["A" =: "1"]] ";a=A,1"- , Test "{;a}" ["a" =: d ["A" =: "1", "B" =: "2"]] ";a=A,1,B,2"- , Test "{;a}" ["a" =: d ["A" =: "%"]] ";a=A,%25"- , Test "{;a}" ["a" =: d ["A" =: "\xa0"]] ";a=A,%C2%A0"- , Test "{;a}" ["a" =: d ["%" =: "1"]] ";a=%25,1"- , Test "{;a*}" [] ""- , Test "{;a*}" ["a" =: s ""] ";a"- , Test "{;a*}" ["a" =: s "A"] ";a=A"- , Test "{;a*}" ["a" =: l []] ""- , Test "{;a*}" ["a" =: l ["A"]] ";a=A"- , Test "{;a*}" ["a" =: l ["A", "B"]] ";a=A;a=B"- , Test "{;a*}" ["a" =: d []] ""- , Test "{;a*}" ["a" =: d ["A" =: "1"]] ";A=1"- , Test "{;a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] ";A=1;B=2"- , Test "{;a:1}" [] ""- , Test "{;a:1}" ["a" =: s ""] ";a"- , Test "{;a:1}" ["a" =: s "A"] ";a=A"- , Test "{;a:1}" ["a" =: s "AB"] ";a=A"- , Test "{;a:1}" ["a" =: s "%B"] ";a=%25"- , Test "{;a:1}" ["a" =: s "\xa0\&B"] ";a=%C2%A0"- , Test "{;a:1}" ["a" =: s "\xd7ff\&B"] ";a=%ED%9F%BF"- , Test "{;a:1}" ["a" =: s "\x10000\&B"] ";a=%F0%90%80%80"- , Test "{;a:1}" ["a" =: l []] ""- , Test "{;a:1}" ["a" =: l ["AB"]] ";a=AB"- , Test "{;a:1}" ["a" =: l ["AB", "CD"]] ";a=AB,CD"- , Test "{;a:1}" ["a" =: d []] ""- , Test "{;a:1}" ["a" =: d ["AB" =: "12"]] ";a=AB,12"- , Test "{;a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] ";a=AB,12,CD,34"- , Test "{;a,a}" [] ""- , Test "{;a,a}" ["a" =: l []] ""- , Test "{;a,a}" ["a" =: d []] ""- , Test "{;a,a}" ["a" =: s ""] ";a;a"- , Test "{;a,b}" ["a" =: s ""] ";a"- , Test "{;a,b}" ["b" =: s ""] ";b"- , Test "{;%aa}" ["%aa" =: s "A"] ";%aa=A"- , Test "{;%aa}" ["%aa" =: l ["A", "B"]] ";%aa=A,B"- , Test "{;%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ";%aa=A,1,B,2"- , Test "{;%aa*}" ["%aa" =: s "A"] ";%aa=A"- , Test "{;%aa*}" ["%aa" =: l ["A", "B"]] ";%aa=A;%aa=B"- , Test "{;%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ";A=1;B=2"- , Test "{?a}" [] ""- , Test "{?a}" ["a" =: l []] ""- , Test "{?a}" ["a" =: d []] ""- , Test "{?a}" ["a" =: s ""] "?a="- , Test "{?a}" ["a" =: s "A"] "?a=A"- , Test "{?a}" ["a" =: s "~"] "?a=~"- , Test "{?a}" ["a" =: s "%"] "?a=%25"- , Test "{?a}" ["a" =: s "?"] "?a=%3F"- , Test "{?a}" ["a" =: s "&"] "?a=%26"- , Test "{?a}" ["a" =: s "\xa0"] "?a=%C2%A0"- , Test "{?a}" ["a" =: s "\xd7ff"] "?a=%ED%9F%BF"- , Test "{?a}" ["a" =: s "\x10000"] "?a=%F0%90%80%80"- , Test "{?a}" ["a" =: l ["A"]] "?a=A"- , Test "{?a}" ["a" =: l ["A", "B"]] "?a=A,B"- , Test "{?a}" ["a" =: l ["%"]] "?a=%25"- , Test "{?a}" ["a" =: l ["\xa0"]] "?a=%C2%A0"- , Test "{?a}" ["a" =: d ["A" =: "1"]] "?a=A,1"- , Test "{?a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "?a=A,1,B,2"- , Test "{?a}" ["a" =: d ["A" =: "%"]] "?a=A,%25"- , Test "{?a}" ["a" =: d ["A" =: "\xa0"]] "?a=A,%C2%A0"- , Test "{?a}" ["a" =: d ["%" =: "1"]] "?a=%25,1"- , Test "{?a*}" [] ""- , Test "{?a*}" ["a" =: s ""] "?a="- , Test "{?a*}" ["a" =: s "A"] "?a=A"- , Test "{?a*}" ["a" =: l []] ""- , Test "{?a*}" ["a" =: l ["A"]] "?a=A"- , Test "{?a*}" ["a" =: l ["A", "B"]] "?a=A&a=B"- , Test "{?a*}" ["a" =: d []] ""- , Test "{?a*}" ["a" =: d ["A" =: "1"]] "?A=1"- , Test "{?a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "?A=1&B=2"- , Test "{?a:1}" [] ""- , Test "{?a:1}" ["a" =: s ""] "?a="- , Test "{?a:1}" ["a" =: s "A"] "?a=A"- , Test "{?a:1}" ["a" =: s "AB"] "?a=A"- , Test "{?a:1}" ["a" =: s "%B"] "?a=%25"- , Test "{?a:1}" ["a" =: s "\xa0\&B"] "?a=%C2%A0"- , Test "{?a:1}" ["a" =: s "\xd7ff\&B"] "?a=%ED%9F%BF"- , Test "{?a:1}" ["a" =: s "\x10000\&B"] "?a=%F0%90%80%80"- , Test "{?a:1}" ["a" =: l []] ""- , Test "{?a:1}" ["a" =: l ["AB"]] "?a=AB"- , Test "{?a:1}" ["a" =: l ["AB", "CD"]] "?a=AB,CD"- , Test "{?a:1}" ["a" =: d []] ""- , Test "{?a:1}" ["a" =: d ["AB" =: "12"]] "?a=AB,12"- , Test "{?a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "?a=AB,12,CD,34"- , Test "{?a,a}" [] ""- , Test "{?a,a}" ["a" =: l []] ""- , Test "{?a,a}" ["a" =: d []] ""- , Test "{?a,a}" ["a" =: s ""] "?a=&a="- , Test "{?a,b}" ["a" =: s ""] "?a="- , Test "{?a,b}" ["b" =: s ""] "?b="- , Test "{?%aa}" ["%aa" =: s "A"] "?%aa=A"- , Test "{?%aa}" ["%aa" =: l ["A", "B"]] "?%aa=A,B"- , Test "{?%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "?%aa=A,1,B,2"- , Test "{?%aa*}" ["%aa" =: s "A"] "?%aa=A"- , Test "{?%aa*}" ["%aa" =: l ["A", "B"]] "?%aa=A&%aa=B"- , Test "{?%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "?A=1&B=2"- , Test "{&a}" [] ""- , Test "{&a}" ["a" =: l []] ""- , Test "{&a}" ["a" =: d []] ""- , Test "{&a}" ["a" =: s ""] "&a="- , Test "{&a}" ["a" =: s "A"] "&a=A"- , Test "{&a}" ["a" =: s "~"] "&a=~"- , Test "{&a}" ["a" =: s "%"] "&a=%25"- , Test "{&a}" ["a" =: s "?"] "&a=%3F"- , Test "{&a}" ["a" =: s "&"] "&a=%26"- , Test "{&a}" ["a" =: s "\xa0"] "&a=%C2%A0"- , Test "{&a}" ["a" =: s "\xd7ff"] "&a=%ED%9F%BF"- , Test "{&a}" ["a" =: s "\x10000"] "&a=%F0%90%80%80"- , Test "{&a}" ["a" =: l ["A"]] "&a=A"- , Test "{&a}" ["a" =: l ["A", "B"]] "&a=A,B"- , Test "{&a}" ["a" =: l ["%"]] "&a=%25"- , Test "{&a}" ["a" =: l ["\xa0"]] "&a=%C2%A0"- , Test "{&a}" ["a" =: d ["A" =: "1"]] "&a=A,1"- , Test "{&a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "&a=A,1,B,2"- , Test "{&a}" ["a" =: d ["A" =: "%"]] "&a=A,%25"- , Test "{&a}" ["a" =: d ["A" =: "\xa0"]] "&a=A,%C2%A0"- , Test "{&a}" ["a" =: d ["%" =: "1"]] "&a=%25,1"- , Test "{&a*}" [] ""- , Test "{&a*}" ["a" =: s ""] "&a="- , Test "{&a*}" ["a" =: s "A"] "&a=A"- , Test "{&a*}" ["a" =: l []] ""- , Test "{&a*}" ["a" =: l ["A"]] "&a=A"- , Test "{&a*}" ["a" =: l ["A", "B"]] "&a=A&a=B"- , Test "{&a*}" ["a" =: d []] ""- , Test "{&a*}" ["a" =: d ["A" =: "1"]] "&A=1"- , Test "{&a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "&A=1&B=2"- , Test "{&a:1}" [] ""- , Test "{&a:1}" ["a" =: s ""] "&a="- , Test "{&a:1}" ["a" =: s "A"] "&a=A"- , Test "{&a:1}" ["a" =: s "AB"] "&a=A"- , Test "{&a:1}" ["a" =: s "%B"] "&a=%25"- , Test "{&a:1}" ["a" =: s "\xa0\&B"] "&a=%C2%A0"- , Test "{&a:1}" ["a" =: s "\xd7ff\&B"] "&a=%ED%9F%BF"- , Test "{&a:1}" ["a" =: s "\x10000\&B"] "&a=%F0%90%80%80"- , Test "{&a:1}" ["a" =: l []] ""- , Test "{&a:1}" ["a" =: l ["AB"]] "&a=AB"- , Test "{&a:1}" ["a" =: l ["AB", "CD"]] "&a=AB,CD"- , Test "{&a:1}" ["a" =: d []] ""- , Test "{&a:1}" ["a" =: d ["AB" =: "12"]] "&a=AB,12"- , Test "{&a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "&a=AB,12,CD,34"- , Test "{&a,a}" [] ""- , Test "{&a,a}" ["a" =: l []] ""- , Test "{&a,a}" ["a" =: d []] ""- , Test "{&a,a}" ["a" =: s ""] "&a=&a="- , Test "{&a,b}" ["a" =: s ""] "&a="- , Test "{&a,b}" ["b" =: s ""] "&b="- , Test "{&%aa}" ["%aa" =: s "A"] "&%aa=A"- , Test "{&%aa}" ["%aa" =: l ["A", "B"]] "&%aa=A,B"- , Test "{&%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "&%aa=A,1,B,2"- , Test "{&%aa*}" ["%aa" =: s "A"] "&%aa=A"- , Test "{&%aa*}" ["%aa" =: l ["A", "B"]] "&%aa=A&%aa=B"- , Test "{&%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "&A=1&B=2"- ]- , let values = ["%AA" =: s "1", "%Aa" =: s "2", "%aA" =: s "3", "%aa" =: s "4"]- in- [ Test "{%AA}" values "1"- , Test "{%Aa}" values "2"- , Test "{%aA}" values "3"- , Test "{%aa}" values "4"- -- This comment forces Brittany to use a multi-line layout.- ]- , let values = ["a" =: s "abcdefghijklmnopqrstuvwxyz"]- in- [ Test "{a:1}" values "a"- , Test "{a:5}" values "abcde"- , Test "{a:10}" values "abcdefghij"- , Test "{a:15}" values "abcdefghijklmno"- , Test "{a:20}" values "abcdefghijklmnopqrst"- , Test "{a:25}" values "abcdefghijklmnopqrstuvwxy"- , Test "{a:26}" values "abcdefghijklmnopqrstuvwxyz"- , Test "{a:30}" values "abcdefghijklmnopqrstuvwxyz"- ]- , let values = ["a" =: l []]- in- [ Test "{a}" values ""- , Test "{+a}" values ""- , Test "{#a}" values ""- , Test "{.a}" values ""- , Test "{/a}" values ""- , Test "{;a}" values ""- , Test "{?a}" values ""- , Test "{&a}" values ""- ]- , let values = ["a" =: d []]- in- [ Test "{a}" values ""- , Test "{+a}" values ""- , Test "{#a}" values ""- , Test "{.a}" values ""- , Test "{/a}" values ""- , Test "{;a}" values ""- , Test "{?a}" values ""- , Test "{&a}" values ""- ]- , let values = ["a" =: s ""]- in- [ Test "{a}" values ""- , Test "{+a}" values ""- , Test "{#a}" values "#"- , Test "{.a}" values "."- , Test "{/a}" values "/"- , Test "{;a}" values ";a"- , Test "{?a}" values "?a="- , Test "{&a}" values "&a="- ]- , let values = ["a" =: s "A"]- in- [ Test "{a}" values "A"- , Test "{+a}" values "A"- , Test "{#a}" values "#A"- , Test "{.a}" values ".A"- , Test "{/a}" values "/A"- , Test "{;a}" values ";a=A"- , Test "{?a}" values "?a=A"- , Test "{&a}" values "&a=A"- ]- , let values = ["b" =: s "B"]- in- [ Test "{a,b}" values "B"- , Test "{+a,b}" values "B"- , Test "{#a,b}" values "#B"- , Test "{.a,b}" values ".B"- , Test "{/a,b}" values "/B"- , Test "{;a,b}" values ";b=B"- , Test "{?a,b}" values "?b=B"- , Test "{&a,b}" values "&b=B"- ]- , let values = ["a" =: s ""]- in- [ Test "{a,a}" values ","- , Test "{+a,a}" values ","- , Test "{#a,a}" values "#,"- , Test "{.a,a}" values ".."- , Test "{/a,a}" values "//"- , Test "{;a,a}" values ";a;a"- , Test "{?a,a}" values "?a=&a="- , Test "{&a,a}" values "&a=&a="- ]- , let values = ["a" =: s "A", "b" =: s "B"]- in- [ Test "{a,b}" values "A,B"- , Test "{+a,b}" values "A,B"- , Test "{#a,b}" values "#A,B"- , Test "{.a,b}" values ".A.B"- , Test "{/a,b}" values "/A/B"- , Test "{;a,b}" values ";a=A;b=B"- , Test "{?a,b}" values "?a=A&b=B"- , Test "{&a,b}" values "&a=A&b=B"- ]- , let values = ["a" =: d ["K! \xa0\xd7ff\x10000" =: "V! \xa0\xd7ff\x10000"]]- in- [ Test "{a*}" values "K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{+a*}" values "K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{#a*}" values "#K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{.a*}" values ".K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{/a*}" values "/K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{;a*}" values ";K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{?a*}" values "?K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- , Test "{&a*}" values "&K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"- ]- , let- values =- [ "empty" =: s ""- , "hello" =: s "Hello World!"- , "keys" =: d ["semi" =: ";", "dot" =: ".", "comma" =: ","]- , "list" =: l ["red", "green", "blue"]- , "path" =: s "/foo/bar"- , "var" =: s "value"- , "x" =: s "1024"- , "y" =: s "768"- ]- in- [ Test "{var}" values "value"- , Test "{hello}" values "Hello%20World%21"- , Test "{+var}" values "value"- , Test "{+hello}" values "Hello%20World!"- , Test "{+path}/here" values "/foo/bar/here"- , Test "here?ref={+path}" values "here?ref=/foo/bar"- , Test "X{#var}" values "X#value"- , Test "X{#hello}" values "X#Hello%20World!"- , Test "map?{x,y}" values "map?1024,768"- , Test "{x,hello,y}" values "1024,Hello%20World%21,768"- , Test "{+x,hello,y}" values "1024,Hello%20World!,768"- , Test "{+path,x}/here" values "/foo/bar,1024/here"- , Test "{#x,hello,y}" values "#1024,Hello%20World!,768"- , Test "{#path,x}/here" values "#/foo/bar,1024/here"- , Test "X{.var}" values "X.value"- , Test "X{.x,y}" values "X.1024.768"- , Test "{/var}" values "/value"- , Test "{/var,x}/here" values "/value/1024/here"- , Test "{;x,y}" values ";x=1024;y=768"- , Test "{;x,y,empty}" values ";x=1024;y=768;empty"- , Test "{?x,y}" values "?x=1024&y=768"- , Test "{?x,y,empty}" values "?x=1024&y=768&empty="- , Test "?fixed=yes{&x}" values "?fixed=yes&x=1024"- , Test "{&x,y,empty}" values "&x=1024&y=768&empty="- , Test "{var:3}" values "val"- , Test "{var:30}" values "value"- , Test "{list}" values "red,green,blue"- , Test "{list*}" values "red,green,blue"- , Test "{keys}" values "comma,%2C,dot,.,semi,%3B"- , Test "{keys*}" values "comma=%2C,dot=.,semi=%3B"- , Test "{+path:6}/here" values "/foo/b/here"- , Test "{+list}" values "red,green,blue"- , Test "{+list*}" values "red,green,blue"- , Test "{+keys}" values "comma,,,dot,.,semi,;"- , Test "{+keys*}" values "comma=,,dot=.,semi=;"- , Test "{#path:6}/here" values "#/foo/b/here"- , Test "{#list}" values "#red,green,blue"- , Test "{#list*}" values "#red,green,blue"- , Test "{#keys}" values "#comma,,,dot,.,semi,;"- , Test "{#keys*}" values "#comma=,,dot=.,semi=;"- , Test "X{.var:3}" values "X.val"- , Test "X{.list}" values "X.red,green,blue"- , Test "X{.list*}" values "X.red.green.blue"- , Test "X{.keys}" values "X.comma,%2C,dot,.,semi,%3B"- , Test "X{.keys*}" values "X.comma=%2C.dot=..semi=%3B"- , Test "{/var:1,var}" values "/v/value"- , Test "{/list}" values "/red,green,blue"- , Test "{/list*}" values "/red/green/blue"- , Test "{/list*,path:4}" values "/red/green/blue/%2Ffoo"- , Test "{/keys}" values "/comma,%2C,dot,.,semi,%3B"- , Test "{/keys*}" values "/comma=%2C/dot=./semi=%3B"- , Test "{;hello:5}" values ";hello=Hello"- , Test "{;list}" values ";list=red,green,blue"- , Test "{;list*}" values ";list=red;list=green;list=blue"- , Test "{;keys}" values ";keys=comma,%2C,dot,.,semi,%3B"- , Test "{;keys*}" values ";comma=%2C;dot=.;semi=%3B"- , Test "{?var:3}" values "?var=val"- , Test "{?list}" values "?list=red,green,blue"- , Test "{?list*}" values "?list=red&list=green&list=blue"- , Test "{?keys}" values "?keys=comma,%2C,dot,.,semi,%3B"- , Test "{?keys*}" values "?comma=%2C&dot=.&semi=%3B"- , Test "{&var:3}" values "&var=val"- , Test "{&list}" values "&list=red,green,blue"- , Test "{&list*}" values "&list=red&list=green&list=blue"- , Test "{&keys}" values "&keys=comma,%2C,dot,.,semi,%3B"- , Test "{&keys*}" values "&comma=%2C&dot=.&semi=%3B"- ]- , let values = ["var" =: s "value", "semi" =: s ";"]- in- [ Test "{var}" values "value"- , Test "{var:20}" values "value"- , Test "{var:3}" values "val"- , Test "{semi}" values "%3B"- , Test "{semi:2}" values "%3B"- -- This comment forces Brittany to use a multi-line layout.- ]- , let values = ["year" =: l ["1965", "2000", "2012"], "dom" =: l ["example", "com"]]- in- [ Test "find{?year*}" values "find?year=1965&year=2000&year=2012"- , Test "www{.dom*}" values "www.example.com"- -- This comment forces Brittany to use a multi-line layout.- ]- , let- values =- [ "base" =: s "http://example.com/home/"- , "count" =: l ["one", "two", "three"]- , "dom" =: l ["example", "com"]- , "dub" =: s "me/too"- , "empty_keys" =: d []- , "empty" =: s ""- , "half" =: s "50%"- , "hello" =: s "Hello World!"- , "keys" =: d ["semi" =: ";", "dot" =: ".", "comma" =: ","]- , "list" =: l ["red", "green", "blue"]- , "path" =: s "/foo/bar"- , "v" =: s "6"- , "var" =: s "value"- , "who" =: s "fred"- , "x" =: s "1024"- , "y" =: s "768"- ]- in- [ Test "{count}" values "one,two,three"- , Test "{count*}" values "one,two,three"- , Test "{/count}" values "/one,two,three"- , Test "{/count*}" values "/one/two/three"- , Test "{;count}" values ";count=one,two,three"- , Test "{;count*}" values ";count=one;count=two;count=three"- , Test "{?count}" values "?count=one,two,three"- , Test "{?count*}" values "?count=one&count=two&count=three"- , Test "{&count*}" values "&count=one&count=two&count=three"- , Test "{var}" values "value"- , Test "{hello}" values "Hello%20World%21"- , Test "{half}" values "50%25"- , Test "O{empty}X" values "OX"- , Test "O{undef}X" values "OX"- , Test "{x,y}" values "1024,768"- , Test "{x,hello,y}" values "1024,Hello%20World%21,768"- , Test "?{x,empty}" values "?1024,"- , Test "?{x,undef}" values "?1024"- , Test "?{undef,y}" values "?768"- , Test "{var:3}" values "val"- , Test "{var:30}" values "value"- , Test "{list}" values "red,green,blue"- , Test "{list*}" values "red,green,blue"- , Test "{keys}" values "comma,%2C,dot,.,semi,%3B"- , Test "{keys*}" values "comma=%2C,dot=.,semi=%3B"- , Test "{+var}" values "value"- , Test "{+hello}" values "Hello%20World!"- , Test "{+half}" values "50%25"- , Test "{base}index" values "http%3A%2F%2Fexample.com%2Fhome%2Findex"- , Test "{+base}index" values "http://example.com/home/index"- , Test "O{+empty}X" values "OX"- , Test "O{+undef}X" values "OX"- , Test "{+path}/here" values "/foo/bar/here"- , Test "here?ref={+path}" values "here?ref=/foo/bar"- , Test "up{+path}{var}/here" values "up/foo/barvalue/here"- , Test "{+x,hello,y}" values "1024,Hello%20World!,768"- , Test "{+path,x}/here" values "/foo/bar,1024/here"- , Test "{+path:6}/here" values "/foo/b/here"- , Test "{+list}" values "red,green,blue"- , Test "{+list*}" values "red,green,blue"- , Test "{+keys}" values "comma,,,dot,.,semi,;"- , Test "{+keys*}" values "comma=,,dot=.,semi=;"- , Test "{#var}" values "#value"- , Test "{#hello}" values "#Hello%20World!"- , Test "{#half}" values "#50%25"- , Test "foo{#empty}" values "foo#"- , Test "foo{#undef}" values "foo"- , Test "{#x,hello,y}" values "#1024,Hello%20World!,768"- , Test "{#path,x}/here" values "#/foo/bar,1024/here"- , Test "{#path:6}/here" values "#/foo/b/here"- , Test "{#list}" values "#red,green,blue"- , Test "{#list*}" values "#red,green,blue"- , Test "{#keys}" values "#comma,,,dot,.,semi,;"- , Test "{#keys*}" values "#comma=,,dot=.,semi=;"- , Test "{.who}" values ".fred"- , Test "{.who,who}" values ".fred.fred"- , Test "{.half,who}" values ".50%25.fred"- , Test "www{.dom*}" values "www.example.com"- , Test "X{.var}" values "X.value"- , Test "X{.empty}" values "X."- , Test "X{.undef}" values "X"- , Test "X{.var:3}" values "X.val"- , Test "X{.list}" values "X.red,green,blue"- , Test "X{.list*}" values "X.red.green.blue"- , Test "X{.keys}" values "X.comma,%2C,dot,.,semi,%3B"- , Test "X{.keys*}" values "X.comma=%2C.dot=..semi=%3B"- , Test "X{.empty_keys}" values "X"- , Test "X{.empty_keys*}" values "X"- , Test "{/who}" values "/fred"- , Test "{/who,who}" values "/fred/fred"- , Test "{/half,who}" values "/50%25/fred"- , Test "{/who,dub}" values "/fred/me%2Ftoo"- , Test "{/var}" values "/value"- , Test "{/var,empty}" values "/value/"- , Test "{/var,undef}" values "/value"- , Test "{/var,x}/here" values "/value/1024/here"- , Test "{/var:1,var}" values "/v/value"- , Test "{/list}" values "/red,green,blue"- , Test "{/list*}" values "/red/green/blue"- , Test "{/list*,path:4}" values "/red/green/blue/%2Ffoo"- , Test "{/keys}" values "/comma,%2C,dot,.,semi,%3B"- , Test "{/keys*}" values "/comma=%2C/dot=./semi=%3B"- , Test "{;who}" values ";who=fred"- , Test "{;half}" values ";half=50%25"- , Test "{;empty}" values ";empty"- , Test "{;v,empty,who}" values ";v=6;empty;who=fred"- , Test "{;v,bar,who}" values ";v=6;who=fred"- , Test "{;x,y}" values ";x=1024;y=768"- , Test "{;x,y,empty}" values ";x=1024;y=768;empty"- , Test "{;x,y,undef}" values ";x=1024;y=768"- , Test "{;hello:5}" values ";hello=Hello"- , Test "{;list}" values ";list=red,green,blue"- , Test "{;list*}" values ";list=red;list=green;list=blue"- , Test "{;keys}" values ";keys=comma,%2C,dot,.,semi,%3B"- , Test "{;keys*}" values ";comma=%2C;dot=.;semi=%3B"- , Test "{?who}" values "?who=fred"- , Test "{?half}" values "?half=50%25"- , Test "{?x,y}" values "?x=1024&y=768"- , Test "{?x,y,empty}" values "?x=1024&y=768&empty="- , Test "{?x,y,undef}" values "?x=1024&y=768"- , Test "{?var:3}" values "?var=val"- , Test "{?list}" values "?list=red,green,blue"- , Test "{?list*}" values "?list=red&list=green&list=blue"- , Test "{?keys}" values "?keys=comma,%2C,dot,.,semi,%3B"- , Test "{?keys*}" values "?comma=%2C&dot=.&semi=%3B"- , Test "{&who}" values "&who=fred"- , Test "{&half}" values "&half=50%25"- , Test "?fixed=yes{&x}" values "?fixed=yes&x=1024"- , Test "{&x,y,empty}" values "&x=1024&y=768&empty="- , Test "{&x,y,undef}" values "&x=1024&y=768"- , Test "{&var:3}" values "&var=val"- , Test "{&list}" values "&list=red,green,blue"- , Test "{&list*}" values "&list=red&list=green&list=blue"- , Test "{&keys}" values "&keys=comma,%2C,dot,.,semi,%3B"- , Test "{&keys*}" values "&comma=%2C&dot=.&semi=%3B"- ]- ]--data Test = Test- { testInput :: String- , testValues :: [(String, Burrito.Value)]- , testOutput :: Output- }- deriving (Eq, Show)--runTest :: Test -> Hspec.Expectation-runTest test =- case (Burrito.parse $ testInput test, unwrapOutput $ testOutput test) of- (Nothing, Nothing) -> pure ()- (Nothing, Just _) -> Hspec.expectationFailure "should have parsed"- (Just _, Nothing) -> Hspec.expectationFailure "should not have parsed"- (Just template, Just expected) -> do- let- values = testValues test- actual = Burrito.expand values template- actual `Hspec.shouldBe` expected- Burrito.parse (show template) `Hspec.shouldBe` Just template- let- relevant =- List.sort $ keepRelevant (templateVariables template) values- Monad.when (isMatchable template relevant) $ do- let matches = List.sort <$> Burrito.match expected template- matches `Hspec.shouldSatisfy` elem relevant--isMatchable :: Template.Template -> [(String, Burrito.Value)] -> Bool-isMatchable template values =- (not . any (isAsterisk . Variable.modifier) $ templateVariables template)- && all (isString . snd) values--isString :: Burrito.Value -> Bool-isString value = case value of- Value.String _ -> True- _ -> False--isAsterisk :: Modifier.Modifier -> Bool-isAsterisk modifier = case modifier of- Modifier.Asterisk -> True- _ -> False--keepRelevant- :: Set.Set Variable.Variable- -> [(String, Burrito.Value)]- -> [(String, Burrito.Value)]-keepRelevant variables =- let- vs =- Map.fromListWith- (\mx my -> case (mx, my) of- (Just x, Just y) -> Just $ max x y- _ -> Nothing- )- . fmap- (\v ->- ( Render.builderToString . Render.name $ Variable.name v- , case Variable.modifier v of- Modifier.Colon n -> Just $ MaxLength.count n- _ -> Nothing- )- )- $ Set.toList variables- in- Maybe.mapMaybe $ \(k, v) -> do- m <- Map.lookup k vs- pure . (,) k $ case m of- Nothing -> v- Just n -> case v of- Value.String t -> Value.String $ Text.take n t- _ -> v--templateVariables :: Template.Template -> Set.Set Variable.Variable-templateVariables =- Set.fromList- . concatMap- (\token -> case token of- Token.Expression expression ->- NonEmpty.toList $ Expression.variables expression- Token.Literal _ -> []- )- . Template.tokens--newtype Output = Output- { unwrapOutput :: Maybe String- } deriving (Eq, Show)--instance String.IsString Output where- fromString = Output . Just--noParse :: Output-noParse = Output Nothing--(=:) :: a -> b -> (a, b)-(=:) = (,)--s :: String -> Burrito.Value-s = Burrito.stringValue--l :: [String] -> Burrito.Value-l = Burrito.listValue--d :: [(String, String)] -> Burrito.Value-d = Burrito.dictionaryValue--newtype Template = Template- { unwrapTemplate :: Template.Template- } deriving (Eq)--instance Show Template where- show (Template template) =- unwords [show $ show template, "{-", show (Template.tokens template), "-}"]--instance QC.Arbitrary Template where- arbitrary = Template <$> arbitraryTemplate- shrink = fmap Template . shrinkTemplate . unwrapTemplate--type Shrink a = a -> [a]--arbitraryTemplate :: QC.Gen Template.Template-arbitraryTemplate = Template.Template . simplify <$> QC.listOf arbitraryToken--shrinkTemplate :: Shrink Template.Template-shrinkTemplate =- fmap (Template.Template . simplify)- . QC.shrinkList shrinkToken- . Template.tokens--simplify :: [Token.Token] -> [Token.Token]-simplify tokens = case tokens of- t1 : t2 : ts -> case (t1, t2) of- (Token.Literal l1, Token.Literal l2) ->- simplify $ Token.Literal (appendLiteral l1 l2) : ts- _ -> t1 : simplify (t2 : ts)- _ -> tokens--appendLiteral :: Literal.Literal -> Literal.Literal -> Literal.Literal-appendLiteral x y =- Literal.Literal $ Literal.characters x <> Literal.characters y--arbitraryToken :: QC.Gen Token.Token-arbitraryToken = QC.oneof- [ Token.Expression <$> arbitraryExpression- , Token.Literal <$> arbitraryLiteral- ]--shrinkToken :: Shrink Token.Token-shrinkToken x = case x of- Token.Expression y -> Token.Expression <$> shrinkExpression y- Token.Literal y -> Token.Literal <$> shrinkLiteral y--arbitraryExpression :: QC.Gen Expression.Expression-arbitraryExpression =- Expression.Expression- <$> arbitraryOperator- <*> arbitraryNonEmpty arbitraryVariable--shrinkExpression :: Shrink Expression.Expression-shrinkExpression x = uncurry Expression.Expression <$> shrinkTuple- shrinkOperator- (shrinkNonEmpty shrinkVariable)- (Expression.operator x, Expression.variables x)--arbitraryOperator :: QC.Gen Operator.Operator-arbitraryOperator = QC.elements- [ Operator.Ampersand- , Operator.FullStop- , Operator.None- , Operator.NumberSign- , Operator.PlusSign- , Operator.QuestionMark- , Operator.Semicolon- , Operator.Solidus- ]--shrinkOperator :: Shrink Operator.Operator-shrinkOperator x = case x of- Operator.None -> []- _ -> [Operator.None]--arbitraryNonEmpty :: QC.Gen a -> QC.Gen (NonEmpty.NonEmpty a)-arbitraryNonEmpty g = (NonEmpty.:|) <$> g <*> QC.listOf g--shrinkNonEmpty :: Shrink a -> Shrink (NonEmpty.NonEmpty a)-shrinkNonEmpty f x = uncurry (NonEmpty.:|)- <$> shrinkTuple f (QC.shrinkList f) (NonEmpty.head x, NonEmpty.tail x)--arbitraryVariable :: QC.Gen Variable.Variable-arbitraryVariable = Variable.Variable <$> arbitraryName <*> arbitraryModifier--shrinkVariable :: Shrink Variable.Variable-shrinkVariable variable = uncurry Variable.Variable <$> shrinkTuple- shrinkName- shrinkModifier- (Variable.name variable, Variable.modifier variable)--shrinkTuple :: Shrink a -> Shrink b -> Shrink (a, b)-shrinkTuple f g (x, y) =- fmap (\a -> (a, y)) (f x) <> fmap (\b -> (x, b)) (g y)--arbitraryName :: QC.Gen Name.Name-arbitraryName = Name.Name <$> arbitraryNonEmpty arbitraryField--shrinkName :: Shrink Name.Name-shrinkName = fmap Name.Name . shrinkNonEmpty shrinkField . Name.fields--arbitraryField :: QC.Gen Field.Field-arbitraryField = Field.Field <$> arbitraryNonEmpty arbitraryFieldCharacter--shrinkField :: Shrink Field.Field-shrinkField =- fmap Field.Field . shrinkNonEmpty shrinkFieldCharacter . Field.characters--arbitraryFieldCharacter :: QC.Gen (Character.Character Field.Field)-arbitraryFieldCharacter = QC.oneof- [ Character.Encoded <$> arbitraryDigit <*> arbitraryDigit- , Character.Unencoded <$> QC.suchThat QC.arbitrary Parse.isFieldCharacter- ]--shrinkFieldCharacter :: Shrink (Character.Character Field.Field)-shrinkFieldCharacter x = case x of- Character.Encoded y z ->- uncurry Character.Encoded <$> shrinkTuple shrinkDigit shrinkDigit (y, z)- Character.Unencoded y ->- fmap Character.Unencoded . filter Parse.isFieldCharacter $ QC.shrink y--arbitraryDigit :: QC.Gen Digit.Digit-arbitraryDigit = QC.oneof- [ pure Digit.Ox0- , pure Digit.Ox1- , pure Digit.Ox2- , pure Digit.Ox3- , pure Digit.Ox4- , pure Digit.Ox5- , pure Digit.Ox6- , pure Digit.Ox7- , pure Digit.Ox8- , pure Digit.Ox9- , Digit.OxA <$> arbitraryCase- , Digit.OxB <$> arbitraryCase- , Digit.OxC <$> arbitraryCase- , Digit.OxD <$> arbitraryCase- , Digit.OxE <$> arbitraryCase- , Digit.OxF <$> arbitraryCase- ]--shrinkDigit :: Shrink Digit.Digit-shrinkDigit x = case x of- Digit.Ox0 -> []- Digit.Ox1 -> [Digit.Ox0]- Digit.Ox2 -> [Digit.Ox0]- Digit.Ox3 -> [Digit.Ox0]- Digit.Ox4 -> [Digit.Ox0]- Digit.Ox5 -> [Digit.Ox0]- Digit.Ox6 -> [Digit.Ox0]- Digit.Ox7 -> [Digit.Ox0]- Digit.Ox8 -> [Digit.Ox0]- Digit.Ox9 -> [Digit.Ox0]- Digit.OxA y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)- Digit.OxB y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)- Digit.OxC y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)- Digit.OxD y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)- Digit.OxE y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)- Digit.OxF y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)--arbitraryCase :: QC.Gen Case.Case-arbitraryCase = QC.elements [Case.Lower, Case.Upper]--shrinkCase :: Shrink Case.Case-shrinkCase x = case x of- Case.Lower -> []- Case.Upper -> [Case.Lower]--arbitraryModifier :: QC.Gen Modifier.Modifier-arbitraryModifier = QC.oneof- [ pure Modifier.Asterisk- , Modifier.Colon <$> arbitraryMaxLength- , pure Modifier.None- ]--shrinkModifier :: Shrink Modifier.Modifier-shrinkModifier x = case x of- Modifier.Asterisk -> [Modifier.None]- Modifier.Colon y -> Modifier.None : fmap Modifier.Colon (shrinkMaxLength y)- Modifier.None -> []--arbitraryMaxLength :: QC.Gen MaxLength.MaxLength-arbitraryMaxLength =- MaxLength.MaxLength <$> QC.suchThat QC.arbitrary Parse.isMaxLength--shrinkMaxLength :: Shrink MaxLength.MaxLength-shrinkMaxLength =- fmap MaxLength.MaxLength- . filter Parse.isMaxLength- . QC.shrink- . MaxLength.count--arbitraryLiteral :: QC.Gen Literal.Literal-arbitraryLiteral =- Literal.Literal <$> arbitraryNonEmpty arbitraryLiteralCharacter--shrinkLiteral :: Shrink Literal.Literal-shrinkLiteral =- fmap Literal.Literal- . shrinkNonEmpty shrinkLiteralCharacter- . Literal.characters--arbitraryLiteralCharacter :: QC.Gen (Character.Character Literal.Literal)-arbitraryLiteralCharacter = QC.oneof- [ Character.Encoded <$> arbitraryDigit <*> arbitraryDigit- , Character.Unencoded <$> QC.suchThat QC.arbitrary Parse.isLiteralCharacter- ]+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE QuasiQuotes #-}+{-# OPTIONS_GHC -Wno-monomorphism-restriction #-}++module Main+ ( main,+ )+where++import qualified Burrito+import qualified Burrito.Internal.Parse as Parse+import qualified Burrito.Internal.Render as Render+import qualified Burrito.Internal.Type.Case as Case+import qualified Burrito.Internal.Type.Character as Character+import qualified Burrito.Internal.Type.Digit as Digit+import qualified Burrito.Internal.Type.Expression as Expression+import qualified Burrito.Internal.Type.Field as Field+import qualified Burrito.Internal.Type.Literal as Literal+import qualified Burrito.Internal.Type.MaxLength as MaxLength+import qualified Burrito.Internal.Type.Modifier as Modifier+import qualified Burrito.Internal.Type.Name as Name+import qualified Burrito.Internal.Type.Operator as Operator+import qualified Burrito.Internal.Type.Template as Template+import qualified Burrito.Internal.Type.Token as Token+import qualified Burrito.Internal.Type.Value as Value+import qualified Burrito.Internal.Type.Variable as Variable+import qualified Control.Monad as Monad+import qualified Data.List as List+import qualified Data.List.NonEmpty as NonEmpty+import qualified Data.Map as Map+import qualified Data.Maybe as Maybe+import qualified Data.Set as Set+import qualified Data.String as String+import qualified Data.Text as Text+import qualified GHC.Stack as Stack+import qualified Test.Hspec as Hspec+import qualified Test.Hspec.QuickCheck as Hspec+import qualified Test.QuickCheck as QC++main :: IO ()+main = Hspec.hspec . Hspec.describe "Burrito" $ do+ Monad.forM_ tests $ \test ->+ Hspec.it (show (testInput test, unwrapOutput $ testOutput test)) $+ runTest test++ Hspec.describe "match" $ do+ let matchTest ::+ (Stack.HasCallStack) =>+ String ->+ [(String, Burrito.Value)] ->+ String ->+ Hspec.Spec+ matchTest input values output = Hspec.it (show (input, output)) $ do+ template <- maybe (fail "invalid Template") pure $ Burrito.parse input+ let matches = Burrito.match output template+ matches `Hspec.shouldSatisfy` elem values+ Monad.forM_ matches $ \match -> do+ let it = Burrito.expand match template+ Monad.when (it /= output) . fail $+ show+ (input, values, output, matches, match, it)++ matchTest "" [] ""+ matchTest "a" [] "a"+ matchTest "!" [] "!"+ matchTest "\xa0" [] "%C2%A0"++ matchTest "{a}" ["a" =: s ""] ""+ matchTest "{a}" ["a" =: s "A"] "A"+ matchTest "{a}" ["a" =: s "AB"] "AB"++ matchTest "a{b}" ["b" =: s ""] "a"+ matchTest "a{b}" ["b" =: s "B"] "aB"+ matchTest "a{b}" ["b" =: s "BC"] "aBC"++ matchTest "{a}b" ["a" =: s ""] "b"+ matchTest "{a}b" ["a" =: s "A"] "Ab"+ matchTest "{a}b" ["a" =: s "AB"] "ABb"++ matchTest "{a}{a}" ["a" =: s "A"] "AA"+ matchTest "{a}{a}" ["a" =: s "AB"] "ABAB"++ matchTest "{a}" ["a" =: s "%"] "%25"+ matchTest "{a}" ["a" =: s "/"] "%2F"+ matchTest "{a}" ["a" =: s "\xa0"] "%C2%A0"+ matchTest "{a}" ["a" =: s "\xd7ff"] "%ED%9F%BF"+ matchTest "{a}" ["a" =: s "\x10000"] "%F0%90%80%80"+ matchTest "{a}" ["a" =: s "A/B"] "A%2FB"+ matchTest "{a}" ["a" =: s "WX\xa0\xa1YZ"] "WX%C2%A0%C2%A1YZ"++ matchTest "{+a}" ["a" =: s "%"] "%25"+ matchTest "{+a}" ["a" =: s "/"] "/"++ matchTest "{#a}" [] ""+ matchTest "{#a}" ["a" =: s ""] "#"+ matchTest "{#a}" ["a" =: s "A"] "#A"+ matchTest "{#a}" ["a" =: s "%"] "#%25"+ matchTest "{#a}" ["a" =: s "/"] "#/"++ matchTest "{.a}" [] ""+ matchTest "{.a}" ["a" =: s ""] "."+ matchTest "{.a}" ["a" =: s "A"] ".A"+ matchTest "{.a}" ["a" =: s "%"] ".%25"+ matchTest "{.a}" ["a" =: s "/"] ".%2F"++ matchTest "{/a}" [] ""+ matchTest "{/a}" ["a" =: s ""] "/"+ matchTest "{/a}" ["a" =: s "A"] "/A"+ matchTest "{/a}" ["a" =: s "%"] "/%25"+ matchTest "{/a}" ["a" =: s "/"] "/%2F"++ matchTest "{;a}" [] ""+ matchTest "{;a}" ["a" =: s ""] ";a"+ matchTest "{;a}" ["a" =: s "A"] ";a=A"+ matchTest "{;a}" ["a" =: s "%"] ";a=%25"+ matchTest "{;a}" ["a" =: s "/"] ";a=%2F"++ matchTest "{?a}" [] ""+ matchTest "{?a}" ["a" =: s ""] "?a="+ matchTest "{?a}" ["a" =: s "A"] "?a=A"+ matchTest "{?a}" ["a" =: s "%"] "?a=%25"+ matchTest "{?a}" ["a" =: s "/"] "?a=%2F"++ matchTest "{&a}" [] ""+ matchTest "{&a}" ["a" =: s ""] "&a="+ matchTest "{&a}" ["a" =: s "A"] "&a=A"+ matchTest "{&a}" ["a" =: s "%"] "&a=%25"+ matchTest "{&a}" ["a" =: s "/"] "&a=%2F"++ matchTest "{a,b}" ["a" =: s "A", "b" =: s "B"] "A,B"+ matchTest "{+a,b}" ["a" =: s "A", "b" =: s "B"] "A,B"+ matchTest "{#a,b}" ["a" =: s "A", "b" =: s "B"] "#A,B"+ matchTest "{.a,b}" ["a" =: s "A", "b" =: s "B"] ".A.B"+ matchTest "{/a,b}" ["a" =: s "A", "b" =: s "B"] "/A/B"+ matchTest "{;a,b}" ["a" =: s "A", "b" =: s "B"] ";a=A;b=B"+ matchTest "{?a,b}" ["a" =: s "A", "b" =: s "B"] "?a=A&b=B"+ matchTest "{&a,b}" ["a" =: s "A", "b" =: s "B"] "&a=A&b=B"++ matchTest "{a,b}" ["a" =: s "A"] "A"+ matchTest "{a,b,c}" ["a" =: s "A"] "A"+ matchTest "{a,b,c}" ["a" =: s "A", "b" =: s "B"] "A,B"+ matchTest "{a}{a,b}" ["b" =: s "B"] "B"+ matchTest "{a}{a,b,c}" ["b" =: s "B"] "B"+ matchTest "{a,b}{a,b,c}" ["c" =: s "C"] "C"+ matchTest "{b}{a,b,c}" ["a" =: s "A", "c" =: s "C"] "A,C"+ matchTest "{a}{a,b,c}" ["b" =: s "B", "c" =: s "C"] "B,C"++ matchTest "{a:1}/{a}" ["a" =: s "AB"] "A/AB"++ -- TODO: Test matching on explode modifier.+ -- matchTest "{a*}" ["a" =: s "A"] "A"++ -- TODO: Test matching on lists.+ -- matchTest "{a}" ["a" =: l ["A", "B"]] "A,B"++ -- TODO: Test matching on dictionaries.+ -- matchTest "{a}" ["a" =: d ["k" =: "v"]] "k,v"++ Hspec.describe "uriTemplate"+ . Hspec.it "works as an expression"+ $ Just [Burrito.uriTemplate|a{b}c|]+ `Hspec.shouldBe` Burrito.parse "a{b}c"++ Hspec.modifyMaxSize (const 10)+ . Hspec.it "round trips"+ . QC.property+ $ \(Template template) -> Burrito.parse (show template) == Just template++-- brittany-next-binding --columns 160+tests :: [Test]+tests =+ mconcat+ [ [ Test "" [] "",+ Test "" ["a" =: s ""] "",+ Test "" ["a" =: l []] "",+ Test "" ["a" =: d []] "",+ Test "!" [] "!",+ Test "#" [] "#",+ Test "$" [] "$",+ Test "&" [] "&",+ Test "(" [] "(",+ Test ")" [] ")",+ Test "*" [] "*",+ Test "+" [] "+",+ Test "," [] ",",+ Test "-" [] "-",+ Test "." [] ".",+ Test "/" [] "/",+ Test "0" [] "0",+ Test "9" [] "9",+ Test ":" [] ":",+ Test ";" [] ";",+ Test "=" [] "=",+ Test "?" [] "?",+ Test "@" [] "@",+ Test "A" [] "A",+ Test "Z" [] "Z",+ Test "[" [] "[",+ Test "]" [] "]",+ Test "_" [] "_",+ Test "a" [] "a",+ Test "z" [] "z",+ Test "~" [] "~",+ Test "\xa0" [] "%C2%A0",+ Test "\xd7ff" [] "%ED%9F%BF",+ Test "\xf900" [] "%EF%A4%80",+ Test "\xfdcf" [] "%EF%B7%8F",+ Test "\xfdf0" [] "%EF%B7%B0",+ Test "\xffef" [] "%EF%BF%AF",+ Test "\x10000" [] "%F0%90%80%80",+ Test "\x1fffd" [] "%F0%9F%BF%BD",+ Test "\x20000" [] "%F0%A0%80%80",+ Test "\x2fffd" [] "%F0%AF%BF%BD",+ Test "\x30000" [] "%F0%B0%80%80",+ Test "\x3fffd" [] "%F0%BF%BF%BD",+ Test "\x40000" [] "%F1%80%80%80",+ Test "\x4fffd" [] "%F1%8F%BF%BD",+ Test "\x50000" [] "%F1%90%80%80",+ Test "\x5fffd" [] "%F1%9F%BF%BD",+ Test "\x60000" [] "%F1%A0%80%80",+ Test "\x6fffd" [] "%F1%AF%BF%BD",+ Test "\x70000" [] "%F1%B0%80%80",+ Test "\x7fffd" [] "%F1%BF%BF%BD",+ Test "\x80000" [] "%F2%80%80%80",+ Test "\x8fffd" [] "%F2%8F%BF%BD",+ Test "\x90000" [] "%F2%90%80%80",+ Test "\x9fffd" [] "%F2%9F%BF%BD",+ Test "\xa0000" [] "%F2%A0%80%80",+ Test "\xafffd" [] "%F2%AF%BF%BD",+ Test "\xb0000" [] "%F2%B0%80%80",+ Test "\xbfffd" [] "%F2%BF%BF%BD",+ Test "\xc0000" [] "%F3%80%80%80",+ Test "\xcfffd" [] "%F3%8F%BF%BD",+ Test "\xd0000" [] "%F3%90%80%80",+ Test "\xdfffd" [] "%F3%9F%BF%BD",+ Test "\xe1000" [] "%F3%A1%80%80",+ Test "\xefffd" [] "%F3%AF%BF%BD",+ Test "\xe000" [] "%EE%80%80",+ Test "\xf8ff" [] "%EF%A3%BF",+ Test "\xf0000" [] "%F3%B0%80%80",+ Test "\xffffd" [] "%F3%BF%BF%BD",+ Test "\x100000" [] "%F4%80%80%80",+ Test "\x10fffd" [] "%F4%8F%BF%BD",+ Test "%00" [] "%00",+ Test "%AA" [] "%AA",+ Test "%Aa" [] "%Aa",+ Test "%aA" [] "%aA",+ Test "%aa" [] "%aa",+ Test "%" [] noParse,+ Test "%0" [] noParse,+ Test "%0z" [] noParse,+ Test "%z" [] noParse,+ Test "%30" [] "%30",+ Test " " [] noParse,+ Test "\"" [] noParse,+ Test "'" [] noParse,+ Test "%" [] noParse,+ Test "<" [] noParse,+ Test ">" [] noParse,+ Test "\\" [] noParse,+ Test "^" [] noParse,+ Test "`" [] noParse,+ Test "{" [] noParse,+ Test "|" [] noParse,+ Test "}" [] noParse,+ Test "{}" [] noParse,+ Test "{,}" [] noParse,+ Test "{a,,b}" [] noParse,+ Test "{+}" [] noParse,+ Test "{:1}" [] noParse,+ Test "{*}" [] noParse,+ Test "{AZ}" [] "",+ Test "{az}" [] "",+ Test "{09}" [] "",+ Test "{_a}" [] "",+ Test "{a_}" [] "",+ Test "{_}" [] "",+ Test "{A.A}" [] "",+ Test "{a.a}" [] "",+ Test "{0.0}" [] "",+ Test "{_._}" [] "",+ Test "{%aa.%aa}" [] "",+ Test "{.}" [] noParse,+ Test "{a.}" [] noParse,+ Test "{+.a}" [] noParse,+ Test "{a..b}" [] noParse,+ Test "{%00}" [] "",+ Test "{%}" [] noParse,+ Test "{%0}" [] noParse,+ Test "{%0z}" [] noParse,+ Test "{%z}" [] noParse,+ Test "{!}" [] noParse,+ Test "{" [] noParse,+ Test "{{}" [] noParse,+ Test "}" [] noParse,+ Test "{}}" [] noParse,+ Test "{a,b}" [] "",+ Test "{a,b,c,d}" [] "",+ Test "{a,a}" [] "",+ Test "{a:5}" [] "",+ Test "{a:67}" [] "",+ Test "{a:801}" [] "",+ Test "{a:234}" [] "",+ Test "{a:9999}" [] "",+ Test "{a:123}" ["a" =: s (replicate 200 'a')] . Output . Just $ replicate 123 'a',+ Test "{a:}" [] noParse,+ Test "{a:0}" [] noParse,+ Test "{a:10000}" [] noParse,+ Test "{a:-1}" [] noParse,+ Test "{a*}" [] "",+ Test "{a:1*}" [] noParse,+ Test "{a*:1}" [] noParse,+ Test "{a,b:1,c*}" [] "",+ Test "{+a}" [] "",+ Test "{#a}" [] "",+ Test "{.a}" [] "",+ Test "{/a}" [] "",+ Test "{;a}" [] "",+ Test "{?a}" [] "",+ Test "{&a}" [] "",+ Test "{=a}" [] noParse,+ Test "{,a}" [] noParse,+ Test "{!a}" [] noParse,+ Test "{@a}" [] noParse,+ Test "{|a}" [] noParse,+ Test "{+#a}" [] noParse,+ Test "{+a,#b}" [] noParse,+ Test "{+a:1}" [] "",+ Test "{#a*}" [] "",+ Test "{+a,b}" [] "",+ Test "{#a,b}" [] "",+ Test "{.a,b}" [] "",+ Test "{/a,b}" [] "",+ Test "{;a,b}" [] "",+ Test "{?a,b}" [] "",+ Test "{&a,b}" [] "",+ Test "{a}{b}" [] "",+ Test "{a}{b}{c}{d}" [] "",+ Test "{a}{a}" [] "",+ Test "{{}}" [] noParse,+ Test "{a{b}}" [] noParse,+ Test "{{a}b}" [] noParse,+ Test "{a{b}c}" [] noParse,+ Test "a{b}" [] "a",+ Test "{a}b" [] "b",+ Test "a{b}c" [] "ac",+ Test "{a}b{c}" [] "b",+ Test "{a}" [] "",+ Test "{+a}" [] "",+ Test "{#a}" [] "",+ Test "{.a}" [] "",+ Test "{/a}" [] "",+ Test "{;a}" [] "",+ Test "{?a}" [] "",+ Test "{&a}" [] "",+ Test "http://example.com/~{username}/" ["username" =: s "fred"] "http://example.com/~fred/",+ Test "http://example.com/~{username}/" ["username" =: s "mark"] "http://example.com/~mark/",+ Test "http://example.com/dictionary/{term:1}/{term}" ["term" =: s "cat"] "http://example.com/dictionary/c/cat",+ Test "http://example.com/dictionary/{term:1}/{term}" ["term" =: s "dog"] "http://example.com/dictionary/d/dog",+ Test "http://example.com/search{?q,lang}" ["q" =: s "cat", "lang" =: s "en"] "http://example.com/search?q=cat&lang=en",+ Test "http://example.com/search{?q,lang}" ["q" =: s "chien", "lang" =: s "fr"] "http://example.com/search?q=chien&lang=fr",+ Test "http://www.example.com/foo{?query,number}" ["query" =: s "mycelium", "number" =: s "100"] "http://www.example.com/foo?query=mycelium&number=100",+ Test "http://www.example.com/foo{?query,number}" ["number" =: s "100"] "http://www.example.com/foo?number=100",+ Test "http://www.example.com/foo{?query,number}" [] "http://www.example.com/foo",+ Test "{a}" [] "",+ Test "{a}" ["a" =: l []] "",+ Test "{a}" ["a" =: d []] "",+ Test "{a}" ["a" =: s ""] "",+ Test "{a}" ["a" =: s "A"] "A",+ Test "{a}" ["a" =: s "~"] "~",+ Test "{a}" ["a" =: s "%"] "%25",+ Test "{a}" ["a" =: s "?"] "%3F",+ Test "{a}" ["a" =: s "&"] "%26",+ Test "{a}" ["a" =: s "\xa0"] "%C2%A0",+ Test "{a}" ["a" =: s "\xd7ff"] "%ED%9F%BF",+ Test "{a}" ["a" =: s "\x10000"] "%F0%90%80%80",+ Test "{a}" ["a" =: l ["A"]] "A",+ Test "{a}" ["a" =: l ["A", "B"]] "A,B",+ Test "{a}" ["a" =: l ["%"]] "%25",+ Test "{a}" ["a" =: l ["\xa0"]] "%C2%A0",+ Test "{a}" ["a" =: d ["A" =: "1"]] "A,1",+ Test "{a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2",+ Test "{a}" ["a" =: d ["A" =: "%"]] "A,%25",+ Test "{a}" ["a" =: d ["A" =: "\xa0"]] "A,%C2%A0",+ Test "{a}" ["a" =: d ["%" =: "1"]] "%25,1",+ Test "{a*}" [] "",+ Test "{a*}" ["a" =: s ""] "",+ Test "{a*}" ["a" =: s "A"] "A",+ Test "{a*}" ["a" =: l []] "",+ Test "{a*}" ["a" =: l ["A"]] "A",+ Test "{a*}" ["a" =: l ["A", "B"]] "A,B",+ Test "{a*}" ["a" =: d []] "",+ Test "{a*}" ["a" =: d ["A" =: "1"]] "A=1",+ Test "{a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2",+ Test "{a:1}" [] "",+ Test "{a:1}" ["a" =: s ""] "",+ Test "{a:1}" ["a" =: s "A"] "A",+ Test "{a:1}" ["a" =: s "AB"] "A",+ Test "{a:1}" ["a" =: s "%B"] "%25",+ Test "{a:1}" ["a" =: s "\xa0\&B"] "%C2%A0",+ Test "{a:1}" ["a" =: s "\xd7ff\&B"] "%ED%9F%BF",+ Test "{a:1}" ["a" =: s "\x10000\&B"] "%F0%90%80%80",+ Test "{a:1}" ["a" =: l []] "",+ Test "{a:1}" ["a" =: l ["AB"]] "AB",+ Test "{a:1}" ["a" =: l ["AB", "CD"]] "AB,CD",+ Test "{a:1}" ["a" =: d []] "",+ Test "{a:1}" ["a" =: d ["AB" =: "12"]] "AB,12",+ Test "{a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34",+ Test "{a,a}" [] "",+ Test "{a,a}" ["a" =: l []] "",+ Test "{a,a}" ["a" =: d []] "",+ Test "{a,a}" ["a" =: s ""] ",",+ Test "{a,b}" ["a" =: s ""] "",+ Test "{a,b}" ["b" =: s ""] "",+ Test "{%aa}" ["%aa" =: s "A"] "A",+ Test "{%aa}" ["%aa" =: l ["A", "B"]] "A,B",+ Test "{%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2",+ Test "{%aa*}" ["%aa" =: s "A"] "A",+ Test "{%aa*}" ["%aa" =: l ["A", "B"]] "A,B",+ Test "{%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2",+ Test "{+a}" [] "",+ Test "{+a}" ["a" =: l []] "",+ Test "{+a}" ["a" =: d []] "",+ Test "{+a}" ["a" =: s ""] "",+ Test "{+a}" ["a" =: s "A"] "A",+ Test "{+a}" ["a" =: s "~"] "~",+ Test "{+a}" ["a" =: s "%"] "%25",+ Test "{+a}" ["a" =: s "?"] "?",+ Test "{+a}" ["a" =: s "&"] "&",+ Test "{+a}" ["a" =: s "\xa0"] "%C2%A0",+ Test "{+a}" ["a" =: s "\xd7ff"] "%ED%9F%BF",+ Test "{+a}" ["a" =: s "\x10000"] "%F0%90%80%80",+ Test "{+a}" ["a" =: l ["A"]] "A",+ Test "{+a}" ["a" =: l ["A", "B"]] "A,B",+ Test "{+a}" ["a" =: l ["%"]] "%25",+ Test "{+a}" ["a" =: l ["\xa0"]] "%C2%A0",+ Test "{+a}" ["a" =: d ["A" =: "1"]] "A,1",+ Test "{+a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2",+ Test "{+a}" ["a" =: d ["A" =: "%"]] "A,%25",+ Test "{+a}" ["a" =: d ["A" =: "\xa0"]] "A,%C2%A0",+ Test "{+a}" ["a" =: d ["%" =: "1"]] "%25,1",+ Test "{+a*}" [] "",+ Test "{+a*}" ["a" =: s ""] "",+ Test "{+a*}" ["a" =: s "A"] "A",+ Test "{+a*}" ["a" =: l []] "",+ Test "{+a*}" ["a" =: l ["A"]] "A",+ Test "{+a*}" ["a" =: l ["A", "B"]] "A,B",+ Test "{+a*}" ["a" =: d []] "",+ Test "{+a*}" ["a" =: d ["A" =: "1"]] "A=1",+ Test "{+a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2",+ Test "{+a:1}" [] "",+ Test "{+a:1}" ["a" =: s ""] "",+ Test "{+a:1}" ["a" =: s "A"] "A",+ Test "{+a:1}" ["a" =: s "AB"] "A",+ Test "{+a:1}" ["a" =: s "%B"] "%25",+ Test "{+a:1}" ["a" =: s "\xa0\&B"] "%C2%A0",+ Test "{+a:1}" ["a" =: s "\xd7ff\&B"] "%ED%9F%BF",+ Test "{+a:1}" ["a" =: s "\x10000\&B"] "%F0%90%80%80",+ Test "{+a:1}" ["a" =: l []] "",+ Test "{+a:1}" ["a" =: l ["AB"]] "AB",+ Test "{+a:1}" ["a" =: l ["AB", "CD"]] "AB,CD",+ Test "{+a:1}" ["a" =: d []] "",+ Test "{+a:1}" ["a" =: d ["AB" =: "12"]] "AB,12",+ Test "{+a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "AB,12,CD,34",+ Test "{+a,a}" [] "",+ Test "{+a,a}" ["a" =: l []] "",+ Test "{+a,a}" ["a" =: d []] "",+ Test "{+a,a}" ["a" =: s ""] ",",+ Test "{+a,b}" ["a" =: s ""] "",+ Test "{+a,b}" ["b" =: s ""] "",+ Test "{+%aa}" ["%aa" =: s "A"] "A",+ Test "{+%aa}" ["%aa" =: l ["A", "B"]] "A,B",+ Test "{+%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A,1,B,2",+ Test "{+%aa*}" ["%aa" =: s "A"] "A",+ Test "{+%aa*}" ["%aa" =: l ["A", "B"]] "A,B",+ Test "{+%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "A=1,B=2",+ Test "{#a}" [] "",+ Test "{#a}" ["a" =: l []] "",+ Test "{#a}" ["a" =: d []] "",+ Test "{#a}" ["a" =: s ""] "#",+ Test "{#a}" ["a" =: s "A"] "#A",+ Test "{#a}" ["a" =: s "~"] "#~",+ Test "{#a}" ["a" =: s "%"] "#%25",+ Test "{#a}" ["a" =: s "?"] "#?",+ Test "{#a}" ["a" =: s "&"] "#&",+ Test "{#a}" ["a" =: s "\xa0"] "#%C2%A0",+ Test "{#a}" ["a" =: s "\xd7ff"] "#%ED%9F%BF",+ Test "{#a}" ["a" =: s "\x10000"] "#%F0%90%80%80",+ Test "{#a}" ["a" =: l ["A"]] "#A",+ Test "{#a}" ["a" =: l ["A", "B"]] "#A,B",+ Test "{#a}" ["a" =: l ["%"]] "#%25",+ Test "{#a}" ["a" =: l ["\xa0"]] "#%C2%A0",+ Test "{#a}" ["a" =: d ["A" =: "1"]] "#A,1",+ Test "{#a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "#A,1,B,2",+ Test "{#a}" ["a" =: d ["A" =: "%"]] "#A,%25",+ Test "{#a}" ["a" =: d ["A" =: "\xa0"]] "#A,%C2%A0",+ Test "{#a}" ["a" =: d ["%" =: "1"]] "#%25,1",+ Test "{#a*}" [] "",+ Test "{#a*}" ["a" =: s ""] "#",+ Test "{#a*}" ["a" =: s "A"] "#A",+ Test "{#a*}" ["a" =: l []] "",+ Test "{#a*}" ["a" =: l ["A"]] "#A",+ Test "{#a*}" ["a" =: l ["A", "B"]] "#A,B",+ Test "{#a*}" ["a" =: d []] "",+ Test "{#a*}" ["a" =: d ["A" =: "1"]] "#A=1",+ Test "{#a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "#A=1,B=2",+ Test "{#a:1}" [] "",+ Test "{#a:1}" ["a" =: s ""] "#",+ Test "{#a:1}" ["a" =: s "A"] "#A",+ Test "{#a:1}" ["a" =: s "AB"] "#A",+ Test "{#a:1}" ["a" =: s "%B"] "#%25",+ Test "{#a:1}" ["a" =: s "\xa0\&B"] "#%C2%A0",+ Test "{#a:1}" ["a" =: s "\xd7ff\&B"] "#%ED%9F%BF",+ Test "{#a:1}" ["a" =: s "\x10000\&B"] "#%F0%90%80%80",+ Test "{#a:1}" ["a" =: l []] "",+ Test "{#a:1}" ["a" =: l ["AB"]] "#AB",+ Test "{#a:1}" ["a" =: l ["AB", "CD"]] "#AB,CD",+ Test "{#a:1}" ["a" =: d []] "",+ Test "{#a:1}" ["a" =: d ["AB" =: "12"]] "#AB,12",+ Test "{#a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "#AB,12,CD,34",+ Test "{#a,a}" [] "",+ Test "{#a,a}" ["a" =: l []] "",+ Test "{#a,a}" ["a" =: d []] "",+ Test "{#a,a}" ["a" =: s ""] "#,",+ Test "{#a,b}" ["a" =: s ""] "#",+ Test "{#a,b}" ["b" =: s ""] "#",+ Test "{#%aa}" ["%aa" =: s "A"] "#A",+ Test "{#%aa}" ["%aa" =: l ["A", "B"]] "#A,B",+ Test "{#%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "#A,1,B,2",+ Test "{#%aa*}" ["%aa" =: s "A"] "#A",+ Test "{#%aa*}" ["%aa" =: l ["A", "B"]] "#A,B",+ Test "{#%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "#A=1,B=2",+ Test "{.a}" [] "",+ Test "{.a}" ["a" =: l []] "",+ Test "{.a}" ["a" =: d []] "",+ Test "{.a}" ["a" =: s ""] ".",+ Test "{.a}" ["a" =: s "A"] ".A",+ Test "{.a}" ["a" =: s "~"] ".~",+ Test "{.a}" ["a" =: s "%"] ".%25",+ Test "{.a}" ["a" =: s "?"] ".%3F",+ Test "{.a}" ["a" =: s "&"] ".%26",+ Test "{.a}" ["a" =: s "\xa0"] ".%C2%A0",+ Test "{.a}" ["a" =: s "\xd7ff"] ".%ED%9F%BF",+ Test "{.a}" ["a" =: s "\x10000"] ".%F0%90%80%80",+ Test "{.a}" ["a" =: l ["A"]] ".A",+ Test "{.a}" ["a" =: l ["A", "B"]] ".A,B",+ Test "{.a}" ["a" =: l ["%"]] ".%25",+ Test "{.a}" ["a" =: l ["\xa0"]] ".%C2%A0",+ Test "{.a}" ["a" =: d ["A" =: "1"]] ".A,1",+ Test "{.a}" ["a" =: d ["A" =: "1", "B" =: "2"]] ".A,1,B,2",+ Test "{.a}" ["a" =: d ["A" =: "%"]] ".A,%25",+ Test "{.a}" ["a" =: d ["A" =: "\xa0"]] ".A,%C2%A0",+ Test "{.a}" ["a" =: d ["%" =: "1"]] ".%25,1",+ Test "{.a*}" [] "",+ Test "{.a*}" ["a" =: s ""] ".",+ Test "{.a*}" ["a" =: s "A"] ".A",+ Test "{.a*}" ["a" =: l []] "",+ Test "{.a*}" ["a" =: l ["A"]] ".A",+ Test "{.a*}" ["a" =: l ["A", "B"]] ".A.B",+ Test "{.a*}" ["a" =: d []] "",+ Test "{.a*}" ["a" =: d ["A" =: "1"]] ".A=1",+ Test "{.a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] ".A=1.B=2",+ Test "{.a:1}" [] "",+ Test "{.a:1}" ["a" =: s ""] ".",+ Test "{.a:1}" ["a" =: s "A"] ".A",+ Test "{.a:1}" ["a" =: s "AB"] ".A",+ Test "{.a:1}" ["a" =: s "%B"] ".%25",+ Test "{.a:1}" ["a" =: s "\xa0\&B"] ".%C2%A0",+ Test "{.a:1}" ["a" =: s "\xd7ff\&B"] ".%ED%9F%BF",+ Test "{.a:1}" ["a" =: s "\x10000\&B"] ".%F0%90%80%80",+ Test "{.a:1}" ["a" =: l []] "",+ Test "{.a:1}" ["a" =: l ["AB"]] ".AB",+ Test "{.a:1}" ["a" =: l ["AB", "CD"]] ".AB,CD",+ Test "{.a:1}" ["a" =: d []] "",+ Test "{.a:1}" ["a" =: d ["AB" =: "12"]] ".AB,12",+ Test "{.a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] ".AB,12,CD,34",+ Test "{.a,a}" [] "",+ Test "{.a,a}" ["a" =: l []] "",+ Test "{.a,a}" ["a" =: d []] "",+ Test "{.a,a}" ["a" =: s ""] "..",+ Test "{.a,b}" ["a" =: s ""] ".",+ Test "{.a,b}" ["b" =: s ""] ".",+ Test "{.%aa}" ["%aa" =: s "A"] ".A",+ Test "{.%aa}" ["%aa" =: l ["A", "B"]] ".A,B",+ Test "{.%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ".A,1,B,2",+ Test "{.%aa*}" ["%aa" =: s "A"] ".A",+ Test "{.%aa*}" ["%aa" =: l ["A", "B"]] ".A.B",+ Test "{.%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ".A=1.B=2",+ Test "{/a}" [] "",+ Test "{/a}" ["a" =: l []] "",+ Test "{/a}" ["a" =: d []] "",+ Test "{/a}" ["a" =: s ""] "/",+ Test "{/a}" ["a" =: s "A"] "/A",+ Test "{/a}" ["a" =: s "~"] "/~",+ Test "{/a}" ["a" =: s "%"] "/%25",+ Test "{/a}" ["a" =: s "?"] "/%3F",+ Test "{/a}" ["a" =: s "&"] "/%26",+ Test "{/a}" ["a" =: s "\xa0"] "/%C2%A0",+ Test "{/a}" ["a" =: s "\xd7ff"] "/%ED%9F%BF",+ Test "{/a}" ["a" =: s "\x10000"] "/%F0%90%80%80",+ Test "{/a}" ["a" =: l ["A"]] "/A",+ Test "{/a}" ["a" =: l ["A", "B"]] "/A,B",+ Test "{/a}" ["a" =: l ["%"]] "/%25",+ Test "{/a}" ["a" =: l ["\xa0"]] "/%C2%A0",+ Test "{/a}" ["a" =: d ["A" =: "1"]] "/A,1",+ Test "{/a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "/A,1,B,2",+ Test "{/a}" ["a" =: d ["A" =: "%"]] "/A,%25",+ Test "{/a}" ["a" =: d ["A" =: "\xa0"]] "/A,%C2%A0",+ Test "{/a}" ["a" =: d ["%" =: "1"]] "/%25,1",+ Test "{/a*}" [] "",+ Test "{/a*}" ["a" =: s ""] "/",+ Test "{/a*}" ["a" =: s "A"] "/A",+ Test "{/a*}" ["a" =: l []] "",+ Test "{/a*}" ["a" =: l ["A"]] "/A",+ Test "{/a*}" ["a" =: l ["A", "B"]] "/A/B",+ Test "{/a*}" ["a" =: d []] "",+ Test "{/a*}" ["a" =: d ["A" =: "1"]] "/A=1",+ Test "{/a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "/A=1/B=2",+ Test "{/a:1}" [] "",+ Test "{/a:1}" ["a" =: s ""] "/",+ Test "{/a:1}" ["a" =: s "A"] "/A",+ Test "{/a:1}" ["a" =: s "AB"] "/A",+ Test "{/a:1}" ["a" =: s "%B"] "/%25",+ Test "{/a:1}" ["a" =: s "\xa0\&B"] "/%C2%A0",+ Test "{/a:1}" ["a" =: s "\xd7ff\&B"] "/%ED%9F%BF",+ Test "{/a:1}" ["a" =: s "\x10000\&B"] "/%F0%90%80%80",+ Test "{/a:1}" ["a" =: l []] "",+ Test "{/a:1}" ["a" =: l ["AB"]] "/AB",+ Test "{/a:1}" ["a" =: l ["AB", "CD"]] "/AB,CD",+ Test "{/a:1}" ["a" =: d []] "",+ Test "{/a:1}" ["a" =: d ["AB" =: "12"]] "/AB,12",+ Test "{/a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "/AB,12,CD,34",+ Test "{/a,a}" [] "",+ Test "{/a,a}" ["a" =: l []] "",+ Test "{/a,a}" ["a" =: d []] "",+ Test "{/a,a}" ["a" =: s ""] "//",+ Test "{/a,b}" ["a" =: s ""] "/",+ Test "{/a,b}" ["b" =: s ""] "/",+ Test "{/%aa}" ["%aa" =: s "A"] "/A",+ Test "{/%aa}" ["%aa" =: l ["A", "B"]] "/A,B",+ Test "{/%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "/A,1,B,2",+ Test "{/%aa*}" ["%aa" =: s "A"] "/A",+ Test "{/%aa*}" ["%aa" =: l ["A", "B"]] "/A/B",+ Test "{/%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "/A=1/B=2",+ Test "{;a}" [] "",+ Test "{;a}" ["a" =: l []] "",+ Test "{;a}" ["a" =: d []] "",+ Test "{;a}" ["a" =: s ""] ";a",+ Test "{;a}" ["a" =: s "A"] ";a=A",+ Test "{;a}" ["a" =: s "~"] ";a=~",+ Test "{;a}" ["a" =: s "%"] ";a=%25",+ Test "{;a}" ["a" =: s "?"] ";a=%3F",+ Test "{;a}" ["a" =: s "&"] ";a=%26",+ Test "{;a}" ["a" =: s "\xa0"] ";a=%C2%A0",+ Test "{;a}" ["a" =: s "\xd7ff"] ";a=%ED%9F%BF",+ Test "{;a}" ["a" =: s "\x10000"] ";a=%F0%90%80%80",+ Test "{;a}" ["a" =: l ["A"]] ";a=A",+ Test "{;a}" ["a" =: l ["A", "B"]] ";a=A,B",+ Test "{;a}" ["a" =: l ["%"]] ";a=%25",+ Test "{;a}" ["a" =: l ["\xa0"]] ";a=%C2%A0",+ Test "{;a}" ["a" =: d ["A" =: "1"]] ";a=A,1",+ Test "{;a}" ["a" =: d ["A" =: "1", "B" =: "2"]] ";a=A,1,B,2",+ Test "{;a}" ["a" =: d ["A" =: "%"]] ";a=A,%25",+ Test "{;a}" ["a" =: d ["A" =: "\xa0"]] ";a=A,%C2%A0",+ Test "{;a}" ["a" =: d ["%" =: "1"]] ";a=%25,1",+ Test "{;a*}" [] "",+ Test "{;a*}" ["a" =: s ""] ";a",+ Test "{;a*}" ["a" =: s "A"] ";a=A",+ Test "{;a*}" ["a" =: l []] "",+ Test "{;a*}" ["a" =: l ["A"]] ";a=A",+ Test "{;a*}" ["a" =: l ["A", "B"]] ";a=A;a=B",+ Test "{;a*}" ["a" =: d []] "",+ Test "{;a*}" ["a" =: d ["A" =: "1"]] ";A=1",+ Test "{;a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] ";A=1;B=2",+ Test "{;a:1}" [] "",+ Test "{;a:1}" ["a" =: s ""] ";a",+ Test "{;a:1}" ["a" =: s "A"] ";a=A",+ Test "{;a:1}" ["a" =: s "AB"] ";a=A",+ Test "{;a:1}" ["a" =: s "%B"] ";a=%25",+ Test "{;a:1}" ["a" =: s "\xa0\&B"] ";a=%C2%A0",+ Test "{;a:1}" ["a" =: s "\xd7ff\&B"] ";a=%ED%9F%BF",+ Test "{;a:1}" ["a" =: s "\x10000\&B"] ";a=%F0%90%80%80",+ Test "{;a:1}" ["a" =: l []] "",+ Test "{;a:1}" ["a" =: l ["AB"]] ";a=AB",+ Test "{;a:1}" ["a" =: l ["AB", "CD"]] ";a=AB,CD",+ Test "{;a:1}" ["a" =: d []] "",+ Test "{;a:1}" ["a" =: d ["AB" =: "12"]] ";a=AB,12",+ Test "{;a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] ";a=AB,12,CD,34",+ Test "{;a,a}" [] "",+ Test "{;a,a}" ["a" =: l []] "",+ Test "{;a,a}" ["a" =: d []] "",+ Test "{;a,a}" ["a" =: s ""] ";a;a",+ Test "{;a,b}" ["a" =: s ""] ";a",+ Test "{;a,b}" ["b" =: s ""] ";b",+ Test "{;%aa}" ["%aa" =: s "A"] ";%aa=A",+ Test "{;%aa}" ["%aa" =: l ["A", "B"]] ";%aa=A,B",+ Test "{;%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ";%aa=A,1,B,2",+ Test "{;%aa*}" ["%aa" =: s "A"] ";%aa=A",+ Test "{;%aa*}" ["%aa" =: l ["A", "B"]] ";%aa=A;%aa=B",+ Test "{;%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] ";A=1;B=2",+ Test "{?a}" [] "",+ Test "{?a}" ["a" =: l []] "",+ Test "{?a}" ["a" =: d []] "",+ Test "{?a}" ["a" =: s ""] "?a=",+ Test "{?a}" ["a" =: s "A"] "?a=A",+ Test "{?a}" ["a" =: s "~"] "?a=~",+ Test "{?a}" ["a" =: s "%"] "?a=%25",+ Test "{?a}" ["a" =: s "?"] "?a=%3F",+ Test "{?a}" ["a" =: s "&"] "?a=%26",+ Test "{?a}" ["a" =: s "\xa0"] "?a=%C2%A0",+ Test "{?a}" ["a" =: s "\xd7ff"] "?a=%ED%9F%BF",+ Test "{?a}" ["a" =: s "\x10000"] "?a=%F0%90%80%80",+ Test "{?a}" ["a" =: l ["A"]] "?a=A",+ Test "{?a}" ["a" =: l ["A", "B"]] "?a=A,B",+ Test "{?a}" ["a" =: l ["%"]] "?a=%25",+ Test "{?a}" ["a" =: l ["\xa0"]] "?a=%C2%A0",+ Test "{?a}" ["a" =: d ["A" =: "1"]] "?a=A,1",+ Test "{?a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "?a=A,1,B,2",+ Test "{?a}" ["a" =: d ["A" =: "%"]] "?a=A,%25",+ Test "{?a}" ["a" =: d ["A" =: "\xa0"]] "?a=A,%C2%A0",+ Test "{?a}" ["a" =: d ["%" =: "1"]] "?a=%25,1",+ Test "{?a*}" [] "",+ Test "{?a*}" ["a" =: s ""] "?a=",+ Test "{?a*}" ["a" =: s "A"] "?a=A",+ Test "{?a*}" ["a" =: l []] "",+ Test "{?a*}" ["a" =: l ["A"]] "?a=A",+ Test "{?a*}" ["a" =: l ["A", "B"]] "?a=A&a=B",+ Test "{?a*}" ["a" =: d []] "",+ Test "{?a*}" ["a" =: d ["A" =: "1"]] "?A=1",+ Test "{?a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "?A=1&B=2",+ Test "{?a:1}" [] "",+ Test "{?a:1}" ["a" =: s ""] "?a=",+ Test "{?a:1}" ["a" =: s "A"] "?a=A",+ Test "{?a:1}" ["a" =: s "AB"] "?a=A",+ Test "{?a:1}" ["a" =: s "%B"] "?a=%25",+ Test "{?a:1}" ["a" =: s "\xa0\&B"] "?a=%C2%A0",+ Test "{?a:1}" ["a" =: s "\xd7ff\&B"] "?a=%ED%9F%BF",+ Test "{?a:1}" ["a" =: s "\x10000\&B"] "?a=%F0%90%80%80",+ Test "{?a:1}" ["a" =: l []] "",+ Test "{?a:1}" ["a" =: l ["AB"]] "?a=AB",+ Test "{?a:1}" ["a" =: l ["AB", "CD"]] "?a=AB,CD",+ Test "{?a:1}" ["a" =: d []] "",+ Test "{?a:1}" ["a" =: d ["AB" =: "12"]] "?a=AB,12",+ Test "{?a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "?a=AB,12,CD,34",+ Test "{?a,a}" [] "",+ Test "{?a,a}" ["a" =: l []] "",+ Test "{?a,a}" ["a" =: d []] "",+ Test "{?a,a}" ["a" =: s ""] "?a=&a=",+ Test "{?a,b}" ["a" =: s ""] "?a=",+ Test "{?a,b}" ["b" =: s ""] "?b=",+ Test "{?%aa}" ["%aa" =: s "A"] "?%aa=A",+ Test "{?%aa}" ["%aa" =: l ["A", "B"]] "?%aa=A,B",+ Test "{?%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "?%aa=A,1,B,2",+ Test "{?%aa*}" ["%aa" =: s "A"] "?%aa=A",+ Test "{?%aa*}" ["%aa" =: l ["A", "B"]] "?%aa=A&%aa=B",+ Test "{?%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "?A=1&B=2",+ Test "{&a}" [] "",+ Test "{&a}" ["a" =: l []] "",+ Test "{&a}" ["a" =: d []] "",+ Test "{&a}" ["a" =: s ""] "&a=",+ Test "{&a}" ["a" =: s "A"] "&a=A",+ Test "{&a}" ["a" =: s "~"] "&a=~",+ Test "{&a}" ["a" =: s "%"] "&a=%25",+ Test "{&a}" ["a" =: s "?"] "&a=%3F",+ Test "{&a}" ["a" =: s "&"] "&a=%26",+ Test "{&a}" ["a" =: s "\xa0"] "&a=%C2%A0",+ Test "{&a}" ["a" =: s "\xd7ff"] "&a=%ED%9F%BF",+ Test "{&a}" ["a" =: s "\x10000"] "&a=%F0%90%80%80",+ Test "{&a}" ["a" =: l ["A"]] "&a=A",+ Test "{&a}" ["a" =: l ["A", "B"]] "&a=A,B",+ Test "{&a}" ["a" =: l ["%"]] "&a=%25",+ Test "{&a}" ["a" =: l ["\xa0"]] "&a=%C2%A0",+ Test "{&a}" ["a" =: d ["A" =: "1"]] "&a=A,1",+ Test "{&a}" ["a" =: d ["A" =: "1", "B" =: "2"]] "&a=A,1,B,2",+ Test "{&a}" ["a" =: d ["A" =: "%"]] "&a=A,%25",+ Test "{&a}" ["a" =: d ["A" =: "\xa0"]] "&a=A,%C2%A0",+ Test "{&a}" ["a" =: d ["%" =: "1"]] "&a=%25,1",+ Test "{&a*}" [] "",+ Test "{&a*}" ["a" =: s ""] "&a=",+ Test "{&a*}" ["a" =: s "A"] "&a=A",+ Test "{&a*}" ["a" =: l []] "",+ Test "{&a*}" ["a" =: l ["A"]] "&a=A",+ Test "{&a*}" ["a" =: l ["A", "B"]] "&a=A&a=B",+ Test "{&a*}" ["a" =: d []] "",+ Test "{&a*}" ["a" =: d ["A" =: "1"]] "&A=1",+ Test "{&a*}" ["a" =: d ["A" =: "1", "B" =: "2"]] "&A=1&B=2",+ Test "{&a:1}" [] "",+ Test "{&a:1}" ["a" =: s ""] "&a=",+ Test "{&a:1}" ["a" =: s "A"] "&a=A",+ Test "{&a:1}" ["a" =: s "AB"] "&a=A",+ Test "{&a:1}" ["a" =: s "%B"] "&a=%25",+ Test "{&a:1}" ["a" =: s "\xa0\&B"] "&a=%C2%A0",+ Test "{&a:1}" ["a" =: s "\xd7ff\&B"] "&a=%ED%9F%BF",+ Test "{&a:1}" ["a" =: s "\x10000\&B"] "&a=%F0%90%80%80",+ Test "{&a:1}" ["a" =: l []] "",+ Test "{&a:1}" ["a" =: l ["AB"]] "&a=AB",+ Test "{&a:1}" ["a" =: l ["AB", "CD"]] "&a=AB,CD",+ Test "{&a:1}" ["a" =: d []] "",+ Test "{&a:1}" ["a" =: d ["AB" =: "12"]] "&a=AB,12",+ Test "{&a:1}" ["a" =: d ["AB" =: "12", "CD" =: "34"]] "&a=AB,12,CD,34",+ Test "{&a,a}" [] "",+ Test "{&a,a}" ["a" =: l []] "",+ Test "{&a,a}" ["a" =: d []] "",+ Test "{&a,a}" ["a" =: s ""] "&a=&a=",+ Test "{&a,b}" ["a" =: s ""] "&a=",+ Test "{&a,b}" ["b" =: s ""] "&b=",+ Test "{&%aa}" ["%aa" =: s "A"] "&%aa=A",+ Test "{&%aa}" ["%aa" =: l ["A", "B"]] "&%aa=A,B",+ Test "{&%aa}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "&%aa=A,1,B,2",+ Test "{&%aa*}" ["%aa" =: s "A"] "&%aa=A",+ Test "{&%aa*}" ["%aa" =: l ["A", "B"]] "&%aa=A&%aa=B",+ Test "{&%aa*}" ["%aa" =: d ["A" =: "1", "B" =: "2"]] "&A=1&B=2"+ ],+ let values = ["%AA" =: s "1", "%Aa" =: s "2", "%aA" =: s "3", "%aa" =: s "4"]+ in [ Test "{%AA}" values "1",+ Test "{%Aa}" values "2",+ Test "{%aA}" values "3",+ Test "{%aa}" values "4"+ -- This comment forces Brittany to use a multi-line layout.+ ],+ let values = ["a" =: s "abcdefghijklmnopqrstuvwxyz"]+ in [ Test "{a:1}" values "a",+ Test "{a:5}" values "abcde",+ Test "{a:10}" values "abcdefghij",+ Test "{a:15}" values "abcdefghijklmno",+ Test "{a:20}" values "abcdefghijklmnopqrst",+ Test "{a:25}" values "abcdefghijklmnopqrstuvwxy",+ Test "{a:26}" values "abcdefghijklmnopqrstuvwxyz",+ Test "{a:30}" values "abcdefghijklmnopqrstuvwxyz"+ ],+ let values = ["a" =: l []]+ in [ Test "{a}" values "",+ Test "{+a}" values "",+ Test "{#a}" values "",+ Test "{.a}" values "",+ Test "{/a}" values "",+ Test "{;a}" values "",+ Test "{?a}" values "",+ Test "{&a}" values ""+ ],+ let values = ["a" =: d []]+ in [ Test "{a}" values "",+ Test "{+a}" values "",+ Test "{#a}" values "",+ Test "{.a}" values "",+ Test "{/a}" values "",+ Test "{;a}" values "",+ Test "{?a}" values "",+ Test "{&a}" values ""+ ],+ let values = ["a" =: s ""]+ in [ Test "{a}" values "",+ Test "{+a}" values "",+ Test "{#a}" values "#",+ Test "{.a}" values ".",+ Test "{/a}" values "/",+ Test "{;a}" values ";a",+ Test "{?a}" values "?a=",+ Test "{&a}" values "&a="+ ],+ let values = ["a" =: s "A"]+ in [ Test "{a}" values "A",+ Test "{+a}" values "A",+ Test "{#a}" values "#A",+ Test "{.a}" values ".A",+ Test "{/a}" values "/A",+ Test "{;a}" values ";a=A",+ Test "{?a}" values "?a=A",+ Test "{&a}" values "&a=A"+ ],+ let values = ["b" =: s "B"]+ in [ Test "{a,b}" values "B",+ Test "{+a,b}" values "B",+ Test "{#a,b}" values "#B",+ Test "{.a,b}" values ".B",+ Test "{/a,b}" values "/B",+ Test "{;a,b}" values ";b=B",+ Test "{?a,b}" values "?b=B",+ Test "{&a,b}" values "&b=B"+ ],+ let values = ["a" =: s ""]+ in [ Test "{a,a}" values ",",+ Test "{+a,a}" values ",",+ Test "{#a,a}" values "#,",+ Test "{.a,a}" values "..",+ Test "{/a,a}" values "//",+ Test "{;a,a}" values ";a;a",+ Test "{?a,a}" values "?a=&a=",+ Test "{&a,a}" values "&a=&a="+ ],+ let values = ["a" =: s "A", "b" =: s "B"]+ in [ Test "{a,b}" values "A,B",+ Test "{+a,b}" values "A,B",+ Test "{#a,b}" values "#A,B",+ Test "{.a,b}" values ".A.B",+ Test "{/a,b}" values "/A/B",+ Test "{;a,b}" values ";a=A;b=B",+ Test "{?a,b}" values "?a=A&b=B",+ Test "{&a,b}" values "&a=A&b=B"+ ],+ let values = ["a" =: d ["K! \xa0\xd7ff\x10000" =: "V! \xa0\xd7ff\x10000"]]+ in [ Test "{a*}" values "K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{+a*}" values "K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{#a*}" values "#K!%20%C2%A0%ED%9F%BF%F0%90%80%80=V!%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{.a*}" values ".K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{/a*}" values "/K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{;a*}" values ";K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{?a*}" values "?K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80",+ Test "{&a*}" values "&K%21%20%C2%A0%ED%9F%BF%F0%90%80%80=V%21%20%C2%A0%ED%9F%BF%F0%90%80%80"+ ],+ let values =+ [ "empty" =: s "",+ "hello" =: s "Hello World!",+ "keys" =: d ["semi" =: ";", "dot" =: ".", "comma" =: ","],+ "list" =: l ["red", "green", "blue"],+ "path" =: s "/foo/bar",+ "var" =: s "value",+ "x" =: s "1024",+ "y" =: s "768"+ ]+ in [ Test "{var}" values "value",+ Test "{hello}" values "Hello%20World%21",+ Test "{+var}" values "value",+ Test "{+hello}" values "Hello%20World!",+ Test "{+path}/here" values "/foo/bar/here",+ Test "here?ref={+path}" values "here?ref=/foo/bar",+ Test "X{#var}" values "X#value",+ Test "X{#hello}" values "X#Hello%20World!",+ Test "map?{x,y}" values "map?1024,768",+ Test "{x,hello,y}" values "1024,Hello%20World%21,768",+ Test "{+x,hello,y}" values "1024,Hello%20World!,768",+ Test "{+path,x}/here" values "/foo/bar,1024/here",+ Test "{#x,hello,y}" values "#1024,Hello%20World!,768",+ Test "{#path,x}/here" values "#/foo/bar,1024/here",+ Test "X{.var}" values "X.value",+ Test "X{.x,y}" values "X.1024.768",+ Test "{/var}" values "/value",+ Test "{/var,x}/here" values "/value/1024/here",+ Test "{;x,y}" values ";x=1024;y=768",+ Test "{;x,y,empty}" values ";x=1024;y=768;empty",+ Test "{?x,y}" values "?x=1024&y=768",+ Test "{?x,y,empty}" values "?x=1024&y=768&empty=",+ Test "?fixed=yes{&x}" values "?fixed=yes&x=1024",+ Test "{&x,y,empty}" values "&x=1024&y=768&empty=",+ Test "{var:3}" values "val",+ Test "{var:30}" values "value",+ Test "{list}" values "red,green,blue",+ Test "{list*}" values "red,green,blue",+ Test "{keys}" values "comma,%2C,dot,.,semi,%3B",+ Test "{keys*}" values "comma=%2C,dot=.,semi=%3B",+ Test "{+path:6}/here" values "/foo/b/here",+ Test "{+list}" values "red,green,blue",+ Test "{+list*}" values "red,green,blue",+ Test "{+keys}" values "comma,,,dot,.,semi,;",+ Test "{+keys*}" values "comma=,,dot=.,semi=;",+ Test "{#path:6}/here" values "#/foo/b/here",+ Test "{#list}" values "#red,green,blue",+ Test "{#list*}" values "#red,green,blue",+ Test "{#keys}" values "#comma,,,dot,.,semi,;",+ Test "{#keys*}" values "#comma=,,dot=.,semi=;",+ Test "X{.var:3}" values "X.val",+ Test "X{.list}" values "X.red,green,blue",+ Test "X{.list*}" values "X.red.green.blue",+ Test "X{.keys}" values "X.comma,%2C,dot,.,semi,%3B",+ Test "X{.keys*}" values "X.comma=%2C.dot=..semi=%3B",+ Test "{/var:1,var}" values "/v/value",+ Test "{/list}" values "/red,green,blue",+ Test "{/list*}" values "/red/green/blue",+ Test "{/list*,path:4}" values "/red/green/blue/%2Ffoo",+ Test "{/keys}" values "/comma,%2C,dot,.,semi,%3B",+ Test "{/keys*}" values "/comma=%2C/dot=./semi=%3B",+ Test "{;hello:5}" values ";hello=Hello",+ Test "{;list}" values ";list=red,green,blue",+ Test "{;list*}" values ";list=red;list=green;list=blue",+ Test "{;keys}" values ";keys=comma,%2C,dot,.,semi,%3B",+ Test "{;keys*}" values ";comma=%2C;dot=.;semi=%3B",+ Test "{?var:3}" values "?var=val",+ Test "{?list}" values "?list=red,green,blue",+ Test "{?list*}" values "?list=red&list=green&list=blue",+ Test "{?keys}" values "?keys=comma,%2C,dot,.,semi,%3B",+ Test "{?keys*}" values "?comma=%2C&dot=.&semi=%3B",+ Test "{&var:3}" values "&var=val",+ Test "{&list}" values "&list=red,green,blue",+ Test "{&list*}" values "&list=red&list=green&list=blue",+ Test "{&keys}" values "&keys=comma,%2C,dot,.,semi,%3B",+ Test "{&keys*}" values "&comma=%2C&dot=.&semi=%3B"+ ],+ let values = ["var" =: s "value", "semi" =: s ";"]+ in [ Test "{var}" values "value",+ Test "{var:20}" values "value",+ Test "{var:3}" values "val",+ Test "{semi}" values "%3B",+ Test "{semi:2}" values "%3B"+ -- This comment forces Brittany to use a multi-line layout.+ ],+ let values = ["year" =: l ["1965", "2000", "2012"], "dom" =: l ["example", "com"]]+ in [ Test "find{?year*}" values "find?year=1965&year=2000&year=2012",+ Test "www{.dom*}" values "www.example.com"+ -- This comment forces Brittany to use a multi-line layout.+ ],+ let values =+ [ "base" =: s "http://example.com/home/",+ "count" =: l ["one", "two", "three"],+ "dom" =: l ["example", "com"],+ "dub" =: s "me/too",+ "empty_keys" =: d [],+ "empty" =: s "",+ "half" =: s "50%",+ "hello" =: s "Hello World!",+ "keys" =: d ["semi" =: ";", "dot" =: ".", "comma" =: ","],+ "list" =: l ["red", "green", "blue"],+ "path" =: s "/foo/bar",+ "v" =: s "6",+ "var" =: s "value",+ "who" =: s "fred",+ "x" =: s "1024",+ "y" =: s "768"+ ]+ in [ Test "{count}" values "one,two,three",+ Test "{count*}" values "one,two,three",+ Test "{/count}" values "/one,two,three",+ Test "{/count*}" values "/one/two/three",+ Test "{;count}" values ";count=one,two,three",+ Test "{;count*}" values ";count=one;count=two;count=three",+ Test "{?count}" values "?count=one,two,three",+ Test "{?count*}" values "?count=one&count=two&count=three",+ Test "{&count*}" values "&count=one&count=two&count=three",+ Test "{var}" values "value",+ Test "{hello}" values "Hello%20World%21",+ Test "{half}" values "50%25",+ Test "O{empty}X" values "OX",+ Test "O{undef}X" values "OX",+ Test "{x,y}" values "1024,768",+ Test "{x,hello,y}" values "1024,Hello%20World%21,768",+ Test "?{x,empty}" values "?1024,",+ Test "?{x,undef}" values "?1024",+ Test "?{undef,y}" values "?768",+ Test "{var:3}" values "val",+ Test "{var:30}" values "value",+ Test "{list}" values "red,green,blue",+ Test "{list*}" values "red,green,blue",+ Test "{keys}" values "comma,%2C,dot,.,semi,%3B",+ Test "{keys*}" values "comma=%2C,dot=.,semi=%3B",+ Test "{+var}" values "value",+ Test "{+hello}" values "Hello%20World!",+ Test "{+half}" values "50%25",+ Test "{base}index" values "http%3A%2F%2Fexample.com%2Fhome%2Findex",+ Test "{+base}index" values "http://example.com/home/index",+ Test "O{+empty}X" values "OX",+ Test "O{+undef}X" values "OX",+ Test "{+path}/here" values "/foo/bar/here",+ Test "here?ref={+path}" values "here?ref=/foo/bar",+ Test "up{+path}{var}/here" values "up/foo/barvalue/here",+ Test "{+x,hello,y}" values "1024,Hello%20World!,768",+ Test "{+path,x}/here" values "/foo/bar,1024/here",+ Test "{+path:6}/here" values "/foo/b/here",+ Test "{+list}" values "red,green,blue",+ Test "{+list*}" values "red,green,blue",+ Test "{+keys}" values "comma,,,dot,.,semi,;",+ Test "{+keys*}" values "comma=,,dot=.,semi=;",+ Test "{#var}" values "#value",+ Test "{#hello}" values "#Hello%20World!",+ Test "{#half}" values "#50%25",+ Test "foo{#empty}" values "foo#",+ Test "foo{#undef}" values "foo",+ Test "{#x,hello,y}" values "#1024,Hello%20World!,768",+ Test "{#path,x}/here" values "#/foo/bar,1024/here",+ Test "{#path:6}/here" values "#/foo/b/here",+ Test "{#list}" values "#red,green,blue",+ Test "{#list*}" values "#red,green,blue",+ Test "{#keys}" values "#comma,,,dot,.,semi,;",+ Test "{#keys*}" values "#comma=,,dot=.,semi=;",+ Test "{.who}" values ".fred",+ Test "{.who,who}" values ".fred.fred",+ Test "{.half,who}" values ".50%25.fred",+ Test "www{.dom*}" values "www.example.com",+ Test "X{.var}" values "X.value",+ Test "X{.empty}" values "X.",+ Test "X{.undef}" values "X",+ Test "X{.var:3}" values "X.val",+ Test "X{.list}" values "X.red,green,blue",+ Test "X{.list*}" values "X.red.green.blue",+ Test "X{.keys}" values "X.comma,%2C,dot,.,semi,%3B",+ Test "X{.keys*}" values "X.comma=%2C.dot=..semi=%3B",+ Test "X{.empty_keys}" values "X",+ Test "X{.empty_keys*}" values "X",+ Test "{/who}" values "/fred",+ Test "{/who,who}" values "/fred/fred",+ Test "{/half,who}" values "/50%25/fred",+ Test "{/who,dub}" values "/fred/me%2Ftoo",+ Test "{/var}" values "/value",+ Test "{/var,empty}" values "/value/",+ Test "{/var,undef}" values "/value",+ Test "{/var,x}/here" values "/value/1024/here",+ Test "{/var:1,var}" values "/v/value",+ Test "{/list}" values "/red,green,blue",+ Test "{/list*}" values "/red/green/blue",+ Test "{/list*,path:4}" values "/red/green/blue/%2Ffoo",+ Test "{/keys}" values "/comma,%2C,dot,.,semi,%3B",+ Test "{/keys*}" values "/comma=%2C/dot=./semi=%3B",+ Test "{;who}" values ";who=fred",+ Test "{;half}" values ";half=50%25",+ Test "{;empty}" values ";empty",+ Test "{;v,empty,who}" values ";v=6;empty;who=fred",+ Test "{;v,bar,who}" values ";v=6;who=fred",+ Test "{;x,y}" values ";x=1024;y=768",+ Test "{;x,y,empty}" values ";x=1024;y=768;empty",+ Test "{;x,y,undef}" values ";x=1024;y=768",+ Test "{;hello:5}" values ";hello=Hello",+ Test "{;list}" values ";list=red,green,blue",+ Test "{;list*}" values ";list=red;list=green;list=blue",+ Test "{;keys}" values ";keys=comma,%2C,dot,.,semi,%3B",+ Test "{;keys*}" values ";comma=%2C;dot=.;semi=%3B",+ Test "{?who}" values "?who=fred",+ Test "{?half}" values "?half=50%25",+ Test "{?x,y}" values "?x=1024&y=768",+ Test "{?x,y,empty}" values "?x=1024&y=768&empty=",+ Test "{?x,y,undef}" values "?x=1024&y=768",+ Test "{?var:3}" values "?var=val",+ Test "{?list}" values "?list=red,green,blue",+ Test "{?list*}" values "?list=red&list=green&list=blue",+ Test "{?keys}" values "?keys=comma,%2C,dot,.,semi,%3B",+ Test "{?keys*}" values "?comma=%2C&dot=.&semi=%3B",+ Test "{&who}" values "&who=fred",+ Test "{&half}" values "&half=50%25",+ Test "?fixed=yes{&x}" values "?fixed=yes&x=1024",+ Test "{&x,y,empty}" values "&x=1024&y=768&empty=",+ Test "{&x,y,undef}" values "&x=1024&y=768",+ Test "{&var:3}" values "&var=val",+ Test "{&list}" values "&list=red,green,blue",+ Test "{&list*}" values "&list=red&list=green&list=blue",+ Test "{&keys}" values "&keys=comma,%2C,dot,.,semi,%3B",+ Test "{&keys*}" values "&comma=%2C&dot=.&semi=%3B"+ ]+ ]++data Test = Test+ { testInput :: String,+ testValues :: [(String, Burrito.Value)],+ testOutput :: Output+ }+ deriving (Eq, Show)++runTest :: Test -> Hspec.Expectation+runTest test =+ case (Burrito.parse $ testInput test, unwrapOutput $ testOutput test) of+ (Nothing, Nothing) -> pure ()+ (Nothing, Just _) -> Hspec.expectationFailure "should have parsed"+ (Just _, Nothing) -> Hspec.expectationFailure "should not have parsed"+ (Just template, Just expected) -> do+ let values = testValues test+ actual = Burrito.expand values template+ actual `Hspec.shouldBe` expected+ Burrito.parse (show template) `Hspec.shouldBe` Just template+ let relevant =+ List.sort $ keepRelevant (templateVariables template) values+ Monad.when (isMatchable template relevant) $ do+ let matches = List.sort <$> Burrito.match expected template+ matches `Hspec.shouldSatisfy` elem relevant++isMatchable :: Template.Template -> [(String, Burrito.Value)] -> Bool+isMatchable template values =+ (not . any (isAsterisk . Variable.modifier) $ templateVariables template)+ && all (isString . snd) values++isString :: Burrito.Value -> Bool+isString value = case value of+ Value.String _ -> True+ _ -> False++isAsterisk :: Modifier.Modifier -> Bool+isAsterisk modifier = case modifier of+ Modifier.Asterisk -> True+ _ -> False++keepRelevant ::+ Set.Set Variable.Variable ->+ [(String, Burrito.Value)] ->+ [(String, Burrito.Value)]+keepRelevant variables =+ let vs =+ Map.fromListWith+ ( \mx my -> case (mx, my) of+ (Just x, Just y) -> Just $ max x y+ _ -> Nothing+ )+ . fmap+ ( \v ->+ ( Render.builderToString . Render.name $ Variable.name v,+ case Variable.modifier v of+ Modifier.Colon n -> Just $ MaxLength.count n+ _ -> Nothing+ )+ )+ $ Set.toList variables+ in Maybe.mapMaybe $ \(k, v) -> do+ m <- Map.lookup k vs+ pure . (,) k $ case m of+ Nothing -> v+ Just n -> case v of+ Value.String t -> Value.String $ Text.take n t+ _ -> v++templateVariables :: Template.Template -> Set.Set Variable.Variable+templateVariables =+ Set.fromList+ . concatMap+ ( \token -> case token of+ Token.Expression expression ->+ NonEmpty.toList $ Expression.variables expression+ Token.Literal _ -> []+ )+ . Template.tokens++newtype Output = Output+ { unwrapOutput :: Maybe String+ }+ deriving (Eq, Show)++instance String.IsString Output where+ fromString = Output . Just++noParse :: Output+noParse = Output Nothing++(=:) :: a -> b -> (a, b)+(=:) = (,)++s :: String -> Burrito.Value+s = Burrito.stringValue++l :: [String] -> Burrito.Value+l = Burrito.listValue++d :: [(String, String)] -> Burrito.Value+d = Burrito.dictionaryValue++newtype Template = Template+ { unwrapTemplate :: Template.Template+ }+ deriving (Eq)++instance Show Template where+ show (Template template) =+ unwords [show $ show template, "{-", show (Template.tokens template), "-}"]++instance QC.Arbitrary Template where+ arbitrary = Template <$> arbitraryTemplate+ shrink = fmap Template . shrinkTemplate . unwrapTemplate++type Shrink a = a -> [a]++arbitraryTemplate :: QC.Gen Template.Template+arbitraryTemplate = Template.Template . simplify <$> QC.listOf arbitraryToken++shrinkTemplate :: Shrink Template.Template+shrinkTemplate =+ fmap (Template.Template . simplify)+ . QC.shrinkList shrinkToken+ . Template.tokens++simplify :: [Token.Token] -> [Token.Token]+simplify tokens = case tokens of+ t1 : t2 : ts -> case (t1, t2) of+ (Token.Literal l1, Token.Literal l2) ->+ simplify $ Token.Literal (appendLiteral l1 l2) : ts+ _ -> t1 : simplify (t2 : ts)+ _ -> tokens++appendLiteral :: Literal.Literal -> Literal.Literal -> Literal.Literal+appendLiteral x y =+ Literal.Literal $ Literal.characters x <> Literal.characters y++arbitraryToken :: QC.Gen Token.Token+arbitraryToken =+ QC.oneof+ [ Token.Expression <$> arbitraryExpression,+ Token.Literal <$> arbitraryLiteral+ ]++shrinkToken :: Shrink Token.Token+shrinkToken x = case x of+ Token.Expression y -> Token.Expression <$> shrinkExpression y+ Token.Literal y -> Token.Literal <$> shrinkLiteral y++arbitraryExpression :: QC.Gen Expression.Expression+arbitraryExpression =+ Expression.Expression+ <$> arbitraryOperator+ <*> arbitraryNonEmpty arbitraryVariable++shrinkExpression :: Shrink Expression.Expression+shrinkExpression x =+ uncurry Expression.Expression+ <$> shrinkTuple+ shrinkOperator+ (shrinkNonEmpty shrinkVariable)+ (Expression.operator x, Expression.variables x)++arbitraryOperator :: QC.Gen Operator.Operator+arbitraryOperator =+ QC.elements+ [ Operator.Ampersand,+ Operator.FullStop,+ Operator.None,+ Operator.NumberSign,+ Operator.PlusSign,+ Operator.QuestionMark,+ Operator.Semicolon,+ Operator.Solidus+ ]++shrinkOperator :: Shrink Operator.Operator+shrinkOperator x = case x of+ Operator.None -> []+ _ -> [Operator.None]++arbitraryNonEmpty :: QC.Gen a -> QC.Gen (NonEmpty.NonEmpty a)+arbitraryNonEmpty g = (NonEmpty.:|) <$> g <*> QC.listOf g++shrinkNonEmpty :: Shrink a -> Shrink (NonEmpty.NonEmpty a)+shrinkNonEmpty f x =+ uncurry (NonEmpty.:|)+ <$> shrinkTuple f (QC.shrinkList f) (NonEmpty.head x, NonEmpty.tail x)++arbitraryVariable :: QC.Gen Variable.Variable+arbitraryVariable = Variable.Variable <$> arbitraryName <*> arbitraryModifier++shrinkVariable :: Shrink Variable.Variable+shrinkVariable variable =+ uncurry Variable.Variable+ <$> shrinkTuple+ shrinkName+ shrinkModifier+ (Variable.name variable, Variable.modifier variable)++shrinkTuple :: Shrink a -> Shrink b -> Shrink (a, b)+shrinkTuple f g (x, y) =+ fmap (\a -> (a, y)) (f x) <> fmap (\b -> (x, b)) (g y)++arbitraryName :: QC.Gen Name.Name+arbitraryName = Name.Name <$> arbitraryNonEmpty arbitraryField++shrinkName :: Shrink Name.Name+shrinkName = fmap Name.Name . shrinkNonEmpty shrinkField . Name.fields++arbitraryField :: QC.Gen Field.Field+arbitraryField = Field.Field <$> arbitraryNonEmpty arbitraryFieldCharacter++shrinkField :: Shrink Field.Field+shrinkField =+ fmap Field.Field . shrinkNonEmpty shrinkFieldCharacter . Field.characters++arbitraryFieldCharacter :: QC.Gen (Character.Character Field.Field)+arbitraryFieldCharacter =+ QC.oneof+ [ Character.Encoded <$> arbitraryDigit <*> arbitraryDigit,+ Character.Unencoded <$> QC.suchThat QC.arbitrary Parse.isFieldCharacter+ ]++shrinkFieldCharacter :: Shrink (Character.Character Field.Field)+shrinkFieldCharacter x = case x of+ Character.Encoded y z ->+ uncurry Character.Encoded <$> shrinkTuple shrinkDigit shrinkDigit (y, z)+ Character.Unencoded y ->+ fmap Character.Unencoded . filter Parse.isFieldCharacter $ QC.shrink y++arbitraryDigit :: QC.Gen Digit.Digit+arbitraryDigit =+ QC.oneof+ [ pure Digit.Ox0,+ pure Digit.Ox1,+ pure Digit.Ox2,+ pure Digit.Ox3,+ pure Digit.Ox4,+ pure Digit.Ox5,+ pure Digit.Ox6,+ pure Digit.Ox7,+ pure Digit.Ox8,+ pure Digit.Ox9,+ Digit.OxA <$> arbitraryCase,+ Digit.OxB <$> arbitraryCase,+ Digit.OxC <$> arbitraryCase,+ Digit.OxD <$> arbitraryCase,+ Digit.OxE <$> arbitraryCase,+ Digit.OxF <$> arbitraryCase+ ]++shrinkDigit :: Shrink Digit.Digit+shrinkDigit x = case x of+ Digit.Ox0 -> []+ Digit.Ox1 -> [Digit.Ox0]+ Digit.Ox2 -> [Digit.Ox0]+ Digit.Ox3 -> [Digit.Ox0]+ Digit.Ox4 -> [Digit.Ox0]+ Digit.Ox5 -> [Digit.Ox0]+ Digit.Ox6 -> [Digit.Ox0]+ Digit.Ox7 -> [Digit.Ox0]+ Digit.Ox8 -> [Digit.Ox0]+ Digit.Ox9 -> [Digit.Ox0]+ Digit.OxA y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)+ Digit.OxB y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)+ Digit.OxC y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)+ Digit.OxD y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)+ Digit.OxE y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)+ Digit.OxF y -> Digit.Ox0 : fmap Digit.OxA (shrinkCase y)++arbitraryCase :: QC.Gen Case.Case+arbitraryCase = QC.elements [Case.Lower, Case.Upper]++shrinkCase :: Shrink Case.Case+shrinkCase x = case x of+ Case.Lower -> []+ Case.Upper -> [Case.Lower]++arbitraryModifier :: QC.Gen Modifier.Modifier+arbitraryModifier =+ QC.oneof+ [ pure Modifier.Asterisk,+ Modifier.Colon <$> arbitraryMaxLength,+ pure Modifier.None+ ]++shrinkModifier :: Shrink Modifier.Modifier+shrinkModifier x = case x of+ Modifier.Asterisk -> [Modifier.None]+ Modifier.Colon y -> Modifier.None : fmap Modifier.Colon (shrinkMaxLength y)+ Modifier.None -> []++arbitraryMaxLength :: QC.Gen MaxLength.MaxLength+arbitraryMaxLength =+ MaxLength.MaxLength <$> QC.suchThat QC.arbitrary Parse.isMaxLength++shrinkMaxLength :: Shrink MaxLength.MaxLength+shrinkMaxLength =+ fmap MaxLength.MaxLength+ . filter Parse.isMaxLength+ . QC.shrink+ . MaxLength.count++arbitraryLiteral :: QC.Gen Literal.Literal+arbitraryLiteral =+ Literal.Literal <$> arbitraryNonEmpty arbitraryLiteralCharacter++shrinkLiteral :: Shrink Literal.Literal+shrinkLiteral =+ fmap Literal.Literal+ . shrinkNonEmpty shrinkLiteralCharacter+ . Literal.characters++arbitraryLiteralCharacter :: QC.Gen (Character.Character Literal.Literal)+arbitraryLiteralCharacter =+ QC.oneof+ [ Character.Encoded <$> arbitraryDigit <*> arbitraryDigit,+ Character.Unencoded <$> QC.suchThat QC.arbitrary Parse.isLiteralCharacter+ ] shrinkLiteralCharacter :: Shrink (Character.Character Literal.Literal) shrinkLiteralCharacter x = case x of