uri-templater 1.0.0 → 1.0.0.1
raw patch · 3 files changed
+74/−4 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- src/Network/URI/Template/Parser.hs +27/−3
- test/Test.hs +46/−0
- uri-templater.cabal +1/−1
src/Network/URI/Template/Parser.hs view
@@ -172,7 +172,11 @@ -- | Parse a URI template segments {-# INLINE uriTemplate #-} uriTemplate :: Parser e (V.Vector TemplateSegment)-uriTemplate = V.fromList <$> (ws *> many (literal <|> embed))+uriTemplate = do+ ws+ segments <- many (literal <|> embed)+ eof -- Ensure we consumed all input+ return (V.fromList segments) -- | Helper to separate a parser by a separator@@ -181,13 +185,33 @@ sepBy1 p sep = (:) <$> p <*> many (sep *> p) +-- | Analyze parse failure to provide a better error message+analyzeParseFailure :: String -> ParseError+analyzeParseFailure input = go 0 0 input+ where+ go depth pos []+ | depth > 0 = UnterminatedExpression (findLastOpen input)+ | otherwise = GenericParseError ("Parse error in URI template: " ++ input)+ go depth pos ('{' : rest) = go (depth + 1) (pos + 1) rest+ go depth pos ('}' : rest)+ | depth <= 0 = UnexpectedCharacter pos '}' "Unexpected closing brace without matching opening brace"+ | otherwise = go (depth - 1) (pos + 1) rest+ go depth pos (_ : rest) = go depth (pos + 1) rest++ findLastOpen = go' 0 0+ where+ go' pos lastPos [] = lastPos+ go' pos lastPos ('{' : rest) = go' (pos + 1) pos rest+ go' pos lastPos (_ : rest) = go' (pos + 1) lastPos rest++ -- | Parse a template from a String parseTemplate :: String -> Either ParseError UriTemplate parseTemplate input = case runParser uriTemplate (TE.encodeUtf8 $ T.pack input) of OK result _ -> Right (UriTemplate result)- Err _ -> Left (GenericParseError ("Parse error in URI template: " ++ input))- Fail -> Left (GenericParseError ("Parse error in URI template: " ++ input))+ Err _ -> Left (analyzeParseFailure input)+ Fail -> Left (analyzeParseFailure input) {- | 'IsString' instance for 'UriTemplate' allows using string literals directly as templates
test/Test.hs view
@@ -18,6 +18,7 @@ import qualified Data.Vector as V import GHC.Generics (Generic, Rep) import Network.URI.Template+import Network.URI.Template.Error import Network.URI.Template.Parser import Network.URI.Template.TH import Network.URI.Template.Types@@ -191,6 +192,50 @@ rendered @?= ("?gen_product_name=Gadget&gen_product_price=200" :: Text) +parseErrorTests :: TestRegistry ()+parseErrorTests = label "Parse Error Tests" $+ suite $ do+ label "Unclosed template expression (missing closing brace)" $ test $ do+ case parseTemplate "http://example.com/dictionary/{term:1}/{term" of+ Left (UnterminatedExpression _) -> return ()+ Left err -> assertFailure $ "Expected UnterminatedExpression, got: " ++ show err+ Right _ -> assertFailure "Should fail to parse template with missing closing brace"++ label "Unopened closing brace (orphan closing brace)" $ test $ do+ case parseTemplate "http://example.com/dictionary/{term:1}/term}" of+ Left (UnexpectedCharacter _ '}' _) -> return ()+ Left err -> assertFailure $ "Expected UnexpectedCharacter for '}', got: " ++ show err+ Right _ -> assertFailure "Should fail to parse template with extra closing brace"++ label "Multiple unclosed braces" $ test $ do+ case parseTemplate "{foo}{bar" of+ Left (UnterminatedExpression _) -> return ()+ Left err -> assertFailure $ "Expected UnterminatedExpression, got: " ++ show err+ Right _ -> assertFailure "Should fail to parse template with unclosed brace"++ label "Nested braces (invalid)" $ test $ do+ case parseTemplate "{foo{bar}}" of+ Left _ -> return ()+ Right _ -> assertFailure "Should fail to parse template with nested braces"++ label "Only opening brace" $ test $ do+ case parseTemplate "{" of+ Left (UnterminatedExpression _) -> return ()+ Left err -> assertFailure $ "Expected UnterminatedExpression, got: " ++ show err+ Right _ -> assertFailure "Should fail to parse template with only opening brace"++ label "Only closing brace" $ test $ do+ case parseTemplate "}" of+ Left (UnexpectedCharacter _ '}' _) -> return ()+ Left err -> assertFailure $ "Expected UnexpectedCharacter for '}', got: " ++ show err+ Right _ -> assertFailure "Should fail to parse template with only closing brace"++ label "Valid template should still parse correctly" $ test $ do+ case parseTemplate "http://example.com/dictionary/{term:1}/{term}" of+ Right _ -> return ()+ Left err -> assertFailure $ "Valid template should parse, got error: " ++ show err++ main :: IO () main = do counts <- testRun@@ -210,6 +255,7 @@ thDerivationTests genericsDerivationTests percentEncodingTests+ parseErrorTests label "RFC 6570 Core Examples" $ suite $ do label "unescaped" $ test $ unescaped strEq
uri-templater.cabal view
@@ -2,7 +2,7 @@ -- documentation, see http://haskell.org/cabal/users-guide/ name: uri-templater-version: 1.0.0+version: 1.0.0.1 synopsis: Parsing & Quasiquoting for RFC 6570 URI Templates description: Parsing & Quasiquoting for RFC 6570 URI Templates homepage: https://github.com/iand675/uri-templater