packages feed

ip 1.4.1 → 1.4.2

raw patch · 11 files changed

+266/−43 lines, 11 filesdep ~hspecdep ~quickcheck-classesnew-uploader

Dependency ranges changed: hspec, quickcheck-classes

Files

ip.cabal view
@@ -1,5 +1,6 @@+cabal-version: 2.0 name: ip-version: 1.4.1+version: 1.4.2 synopsis: Library for IP and MAC addresses homepage: https://github.com/andrewthad/haskell-ip#readme license: BSD3@@ -9,7 +10,6 @@ copyright: 2016 Andrew Martin category: web build-type: Simple-cabal-version: >=1.10 description:   The `ip` package provides types and functions for dealing with   IPv4 addresses, CIDR blocks, and MAC addresses. We provide instances@@ -68,7 +68,7 @@     , test-framework     , test-framework-quickcheck2     , QuickCheck-    , quickcheck-classes >= 0.4.13 && < 0.5+    , quickcheck-classes >= 0.4.13 && < 0.7.0.0     , text     , bytestring     , HUnit@@ -89,13 +89,13 @@   build-depends:       base     , ip-    , hspec-    , hspec >= 2.4.4+    , hspec >= 2.7   other-modules:     Net.IPv4Spec     Net.IPv4.RangeSpec   ghc-options: -Wall -O2   default-language: Haskell2010+  build-tool-depends: hspec-discover:hspec-discover >= 2.7  test-suite doctest   type: exitcode-stdio-1.0
src/Data/ByteString/Builder/Fixed.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-}@@ -22,7 +23,9 @@   , word12HexFixedUpper   ) where +#if !MIN_VERSION_base(4,11,0) import Data.Monoid+#endif import Data.Word import Data.Word.Synthetic.Word12 (Word12) import Data.Bits
src/Data/Text/Builder/Fixed.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE OverloadedStrings #-}@@ -22,7 +23,9 @@   ) where  import Control.Monad.ST+#if !MIN_VERSION_base(4,11,0) import Data.Monoid+#endif import Data.Word import Data.Bits import Data.Char (ord)
src/Data/Text/Builder/Variable.hs view
@@ -8,7 +8,7 @@     than the builder provided by the @text@ library. However,     data whose textual encoding has no known upper bound cannot     be encoded by the builder provided here. For example, it-    is possible to provide decimal builders for types like 'Int8' and+    is possible to provide decimal builders for types like 'Data.Int.Int8' and     'Word16', whose lengths are respectively bounded by     4 and 5. However, this is not possible for 'Integer', since     its decimal representation could be arbitrarily long.
src/Net/IP.hs view
@@ -60,6 +60,15 @@ import qualified Data.Aeson as Aeson import qualified Data.Text.IO as TIO +-- | Run a function over an 'IP' depending on its status+--   as an 'IPv4' or 'IPv6'.+--+--   >>> case_ IPv4.encode IPv6.encode (ipv4 192 168 2 47)+--   "192.168.2.47"+--+--   >>> addr = ipv6 0x2001 0x0db8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0001+--   >>> case_ IPv4.encode IPv6.encode addr+--   "2001:db8::1" case_ :: (IPv4 -> a) -> (IPv6 -> a) -> IP -> a case_ f g (IP addr@(IPv6 w1 w2)) = if w1 == 0 && (0xFFFFFFFF00000000 .&. w2 == 0x0000FFFF00000000)   then f (IPv4 (fromIntegral w2))@@ -77,15 +86,19 @@      -> IP ipv6 a b c d e f g h = fromIPv6 (IPv6.fromWord16s a b c d e f g h) +-- | Turn an 'IPv4' into an 'IP'. fromIPv4 :: IPv4 -> IP fromIPv4 (IPv4 w) = IP (IPv6 0 (0x0000FFFF00000000 .|. fromIntegral w)) +-- | Turn an 'IPv6' into an 'IP'. fromIPv6 :: IPv6 -> IP fromIPv6 = IP +-- | Encode an 'IP' as 'Text'. encode :: IP -> Text encode = case_ IPv4.encode IPv6.encode +-- | Decode an 'IP' from 'Text'. decode :: Text -> Maybe IP decode t = case IPv4.decode t of   Nothing -> case IPv6.decode t of@@ -93,6 +106,7 @@     Just v6 -> Just (fromIPv6 v6)   Just v4 -> Just (fromIPv4 v4) +-- | Print an 'IP' using the textual encoding. print :: IP -> IO () print = TIO.putStrLn . encode 
src/Net/IPv4.hs view
@@ -1,25 +1,14 @@ {-# LANGUAGE BangPatterns #-}-{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE CPP #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}-{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeInType #-}-{-# LANGUAGE CPP #-}--{-# OPTIONS_GHC -Wall #-}-{-| An IPv4 data type--    This module provides the IPv4 data type and functions for working-    with it. There are also encoding and decoding functions provided-    in this module, but they should be imported from-    @Net.IPv4.Text@ and @Net.ByteString.Char8@ instead. They are-    defined here so that the 'FromJSON' and 'ToJSON' instances can-    use them.+{-# LANGUAGE UnboxedTuples #-} -    At some point, a highly efficient IPv4-to-ByteString function needs-    to be added to this module to take advantage of @aeson@'s new-    @toEncoding@ method.+{-| This module provides the IPv4 data type and functions for working+    with it. -}  module Net.IPv4@@ -31,6 +20,7 @@     -- * Special IP Addresses   , any   , loopback+  , localhost   , broadcast     -- * Range Predicates   , private@@ -174,15 +164,31 @@   , fromIntegral w   ) --- | The IP address representing any host: @0.0.0.0@+-- | The IP address representing any host.+--+--   >>> any+--   ipv4 0 0 0 0 any :: IPv4 any = IPv4 0 --- | The local loopback IP address: @127.0.0.1@+-- | The local loopback IP address.+--+--   >>> loopback+--   ipv4 127 0 0 1 loopback :: IPv4 loopback = fromOctets 127 0 0 1 --- | The broadcast IP address: @255.255.255.255@+-- | A useful and common alias for 'loopback'.+--+--   >>> localhost+--   ipv4 127 0 0 1+localhost :: IPv4+localhost = loopback++-- | The broadcast IP address.+--+--   >>> broadcast+--   ipv4 255 255 255 255 broadcast :: IPv4 broadcast = fromOctets 255 255 255 255 @@ -255,20 +261,46 @@ encode = toDotDecimalText  -- | Decode an 'IPv4' address.+--+--   >>> decode (Text.pack "192.168.2.47")+--   Just (ipv4 192 168 2 47)+--+--   >>> decode (Text.pack "10.100.256.256")+--   Nothing decode :: Text -> Maybe IPv4 decode = decodeIPv4TextMaybe  -- | Encode an 'IPv4' address to a text 'TBuilder.Builder'.+--+--   >>> builder (ipv4 192 168 2 47)+--   "192.168.2.47" builder :: IPv4 -> TBuilder.Builder builder = toDotDecimalBuilder +-- | Parse an 'IPv4' address using a 'TextRead.Reader'.+--+--   >>> reader (Text.pack "192.168.2.47")+--   Right (ipv4 192 168 2 47,"")+--+--   >>> reader (Text.pack "192.168.2.470")+--   Left "All octets in an IPv4 address must be between 0 and 255" reader :: TextRead.Reader IPv4 reader = decodeIPv4TextReader +-- | Parse an 'IPv4' address using a 'AT.Parser'.+--+--   >>> AT.parseOnly parser (Text.pack "192.168.2.47")+--   Right (ipv4 192 168 2 47)+--+--   >>> AT.parseOnly parser (Text.pack "192.168.2.470")+--   Left "Failed reading: All octets in an IPv4 address must be between 0 and 255" parser :: AT.Parser IPv4 parser = dotDecimalParser  -- | Encode an 'IPv4' address to a UTF-8 encoded 'ByteString'.+--+--   >>> encodeUtf8 (ipv4 192 168 2 47)+--   "192.168.2.47" encodeUtf8 :: IPv4 -> ByteString encodeUtf8 = toBSPreAllocated @@ -311,14 +343,26 @@               poke ptr (word + 48)               return 1 --- This should be rewritten to not go through text--- as an intermediary.+-- | Decode a UTF8-encoded 'ByteString' into an 'IPv4'.+--+--   >>> decodeUtf8 (BC8.pack "192.168.2.47")+--   Just (ipv4 192 168 2 47) decodeUtf8 :: ByteString -> Maybe IPv4 decodeUtf8 = decode <=< rightToMaybe . decodeUtf8'+-- This (decodeUtf8) should be rewritten to not go through text+-- as an intermediary. +-- | Encode an 'IPv4' as a 'Builder.Builder' builderUtf8 :: IPv4 -> Builder.Builder builderUtf8 = Builder.byteString . encodeUtf8 +-- | Parse an 'IPv4' using a 'AB.Parser'.+--+--   >>> AB.parseOnly parserUtf8 (BC8.pack "192.168.2.47")+--   Right (ipv4 192 168 2 47)+--+--   >>> AB.parseOnly parserUtf8 (BC8.pack "192.168.2.470")+--   Left "Failed reading: All octets in an ipv4 address must be between 0 and 255" parserUtf8 :: AB.Parser IPv4 parserUtf8 = fromOctets'   <$> (AB.decimal >>= limitSize)@@ -344,9 +388,11 @@  -} +-- | Encode an 'IPv4' as a 'String'. encodeString :: IPv4 -> String encodeString = Text.unpack . encode +-- | Decode an 'IPv4' from a 'String'. decodeString :: String -> Maybe IPv4 decodeString = decode . Text.pack @@ -382,6 +428,7 @@     d <- step readPrec     return (fromOctets a b c d) +-- | Print an 'IPv4' using the textual encoding. print :: IPv4 -> IO () print = TIO.putStrLn . encode @@ -745,6 +792,10 @@ lowerInclusive (IPv4Range (IPv4 w) len) =   IPv4 (w .&. mask len) +-- | The inclusive upper bound of an 'IPv4Range'.+--+--   >>> T.putStrLn $ encode $ upperInclusive $ IPv4Range (ipv4 10 10 1 160) 25+--   10.10.1.255 upperInclusive :: IPv4Range -> IPv4 upperInclusive (IPv4Range (IPv4 w) len) =   let theInvertedMask = shiftR 0xffffffff (fromIntegral len)@@ -786,6 +837,8 @@   let totalAddrs = countAddrs len    in wordSuccessors totalAddrs ip +-- | A stream-polymorphic generator over an 'IPv4Range'.+--   For more information, see <http://www.haskellforall.com/2014/11/how-to-build-library-agnostic-streaming.html How to build library-agnostic streaming sources>. toGenerator :: MonadPlus m => IPv4Range -> m IPv4 toGenerator (IPv4Range ip len) =   let totalAddrs = countAddrs len@@ -827,15 +880,19 @@       w' = w .&. mask len'    in IPv4Range (IPv4 w') len' +-- | Encode an 'IPv4Range' as 'Text'. encodeRange :: IPv4Range -> Text encodeRange = rangeToDotDecimalText +-- | Decode an 'IPv4Range' from 'Text'. decodeRange :: Text -> Maybe IPv4Range decodeRange = rightToMaybe . AT.parseOnly (parserRange <* AT.endOfInput) +-- | Encode an 'IPv4Range' to a 'TBuilder.Builder'. builderRange :: IPv4Range -> TBuilder.Builder builderRange = rangeToDotDecimalBuilder +-- | Parse an 'IPv4Range' using a 'AT.Parser.' parserRange :: AT.Parser IPv4Range parserRange = do   ip <- parser
src/Net/IPv6.hs view
@@ -8,8 +8,9 @@ {-# LANGUAGE StandaloneDeriving  #-} {-# LANGUAGE UnboxedTuples       #-} -{-# OPTIONS_GHC -Wall #-}-+{-| This module provides the IPv6 data type and functions for working+    with it.+-} module Net.IPv6   ( -- * Convert     ipv6@@ -23,6 +24,7 @@     -- * Special IP Addresses   , any   , loopback+  , localhost     -- * Textual Conversion     -- ** Text   , encode@@ -228,6 +230,7 @@ internal_ m s = case internal m s of   (# s', _ #) -> s' +-- | Print an 'IPv6' using the textual encoding. print :: IPv6 -> IO () print = TIO.putStrLn . encode @@ -269,6 +272,14 @@ rightToMaybe :: Either a b -> Maybe b rightToMaybe = either (const Nothing) Just +-- | This could be useful for the rare occasion+--   in which one could construct an 'IPv6' from+--   octets.+--+--   Note that while @Net.IPv4.'Net.IPv4.fromOctets' = Net.IPv4.'Net.IPv4.ipv4'@,+--   @Net.IPv6.fromOctets /= Net.IPv6.ipv6@. While this should be obvious+--   from their types, it is worth mentioning since the similarity in naming+--   might be confusing. fromOctets ::      Word8 -> Word8 -> Word8 -> Word8   -> Word8 -> Word8 -> Word8 -> Word8@@ -349,9 +360,24 @@   , fromIntegral b   ) +-- | The local loopback IP address.+--+--   >>> loopback+--   ipv6 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0001 loopback :: IPv6 loopback = IPv6 0 1 +-- | A useful alias for 'loopback'.+--+--   >>> localhost+--   ipv6 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0001+localhost :: IPv6+localhost = loopback++-- | The IP address representing any host.+--   +--   >>> any +--   ipv6 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 any :: IPv6 any = IPv6 0 0 @@ -397,6 +423,11 @@ decode :: Text -> Maybe IPv6 decode t = rightToMaybe (AT.parseOnly (parser <* AT.endOfInput) t) +-- | Parse an 'IPv6' using 'Atto.Parser'.+--+--   >>> ip = ipv6 0xDEAD 0xBEEF 0x3240 0xA426 0xBA68 0x1CD0 0x4263 0x109B+--   >>> Atto.parseOnly parser (Text.pack "dead:beef:3240:a426:ba68:1cd0:4263:109b")+--   Right (ipv6 0xdead 0xbeef 0x3240 0xa426 0xba68 0x1cd0 0x4263 0x109b) parser :: Atto.Parser IPv6 parser = makeIP <$> ip   where@@ -496,6 +527,8 @@ fromWord32Word64 :: Word64 -> Word64 -> Word64 fromWord32Word64 a b = fromIntegral (unsafeShiftL a 32 .|. b) +-- | An 'IPv6Range'. It is made up of the first 'IPv6' in the range+--   and its length. data IPv6Range = IPv6Range   { ipv6RangeBase   :: {-# UNPACK #-} !IPv6   , ipv6RangeLength :: {-# UNPACK #-} !Word8@@ -508,6 +541,25 @@   then 0xffffffffffffffff    else complement (shiftR 0xffffffffffffffff (fromIntegral w)) +-- | Normalize an 'IPv6Range'. The first result of this is that the+--   'IPv6' inside the 'IPv6Range' is changed so that the insignificant+--   bits are zeroed out. For example:+--+--   >>> addr1 = ipv6 0x0192 0x0168 0x0001 0x0019 0x0000 0x0000 0x0000 0x0000+--   >>> addr2 = ipv6 0x0192 0x0168 0x0001 0x0163 0x0000 0x0000 0x0000 0x0000+--   >>> printRange $ normalize $ IPv6Range addr1 24+--   192:100::/24 +--   >>> printRange $ normalize $ IPv6Range addr2 28+--   192:160::/28+--+--   The second effect of this is that the mask length is lowered to be 128+--   or smaller. Working with 'IPv6Range's that have not been normalized does+--   not cause any issues for this library, although other applications may+--   reject such ranges (especially those with a mask length above 128).+--+--   Note that 'normalize is idempotent, that is:+--+--   prop> normalize r == (normalize . normalize) r normalize :: IPv6Range -> IPv6Range normalize (IPv6Range (IPv6 w1 w2) len) =   let len' = min len 128@@ -516,12 +568,23 @@         | otherwise =  (IPv6Range (IPv6 (w1 .&. mask 64) (w2 .&. mask (len' - 64))) len')   in norm +-- | Encode an 'IPv6Range' as 'Text'.+--+--   >>> addr = ipv6 0xDEAD 0xBEEF 0x3240 0xA426 0xBA68 0x1CD0 0x4263 0x109B+--   >>> T.putStrLn $ encodeRange $ IPv6Range addr 28+--   dead:beef:3240:a426:ba68:1cd0:4263:109b/28 encodeRange :: IPv6Range -> Text encodeRange x = encode (ipv6RangeBase x) <> Text.pack "/" <> (Text.pack $ (show . fromEnum) $ ipv6RangeLength x) +-- | Decode an 'IPv6Range' from 'Text'.+--+--   >>> addr = ipv6 0xDEAD 0xBEEF 0x3240 0xA426 0xBA68 0x1CD0 0x4263 0x109B+--   >>> fmap encodeRange $ decodeRange (Text.pack "dead:beef:3240:a426:ba68:1cd0:4263:109b/28")+--   Just "dead:bee0::/28" decodeRange :: Text -> Maybe IPv6Range decodeRange = rightToMaybe . AT.parseOnly (parserRange <* AT.endOfInput) +-- | Parse an 'IPv6Range' using a 'AT.Parser'. parserRange :: AT.Parser IPv6Range parserRange = do   ip <- parser@@ -581,7 +644,7 @@ -- >>> T.putStrLn $ encode $ lowerInclusive $ IPv6Range (ipv6 0x2001 0x0db8 0x0000 0x0000 0x0000 0x0000 0x0000 0x0001) 25 -- 2001:d80:: ----- Note that the lower bound of a normalized 'IPv4Range' is simply the+-- Note that the lower bound of a normalized 'IPv6Range' is simply the -- ip address of the range: -- -- prop> lowerInclusive r == ipv6RangeBase (normalize r)@@ -589,6 +652,12 @@ lowerInclusive (IPv6Range (IPv6 w1 w2) len) =   ipv6RangeBase (normalize (IPv6Range (IPv6 w1 w2) len)) +-- | The inclusive upper bound of an 'IPv6Range'.+--+--   >>> let addr = ipv6 0xDEAD 0xBEEF 0x3240 0xA426 0xBA68 0x1CD0 0x4263 0x109B+--   >>> T.putStrLn $ encode $ upperInclusive $ IPv6Range addr 25+--   dead:beff:ffff:ffff:ffff:ffff:ffff:ffff+-- upperInclusive :: IPv6Range -> IPv6 upperInclusive (IPv6Range (IPv6 w1 w2) len) =   let len' = min 128 len@@ -600,13 +669,29 @@         | otherwise =  IPv6 (w1) (w2 .|. theInvertedMask2)   in upper --- | This exists mostly for testing purposes.+-- | Print an 'IPv6Range' using the textual encoding. printRange :: IPv6Range -> IO () printRange = TIO.putStrLn . encodeRange +-- | Smart constructor for 'IPv6Range'. Ensures the mask is appropriately+--   sized and sets masked bits in the 'IPv6' to zero.+--+--   >>> let addr = ipv6 0xDEAD 0xBEEF 0x3240 0xA426 0xBA68 0x1CD0 0x4263 0x109B+--   >>> printRange $ range addr 25+--   dead:be80::/25 range :: IPv6 -> Word8 -> IPv6Range range addr len = normalize (IPv6Range addr len) +-- | Given an inclusive lower and upper ip address, create the smallest 'IPv6Range'+--   that contains the two. This is helpful in situations where input is given as a+--   range, like @ @.+--   +--   This makes the range broader if it cannot be represented in <https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR> notation.+--+--   >>> addrLower = ipv6 0xDEAD 0xBE80 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000+--   >>> addrUpper = ipv6 0xDEAD 0xBEFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF 0xFFFF+--   >>> printRange $ fromBounds addrLower addrUpper+--   dead:be80::/25 fromBounds :: IPv6 -> IPv6 -> IPv6Range fromBounds (IPv6 a1 a2) (IPv6 b1 b2) =   normalize (IPv6Range (IPv6 a1 a2) (maskFromBounds a1 b1 a2 b2))
src/Net/Mac.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE MagicHash #-} {-# LANGUAGE MultiParamTypeClasses #-}@@ -7,10 +9,9 @@ {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UnboxedTuples #-} -{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE GeneralizedNewtypeDeriving #-}--{-# OPTIONS_GHC -Wall #-}+{-| This module provides the Mac data type and functions for working+    with it.+-} module Net.Mac   ( -- * Convert     mac@@ -80,6 +81,7 @@ import qualified Data.Text.Builder.Fixed as TFB import qualified Data.Text.IO as TIO import qualified Data.Text.Lazy.Builder as TBuilder+import qualified Data.Text as Text ()  -- $setup --@@ -144,14 +146,21 @@ c2w :: Char -> Word8 c2w = fromIntegral . ord --- | Encode a 'Mac' address lowercase hex, separating every two characters---   with a colon:+-- | Encode a 'Mac' address using the default 'MacCodec' 'defCodec'. -- --   >>> T.putStrLn (encode (Mac 0xA47F247AB423)) --   a4:7f:24:7a:b4:23 encode :: Mac -> Text encode = encodeWith defCodec +-- | Encode a 'Mac' address using the given 'MacCodec'.+--+--   >>> m = Mac 0xA47F247AB423+--   >>> T.putStrLn $ encodeWith defCodec m+--   a4:7f:24:7a:b4:23+--+--   >>> T.putStrLn $ encodeWith (MacCodec (MacGroupingTriples '-') True) m+--   A47-F24-7AB-423 encodeWith :: MacCodec -> Mac -> Text encodeWith (MacCodec g u) m = case g of   MacGroupingNoSeparator -> case u of@@ -168,18 +177,49 @@     True -> TFB.run (fixedBuilderQuadruples TFB.word8HexFixedUpper) (Pair c m)     False -> TFB.run (fixedBuilderQuadruples TFB.word8HexFixedLower) (Pair c m) +-- | Decode a 'Mac' address using the default 'MacCodec' 'defCodec'.+--+--   >>> decode (Text.pack "a4:7f:24:7a:b4:23")+--   Just (mac 0xa47f247ab423)+--+--   >>> decode (Text.pack "a47-f24-7ab-423")+--   Nothing decode :: Text -> Maybe Mac decode = decodeWith defCodec +-- | Decode a 'Mac' address from 'Text' using the given 'MacCodec'.+--+-- >>> decodeWith defCodec (Text.pack "a4:7f:24:7a:b4:23")+-- Just (mac 0xa47f247ab423)+--+-- >>> decodeWith (MacCodec MacGroupingNoSeparator False) (Text.pack "a47f247ab423")+-- Just (mac 0xa47f247ab423) decodeWith :: MacCodec -> Text -> Maybe Mac decodeWith codec t = rightToMaybe (AT.parseOnly (parserWith codec <* AT.endOfInput) t) +-- | Encode a 'Mac' address as a 'TBuilder.Builder'. builder :: Mac -> TBuilder.Builder builder = TBuilder.fromText . encode +-- | Parse a 'Mac' address using a 'AT.Parser'.+--+--   >>> AT.parseOnly parser (Text.pack "a4:7f:24:7a:b4:23")+--   Right (mac 0xa47f247ab423)+--+--   >>> AT.parseOnly parser (Text.pack "a47-f24-7ab-423")+--   Left "':': Failed reading: satisfy" parser :: AT.Parser Mac parser = parserWith defCodec +-- | Parser a 'Mac' address using the given 'MacCodec'.+--+--   >>> p1 = parserWith defCodec+--   >>> AT.parseOnly p1 (Text.pack "a4:7f:24:7a:b4:23")+--   Right (mac 0xa47f247ab423)+--+--   >>> p2 = parserWith (MacCodec MacGroupingNoSeparator False)+--   >>> AT.parseOnly p2 (Text.pack "a47f247ab423")+--   Right (mac 0xa47f247ab423) parserWith :: MacCodec -> AT.Parser Mac parserWith (MacCodec g _) = case g of   MacGroupingQuadruples c -> parserQuadruples c@@ -187,6 +227,10 @@   MacGroupingPairs c -> parserPairs c   MacGroupingNoSeparator -> parserNoSeparator +-- | The default 'MacCodec': all characters are lowercase hex, separated by colons into pairs.+--+--   >>> T.putStrLn $ encodeWith defCodec (Mac 0xa47f247ab423)+--   a4:7f:24:7a:b4:23 defCodec :: MacCodec defCodec = MacCodec (MacGroupingPairs ':') False @@ -320,8 +364,7 @@ word12At i (Mac w) = fromIntegral (unsafeShiftR w i) {-# INLINE word12At #-} --- | Encode a 'Mac' address, as lowercase hexadecimal digits---   separated by a colon:+-- | Encode a 'Mac' address using the default 'MacCodec' 'defCodec'. -- --   >>> BC.putStrLn (encodeUtf8 (mac 0x64255A0F2C47)) --   64:25:5a:0f:2c:47@@ -329,7 +372,7 @@ encodeUtf8 = encodeWithUtf8 defCodec  -- | Lenient decoding of MAC address that accepts lowercase, uppercase,---   and any kind separator.+--   and any kind of separator. -- --   >>> decodeUtf8 "A2:DE:AD:BE:EF:67" --   Just (mac 0xa2deadbeef67)@@ -340,6 +383,13 @@ decodeUtf8 :: ByteString -> Maybe Mac decodeUtf8 = decodeLenientUtf8 +-- | Decode a 'ByteString' as a 'Mac' address using the given 'MacCodec'.+--+--   >>> decodeWithUtf8 defCodec (BC.pack "64:25:5a:0f:2c:47")+--   Just (mac 0x64255a0f2c47)+--+--   >>> decodeWithUtf8 (MacCodec MacGroupingNoSeparator False) (BC.pack "64255a0f2c47")+--   Just (mac 0x64255a0f2c47) decodeWithUtf8 :: MacCodec -> ByteString -> Maybe Mac decodeWithUtf8 codec bs = rightToMaybe (AB.parseOnly (parserWithUtf8 codec <* AB.endOfInput) bs) @@ -468,6 +518,14 @@ parseWord8Hex :: Word8 -> AB.Parser Word8 parseWord8Hex = tryParseWord8Hex (fail "invalid hexadecimal character") +-- | Encode a 'Mac' address as a 'ByteString' using the given 'MacCodec'.+--+--   >>> m = Mac 0xA47F247AB423+--   >>> BC.putStrLn $ encodeWithUtf8 defCodec m+--   a4:7f:24:7a:b4:23+--+--   >>> BC.putStrLn $ encodeWithUtf8 (MacCodec (MacGroupingTriples '-') True) m+--   A47-F24-7AB-423 encodeWithUtf8 :: MacCodec -> Mac -> ByteString encodeWithUtf8 (MacCodec g u) m = case g of   MacGroupingNoSeparator -> case u of@@ -657,6 +715,7 @@   toEnum i = Mac (toEnum i)   fromEnum (Mac x) = fromEnum x +-- | Print a 'Mac' address using the textual encoding. print :: Mac -> IO () print = TIO.putStrLn . encode @@ -673,6 +732,8 @@   | w < 10 = chr (fromIntegral (w + 48))   | otherwise = chr (fromIntegral (w + 87)) +-- | A 'MacCodec' allows users to control the encoding/decoding+--   of their 'Mac' addresses. data MacCodec = MacCodec   { macCodecGrouping :: !MacGrouping   , macCodecUpperCase :: !Bool
src/Net/Types.hs view
@@ -1,5 +1,5 @@-{-# OPTIONS_GHC -Wall -Werror #-}-+{-| This module re-exports all of the thematic types that this library defines.+-} module Net.Types   ( IPv4(..)   , IPv6(..)
test/Doctests.hs view
@@ -10,4 +10,5 @@   , "src/Data/Text/Builder/Fixed.hs"   , "src/Data/ByteString/Builder/Fixed.hs"   , "src/Net/Mac.hs"+  , "-XCPP"   ]
test/Net/IPv4/RangeSpec.hs view
@@ -3,7 +3,6 @@ import Prelude hiding (any) import Data.Bits import Net.IPv4-import Net.IPv4.Range import Test.Hspec  spec :: Spec