HostAndPort 0.1.0 → 0.2.0
raw patch · 5 files changed
+141/−31 lines, 5 filesdep +criterionPVP ok
version bump matches the API change (PVP)
Dependencies added: criterion
API changes (from Hackage documentation)
+ Network.HostAndPort: HostName :: a -> ConnectionDetail a
+ Network.HostAndPort: IPv4Address :: a -> ConnectionDetail a
+ Network.HostAndPort: IPv6Address :: a -> ConnectionDetail a
+ Network.HostAndPort: data ConnectionDetail a
+ Network.HostAndPort: detailedHostAndPort :: String -> Either String (RemoteAddr, Maybe String)
+ Network.HostAndPort: instance Eq a => Eq (ConnectionDetail a)
+ Network.HostAndPort: instance Ord a => Ord (ConnectionDetail a)
+ Network.HostAndPort: instance Show a => Show (ConnectionDetail a)
+ Network.HostAndPort: type RemoteAddr = ConnectionDetail String
Files
- HostAndPort.cabal +27/−10
- README.md +17/−0
- benchmarks/Benchmark.hs +18/−0
- src/Network/HostAndPort.hs +58/−20
- tests/Spec.hs +21/−1
HostAndPort.cabal view
@@ -1,5 +1,5 @@ name: HostAndPort-version: 0.1.0+version: 0.2.0 license: MIT license-file: LICENSE author: Slava Bacherikov@@ -10,7 +10,9 @@ category: Network build-type: Simple cabal-version: >=1.10-synopsis: Parser host and port pairs like localhost:22+tested-with: GHC == 7.4, GHC == 7.6, GHC == 7.8, GHC == 7.10+extra-source-files: README.md+synopsis: Parser for host and port pairs like localhost:22 description: Simple parser for parsing host and port pairs.@@ -21,13 +23,19 @@ . Examples: .- * localhost- * localhost:8080- * 127.0.0.1- * 127.0.0.1:8080- * [::1]- * [::1]:8080+ * localhost+ .+ * localhost:8080+ .+ * 127.0.0.1+ .+ * 127.0.0.1:8080+ .+ * [::1]+ .+ * [::1]:8080 + library exposed-modules: Network.HostAndPort build-depends: base >=4.5 && <5,@@ -37,7 +45,7 @@ default-language: Haskell2010 test-suite tests- default-language: Haskell2010+ default-language: Haskell98 type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: Spec.hs@@ -47,7 +55,7 @@ hspec test-suite doctests- default-language: Haskell2010+ default-language: Haskell98 type: exitcode-stdio-1.0 hs-source-dirs: tests main-is: Doctests.hs@@ -56,6 +64,15 @@ HostAndPort, doctest +benchmark bench-hostandport+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmarks+ main-is: Benchmark.hs+ ghc-options: -O2 -Wall -fno-warn-name-shadowing+ default-language: Haskell98+ build-depends: base >=4.5 && <5,+ HostAndPort,+ criterion source-repository head type: git
+ README.md view
@@ -0,0 +1,17 @@+HostAndPort+===========++[]+(https://travis-ci.org/bacher09/hostandport)+++Simple Haskell library for parsing connection strings that contain+host and port info. For more info see package [documentation][docs].+++License+-------+MIT+++[docs]: https://hackage.haskell.org/package/HostAndPort-0.1.0/docs/Network-HostAndPort.html
+ benchmarks/Benchmark.hs view
@@ -0,0 +1,18 @@+module Main where+import Criterion.Main+import Network.HostAndPort+++main :: IO ()+main = defaultMain [+ bgroup "ipv4" [ bench "127.0.0.1" $ whnf isIPv4Address "127.0.0.1"+ , bench "bad" $ whnf isIPv4Address "22222.2222.222.22"]+ , bgroup "ipv6" [ bench "::1" $ whnf isIPv6Address "::1"+ , bench "vinnica.in" $ whnf isIPv6Address "2605:2700:0:2::4713:9eef"+ , bench "full" $ whnf isIPv6Address "1:2:3:4:5:6:7:8"+ , bench "with-ipv4" $ whnf isIPv6Address "1:2:3:4:5:6:127.0.0.1"+ , bench "bad" $ whnf isIPv6Address "2605:2700:0:2::4713:9eef"]+ , bgroup "HostAndPort" [ bench "127.0.0.1" $ nf hostAndPort "127.0.0.1"+ , bench "localhost:9090" $ nf hostAndPort "localhost:9090"+ , bench "ipv6-and-port" $ nf hostAndPort "[2605:2700:0:2::4713:9eef]:26000"]+ ]
src/Network/HostAndPort.hs view
@@ -1,17 +1,30 @@-module Network.HostAndPort (- isIPv4Address,- isIPv6Address,- hostAndPort,- maybeHostAndPort,- defaultHostAndPort-) where-import Text.Parsec-import Control.Applicative hiding((<|>), many)+module Network.HostAndPort+ ( ConnectionDetail(..)+ , RemoteAddr+ , isIPv4Address+ , isIPv6Address+ , hostAndPort+ , detailedHostAndPort+ , maybeHostAndPort+ , defaultHostAndPort+ ) where++ import Control.Monad import Data.Maybe+import Control.Applicative hiding((<|>), many)+import Control.Arrow (second)+import Text.Parsec +data ConnectionDetail a = IPv4Address a+ | IPv6Address a+ | HostName a+ deriving(Show, Eq, Ord)++ type Parser = Parsec String ()+type RemoteAddr = ConnectionDetail String countMinMax :: (Stream s m t) => Int -> Int -> ParsecT s u m a -> ParsecT s u m [a]@@ -85,7 +98,7 @@ last2f = try ipv4address <|> consequence [h4s, hexShortNum] last2 f = if f then last2f- else choice [try $ last2f,+ else choice [try last2f, try $ consequence [string "::", hexShortNum], consequence [hexShortNum, string "::"]] @@ -122,7 +135,7 @@ -- | This function will validate ipv4 address--- and return True if string is valie adress+-- and return True if string is valid adress isIPv4Address :: String -> Bool isIPv4Address = isParsed $ ipv4address <* eof @@ -148,9 +161,13 @@ asciiAlphaNum = satisfy isAsciiAlphaNum -connectionStr :: Parser (String, Maybe String)-connectionStr = do- addr <- try ipv6str <|> try ipv4address <|> hostname+connectionStr :: (String -> a) ->+ (String -> a) ->+ (String -> a) -> Parser (a, Maybe String)+connectionStr ipv6Fun ipv4Fun hostFun = do+ addr <- try (ipv6Fun <$> ipv6str)+ <|> try (ipv4Fun <$> ipv4address)+ <|> (hostFun <$> hostname) p <- maybePort return (addr, p) where@@ -161,7 +178,30 @@ return ipv6 maybePort = option Nothing $ char ':' >> Just <$> port + -- | This function will parse it's argument and return either+-- `String` (`Left`) in case of error or (`ConnectionDetail String`, Maybe Port)+-- tuple (`Right`).+--+-- Examples:+--+-- >>> detailedHostAndPort "localhost"+-- Right (HostName "localhost",Nothing)+-- >>> detailedHostAndPort "[::1]:3030"+-- Right (IPv6Address "::1",Just "3030")+-- >>> detailedHostAndPort "127.0.0.1:1080"+-- Right (IPv4Address "127.0.0.1",Just "1080")+--+-- /Since/ 0.2+detailedHostAndPort :: String -> Either String (RemoteAddr, Maybe String)+detailedHostAndPort s = case runParser parser () "" s of+ (Right v) -> Right v+ (Left e) -> Left $ show e+ where+ parser = connectionStr IPv6Address IPv4Address HostName <* eof+++-- | This function will parse it's argument and return either -- `String` (`Left`) with info about error or (Host, `Maybe` Port) -- tuple (`Right`). --@@ -172,9 +212,9 @@ -- >>> hostAndPort "[::1]:3030" -- Right ("::1",Just "3030") hostAndPort :: String -> Either String (String, Maybe String)-hostAndPort s = case runParser (connectionStr <* eof) () "" s of+hostAndPort s = case runParser (connectionStr id id id <* eof) () "" s of (Right v) -> Right v- (Left e) -> Left $ show $ e+ (Left e) -> Left $ show e -- | Function will parse argument and return Maybe (Host, Maybe Port)@@ -186,9 +226,7 @@ -- >>> maybeHostAndPort "192.168.10.12:7272" -- Just ("192.168.10.12",Just "7272") maybeHostAndPort :: String -> Maybe (String, Maybe String)-maybeHostAndPort s = case hostAndPort s of- (Right v) -> Just v- (Left _) -> Nothing+maybeHostAndPort s = either (const Nothing) Just $ hostAndPort s -- | Function will take default port and connection string@@ -206,4 +244,4 @@ defaultHostAndPort :: String -- ^ default Port number -> String -- ^ connection string -> Maybe (String, String) -- ^ Maybe (Host, Port)-defaultHostAndPort p s = (\(h, mp) -> (h, fromMaybe p mp)) <$> maybeHostAndPort s+defaultHostAndPort p s = second (fromMaybe p) <$> maybeHostAndPort s
tests/Spec.hs view
@@ -1,9 +1,14 @@ module Main where+import Text.Printf import Test.Hspec import Network.HostAndPort-import Text.Printf +isLeft :: Either a b -> Bool+isLeft (Left _) = True+isLeft (Right _) = False++ main :: IO () main = hspec spec @@ -90,6 +95,21 @@ it "testing invalid" $ do defaultHostAndPort "60" "localhost:99999" `shouldBe` Nothing defaultHostAndPort "60" "[bad]:25" `shouldBe` Nothing++ describe "detailedHostAndPort" $ do+ it "parsing hosts" $ do+ detailedHostAndPort "localhost" `shouldBe` Right (HostName "localhost", Nothing)+ detailedHostAndPort "some-domain.com" `shouldBe` Right (HostName "some-domain.com", Nothing)+ detailedHostAndPort "42.another-domain.com:3030" `shouldBe` Right (HostName "42.another-domain.com", Just "3030")++ it "parsing ipv6address" $ do+ detailedHostAndPort "[::1]:8080" `shouldBe` Right (IPv6Address "::1", Just "8080")+ detailedHostAndPort "[10::1:f]:8080" `shouldBe` Right (IPv6Address "10::1:f", Just "8080")+ detailedHostAndPort "[::1]:65536" `shouldSatisfy` isLeft++ it "parsing ipv4address" $ do+ detailedHostAndPort "127.0.0.1" `shouldBe` Right (IPv4Address "127.0.0.1", Nothing)+ detailedHostAndPort "127.0.0.1:6060" `shouldBe` Right (IPv4Address "127.0.0.1", Just "6060") where valid f s = it (printf "valid \"%s\"" s) (f s `shouldBe` True) invalid f s = it (printf "invalid \"%s\"" s) (f s `shouldBe` False)