]> gitweb @ CieloNegro.org - Lucu.git/commitdiff
Done.
authorPHO <pho@cielonegro.org>
Mon, 19 Dec 2011 14:30:34 +0000 (23:30 +0900)
committerPHO <pho@cielonegro.org>
Mon, 19 Dec 2011 14:30:34 +0000 (23:30 +0900)
Ditz-issue: d23a51590bc111f85646532c9a8538dd04aa20b4

20 files changed:
Data/Attoparsec/Parsable.hs
Lucu.cabal
Network/HTTP/Lucu.hs
Network/HTTP/Lucu/Authentication.hs
Network/HTTP/Lucu/Chunk.hs
Network/HTTP/Lucu/ContentCoding.hs
Network/HTTP/Lucu/ETag.hs
Network/HTTP/Lucu/Headers.hs
Network/HTTP/Lucu/HttpVersion.hs
Network/HTTP/Lucu/Implant.hs
Network/HTTP/Lucu/MIMEParams.hs
Network/HTTP/Lucu/MIMEType.hs
Network/HTTP/Lucu/MIMEType/Guess.hs
Network/HTTP/Lucu/MIMEType/TH.hs [deleted file]
Network/HTTP/Lucu/MultipartForm.hs
Network/HTTP/Lucu/Request.hs
Network/HTTP/Lucu/RequestReader.hs
Network/HTTP/Lucu/Resource.hs
Network/HTTP/Lucu/StaticFile.hs
bugs/issue-d23a51590bc111f85646532c9a8538dd04aa20b4.yaml

index a991607845646e86ba64b38b6775bb84aa24a0e6..d801fb7b3eee1bb066c1348f111c77ba9290aff1 100644 (file)
@@ -16,50 +16,50 @@ import Data.Word
 
 -- |Class of types which have their corresponding parsers.
 --
--- Minimal complete definition: 'parse'
+-- Minimal complete definition: 'parser'
 class Parsable t a where
-    parse :: Parser t a
+    parser :: Parser t a
 
 instance Parsable B.ByteString Word8 where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.anyWord8
+    {-# INLINE CONLIKE parser #-}
+    parser = B.anyWord8
 
 instance Parsable B.ByteString Char where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.anyChar
+    {-# INLINE CONLIKE parser #-}
+    parser = B.anyChar
 
 instance Parsable B.ByteString B.ByteString where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.takeByteString
+    {-# INLINE CONLIKE parser #-}
+    parser = B.takeByteString
 
 instance Parsable B.ByteString LB.ByteString where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.takeLazyByteString
+    {-# INLINE CONLIKE parser #-}
+    parser = B.takeLazyByteString
 
 instance Parsable B.ByteString Double where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.double
+    {-# INLINE CONLIKE parser #-}
+    parser = B.double
 
 instance Parsable B.ByteString Number where
-    {-# INLINE CONLIKE parse #-}
-    parse = B.number
+    {-# INLINE CONLIKE parser #-}
+    parser = B.number
 
 instance Parsable T.Text Char where
-    {-# INLINE CONLIKE parse #-}
-    parse = T.anyChar
+    {-# INLINE CONLIKE parser #-}
+    parser = T.anyChar
 
 instance Parsable T.Text T.Text where
-    {-# INLINE CONLIKE parse #-}
-    parse = T.takeText
+    {-# INLINE CONLIKE parser #-}
+    parser = T.takeText
 
 instance Parsable T.Text LT.Text where
-    {-# INLINE CONLIKE parse #-}
-    parse = T.takeLazyText
+    {-# INLINE CONLIKE parser #-}
+    parser = T.takeLazyText
 
 instance Parsable T.Text Double where
-    {-# INLINE CONLIKE parse #-}
-    parse = T.double
+    {-# INLINE CONLIKE parser #-}
+    parser = T.double
 
 instance Parsable T.Text Number where
-    {-# INLINE CONLIKE parse #-}
-    parse = T.number
+    {-# INLINE CONLIKE parser #-}
+    parser = T.number
index 163b9b70fdfaf34fe5186b679198df4d79d2b65a..effcefd44304ba3cf398d75b24a5d3dd20ba43ac 100644 (file)
@@ -107,7 +107,6 @@ Library
         Network.HTTP.Lucu.MIMEType
         Network.HTTP.Lucu.MIMEType.DefaultExtensionMap
         Network.HTTP.Lucu.MIMEType.Guess
-        Network.HTTP.Lucu.MIMEType.TH
         Network.HTTP.Lucu.MultipartForm
         Network.HTTP.Lucu.Parser.Http
         Network.HTTP.Lucu.Parser
index 876064ce8c14701931f0a040e6e8c511a02783b1..f82617656bb882075697cb2f66ee61bf387e613e 100644 (file)
@@ -81,8 +81,7 @@ import Network.HTTP.Lucu.Dispatcher
 import Network.HTTP.Lucu.ETag
 import Network.HTTP.Lucu.Httpd
 import Network.HTTP.Lucu.MIMEParams
-import Network.HTTP.Lucu.MIMEType hiding (mimeType)
-import Network.HTTP.Lucu.MIMEType.TH
+import Network.HTTP.Lucu.MIMEType
 import Network.HTTP.Lucu.Request
 import Network.HTTP.Lucu.Resource
 import Network.HTTP.Lucu.Response
index 69223f2e1bb82c878c2f1174cc007a44a850f90e..c91aa7ea54dfae12364f2dde106aa4c3b4e89dca 100644 (file)
@@ -12,14 +12,15 @@ module Network.HTTP.Lucu.Authentication
     , Realm
     , UserID
     , Password
-    , authCredential
     )
     where
 import Control.Monad
 import Data.Ascii (Ascii, AsciiBuilder)
 import Data.Attempt
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
 import qualified Data.ByteString.Base64 as B64
+import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as C8
 import Data.Convertible.Base
 import Data.Convertible.Instances.Ascii ()
@@ -64,25 +65,24 @@ deriveAttempts [ ([t| AuthChallenge |], [t| Ascii        |])
                , ([t| AuthChallenge |], [t| AsciiBuilder |])
                ]
 
--- |'Parser' for an 'AuthCredential'.
-authCredential ∷ Parser AuthCredential
-authCredential
-    = do void $ string "Basic"
-         skipMany1 lws
-         b64 ← takeWhile1 base64
-         case C8.break (≡ ':') (B64.decodeLenient b64) of
-           (user, cPassword)
-               | C8.null cPassword
-                   → fail "no colons in the basic auth credential"
-               | otherwise
-                   → do u ← asc user
-                        p ← asc (C8.tail cPassword)
-                        return (BasicAuthCredential u p)
-    where
-      base64 ∷ Char → Bool
-      base64 = inClass "a-zA-Z0-9+/="
+instance Parsable ByteString AuthCredential where
+    parser = do void $ string "Basic"
+                skipMany1 lws
+                b64 ← takeWhile1 base64
+                case C8.break (≡ ':') (B64.decodeLenient b64) of
+                  (user, cPassword)
+                      | C8.null cPassword
+                          → fail "no colons in the basic auth credential"
+                      | otherwise
+                          → do u ← asc user
+                               p ← asc (C8.tail cPassword)
+                               return (BasicAuthCredential u p)
+        where
+          base64 ∷ Char → Bool
+          base64 = inClass "a-zA-Z0-9+/="
 
-      asc ∷ C8.ByteString → Parser Ascii
-      asc bs = case ca bs of
-                 Success as → return as
-                 Failure _  → fail "Non-ascii character in auth credential"
+          asc ∷ C8.ByteString → Parser Ascii
+          asc bs
+              = case ca bs of
+                  Success as → return as
+                  Failure _  → fail "Non-ascii character in auth credential"
index e8c9de41286c4fb3240425843acc67c52e565db5..101ed7436c9b281db152af0ad542137335aef7c0 100644 (file)
@@ -9,6 +9,7 @@ module Network.HTTP.Lucu.Chunk
     where
 import Control.Applicative
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
 import Data.Bits
 import Network.HTTP.Lucu.Headers
 import Network.HTTP.Lucu.Parser.Http
@@ -31,4 +32,4 @@ chunkFooter ∷ Parser ()
 chunkFooter = crlf
 
 chunkTrailer ∷ Parser Headers
-chunkTrailer = headers
+chunkTrailer = parser
index a5f02b13ecf69ac28f15e747016f4af124eb2191..033b48b4d941b6d462cfa4ebac231d0f3f1869d2 100644 (file)
@@ -1,11 +1,11 @@
 {-# LANGUAGE
-    OverloadedStrings
+    FlexibleInstances
+  , MultiParamTypeClasses
+  , OverloadedStrings
   , UnicodeSyntax
   #-}
 module Network.HTTP.Lucu.ContentCoding
     ( AcceptEncoding(..)
-
-    , acceptEncodingList
     , normalizeCoding
     , unnormalizeCoding
     )
@@ -13,6 +13,8 @@ module Network.HTTP.Lucu.ContentCoding
 import Control.Applicative
 import Data.Ascii (CIAscii, toCIAscii)
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import Data.Ord
 import Data.Maybe
 import Network.HTTP.Lucu.Parser.Http
@@ -34,24 +36,28 @@ instance Ord AcceptEncoding where
           q1' = fromMaybe 0 q1
           q2' = fromMaybe 0 q2
 
-acceptEncodingList ∷ Parser [AcceptEncoding]
-acceptEncodingList = listOf accEnc
+instance Parsable ByteString [AcceptEncoding] where
+    {-# INLINE parser #-}
+    parser = listOf parser
 
-accEnc ∷ Parser AcceptEncoding
-accEnc = do coding ← toCIAscii <$> token
-            qVal   ← option Nothing
-                     $ do _ ← string ";q="
-                          q ← qvalue
-                          return $ Just q
-            return $ AcceptEncoding (normalizeCoding coding) qVal
+instance Parsable ByteString AcceptEncoding where
+    {-# INLINE parser #-}
+    parser = do coding ← toCIAscii <$> token
+                qVal   ← option Nothing
+                             $ do _ ← string ";q="
+                                  q ← qvalue
+                                  return $ Just q
+                return $ AcceptEncoding (normalizeCoding coding) qVal
 
 normalizeCoding ∷ CIAscii → CIAscii
+{-# INLINEABLE normalizeCoding #-}
 normalizeCoding coding
     | coding ≡ "x-gzip"     = "gzip"
     | coding ≡ "x-compress" = "compress"
     | otherwise             = coding
 
 unnormalizeCoding ∷ CIAscii → CIAscii
+{-# INLINEABLE unnormalizeCoding #-}
 unnormalizeCoding coding
     | coding ≡ "gzip"     = "x-gzip"
     | coding ≡ "compress" = "x-compress"
index 6d09aee5673634d273d0296623404b5f29704761..b04912002be300ad01ad2b17b9a167caff195ebc 100644 (file)
@@ -12,14 +12,14 @@ module Network.HTTP.Lucu.ETag
     ( ETag(..)
     , strongETag
     , weakETag
-    , eTag
-    , eTagList
     )
     where
 import Control.Applicative
 import Control.Monad
 import Data.Ascii (Ascii, AsciiBuilder)
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import Data.Convertible.Base
 import Data.Convertible.Instances.Ascii ()
 import Data.Convertible.Utils
@@ -81,17 +81,15 @@ weakETag ∷ Ascii → ETag
 {-# INLINE weakETag #-}
 weakETag = ETag True
 
--- |'Parser' for an 'ETag'.
-eTag ∷ Parser ETag
-{-# INLINEABLE eTag #-}
-eTag = do isWeak ← option False (string "W/" *> return True)
-          str    ← quotedStr
-          return $ ETag isWeak str
+instance Parsable ByteString ETag where
+    {-# INLINEABLE parser #-}
+    parser = do isWeak ← option False (string "W/" *> return True)
+                str    ← quotedStr
+                return $ ETag isWeak str
 
--- |'Parser' for a list of 'ETag's.
-eTagList ∷ Parser [ETag]
-{-# INLINEABLE eTagList #-}
-eTagList = do xs ← listOf eTag
-              when (null xs) $
-                  fail "empty list of ETags"
-              return xs
+instance Parsable ByteString [ETag] where
+    {-# INLINEABLE parser #-}
+    parser = do xs ← listOf parser
+                when (null xs) $
+                    fail "empty list of ETags"
+                return xs
index f0e6ad8958bf056c30f41767263469c51a65922f..e6641154adc1d390b1c7cd35bb4d5d5a4c71342d 100644 (file)
@@ -12,7 +12,6 @@
 module Network.HTTP.Lucu.Headers
     ( Headers
     , HasHeaders(..)
-    , headers
     )
     where
 import Control.Applicative hiding (empty)
@@ -21,6 +20,8 @@ import Control.Monad
 import Data.Ascii (Ascii, AsciiBuilder, CIAscii)
 import qualified Data.Ascii as A
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import qualified Data.Collections.Newtype.TH as C
 import Data.Convertible.Base
 import Data.Convertible.Instances.Ascii ()
@@ -138,30 +139,31 @@ deriveAttempts [ ([t| Headers |], [t| Ascii        |])
   field-value の先頭および末尾にある LWS は全て削除され、それ以外の
   LWS は單一の SP に變換される。
 -}
-headers ∷ Parser Headers
-{-# INLINEABLE headers #-}
-headers = do xs ← many header
-             crlf
-             return $ fromFoldable xs
-    where
-      header ∷ Parser (CIAscii, Ascii)
-      header = do name ← cs <$> token
-                  void $ char ':'
-                  skipMany lws
-                  values ← content `sepBy` try lws
-                  skipMany (try lws)
-                  crlf
-                  return (name, joinValues values)
-
-      content ∷ Parser Ascii
-      {-# INLINE content #-}
-      content = A.unsafeFromByteString
-                <$>
-                takeWhile1 (\c → isText c ∧ c ≢ '\x20')
-
-      joinValues ∷ [Ascii] → Ascii
-      {-# INLINE joinValues #-}
-      joinValues = cs
-                   ∘ mconcat
-                   ∘ intersperse (cs ("\x20" ∷ Ascii) ∷ AsciiBuilder)
-                   ∘ (cs <$>)
+instance Parsable ByteString Headers where
+    {-# INLINEABLE parser #-}
+    parser = do xs ← many header
+                crlf
+                return $ fromFoldable xs
+        where
+          header ∷ Parser (CIAscii, Ascii)
+          {-# INLINEABLE header #-}
+          header = do name ← cs <$> token
+                      void $ char ':'
+                      skipMany lws
+                      values ← content `sepBy` try lws
+                      skipMany (try lws)
+                      crlf
+                      return (name, joinValues values)
+
+          content ∷ Parser Ascii
+          {-# INLINEABLE content #-}
+          content = A.unsafeFromByteString
+                    <$>
+                    takeWhile1 (\c → isText c ∧ c ≢ '\x20')
+
+          joinValues ∷ [Ascii] → Ascii
+          {-# INLINEABLE joinValues #-}
+          joinValues = cs
+                       ∘ mconcat
+                       ∘ intersperse (cs ("\x20" ∷ Ascii) ∷ AsciiBuilder)
+                       ∘ (cs <$>)
index 889042728fcdd310811bdfce67659430d1cbab32..983cb503f09d0f85a670af16a387906f3c8d1a02 100644 (file)
@@ -8,13 +8,14 @@
 -- |An internal module for HTTP version numbers.
 module Network.HTTP.Lucu.HttpVersion
     ( HttpVersion(..)
-    , httpVersion
     )
     where
 import Control.Applicative
 import Control.Applicative.Unicode
 import Data.Ascii (Ascii, AsciiBuilder)
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import Data.Convertible.Base
 import Data.Convertible.Instances.Ascii ()
 import Data.Convertible.Utils
@@ -57,8 +58,8 @@ deriveAttempts [ ([t| HttpVersion |], [t| Ascii        |])
                , ([t| HttpVersion |], [t| AsciiBuilder |])
                ]
 
--- |'Parser' for an 'HttpVersion'.
-httpVersion ∷ Parser HttpVersion
-httpVersion = string "HTTP/"
-              *>
-              (HttpVersion <$> decimal ⊛ (char '.' *> decimal))
+instance Parsable ByteString HttpVersion where
+    {-# INLINEABLE parser #-}
+    parser = string "HTTP/"
+             *>
+             (HttpVersion <$> decimal ⊛ (char '.' *> decimal))
index 58e2b2e97e618f92dc77027ef1ba033edb9e9894..1d418c006c730530e09923ace8bc48e16c48ba0b 100644 (file)
@@ -24,10 +24,9 @@ import Data.Digest.Pure.SHA
 import Data.Maybe
 import Data.Time
 import Network.HTTP.Lucu.ETag
-import Network.HTTP.Lucu.MIMEType hiding (mimeType)
+import Network.HTTP.Lucu.MIMEType
 import Network.HTTP.Lucu.MIMEType.DefaultExtensionMap
 import Network.HTTP.Lucu.MIMEType.Guess
-import Network.HTTP.Lucu.MIMEType.TH
 import Network.HTTP.Lucu.Utils
 import Prelude.Unicode
 
index 6f9eb7e1b8a9f4055b0bc878578d6b2679952991..37410330bd25fee7c589f5a1c3b7a234cbce0e18 100644 (file)
@@ -16,7 +16,6 @@
 -- (<http://tools.ietf.org/html/rfc2231>).
 module Network.HTTP.Lucu.MIMEParams
     ( MIMEParams
-    , mimeParams
     )
     where
 import Control.Applicative hiding (empty)
@@ -25,7 +24,9 @@ import Control.Monad.Unicode
 import Data.Ascii (Ascii, CIAscii, AsciiBuilder)
 import qualified Data.Ascii as A
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
 import Data.Bits
+import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as BS
 import Data.Char
 import Data.Collections
@@ -145,34 +146,33 @@ section ∷ ExtendedParam → Integer
 section (InitialEncodedParam {..}) = 0
 section ep                         = epSection ep
 
--- |'Parser' for MIME parameter values.
-mimeParams ∷ Parser MIMEParams
-{-# INLINEABLE mimeParams #-}
-mimeParams = decodeParams =≪ many (try paramP)
+instance Parsable ByteString MIMEParams where
+    {-# INLINEABLE parser #-}
+    parser = decodeParams =≪ many (try parser)
 
-paramP ∷ Parser ExtendedParam
-paramP = do skipMany lws
-            void $ char ';'
-            skipMany lws
-            epm ← nameP
-            void $ char '='
-            case epm of
-              (name, 0, True)
-                  → do (charset, payload) ← initialEncodedValue
-                       return $ InitialEncodedParam name charset payload
-              (name, sect, True)
-                  → do payload ← encodedPayload
-                       return $ ContinuedEncodedParam name sect payload
-              (name, sect, False)
-                  → do payload ← token <|> quotedStr
-                       return $ AsciiParam name sect payload
+instance Parsable ByteString ExtendedParam where
+    parser = do skipMany lws
+                void $ char ';'
+                skipMany lws
+                epm ← name
+                void $ char '='
+                case epm of
+                  (nm, 0, True)
+                      → do (charset, payload) ← initialEncodedValue
+                           return $ InitialEncodedParam nm charset payload
+                  (nm, sect, True)
+                      → do payload ← encodedPayload
+                           return $ ContinuedEncodedParam nm sect payload
+                  (nm, sect, False)
+                      → do payload ← token <|> quotedStr
+                           return $ AsciiParam nm sect payload
 
-nameP ∷ Parser (CIAscii, Integer, Bool)
-nameP = do name      ← (cs ∘ A.unsafeFromByteString) <$>
-                       takeWhile1 (\c → isToken c ∧ c ≢ '*')
-           sect      ← option 0     $ try (char '*' *> decimal  )
-           isEncoded ← option False $ try (char '*' *> pure True)
-           return (name, sect, isEncoded)
+name ∷ Parser (CIAscii, Integer, Bool)
+name = do nm        ← (cs ∘ A.unsafeFromByteString) <$>
+                      takeWhile1 (\c → isToken c ∧ c ≢ '*')
+          sect      ← option 0     $ try (char '*' *> decimal  )
+          isEncoded ← option False $ try (char '*' *> pure True)
+          return (nm, sect, isEncoded)
 
 initialEncodedValue ∷ Parser (CIAscii, BS.ByteString)
 initialEncodedValue
index 250fdbfc4033a3268412e1b933d2bca37f308918..949bc44d2668219cef9b265eecc66b9eacae9774 100644 (file)
@@ -6,28 +6,34 @@
   , RecordWildCards
   , TemplateHaskell
   , UnicodeSyntax
+  , ViewPatterns
   #-}
 -- |Parsing and printing MIME Media Types
 -- (<http://tools.ietf.org/html/rfc2046>).
 module Network.HTTP.Lucu.MIMEType
     ( MIMEType(..)
     , mimeType
-    , mimeTypeList
     )
     where
 import Control.Applicative
+import Control.Monad.Unicode
 import Data.Ascii (Ascii, AsciiBuilder, CIAscii)
+import Data.Attempt
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import Data.Convertible.Base
 import Data.Convertible.Instances.Ascii ()
 import Data.Convertible.Utils
 import Data.Monoid.Unicode
 import Data.Typeable
 import Language.Haskell.TH.Syntax
+import Language.Haskell.TH.Quote
 import Network.HTTP.Lucu.MIMEParams
 import Network.HTTP.Lucu.OrphanInstances ()
 import Network.HTTP.Lucu.Parser
 import Network.HTTP.Lucu.Parser.Http
+import Network.HTTP.Lucu.Utils
 import Prelude.Unicode
 
 -- |A media type, subtype, and parameters.
@@ -65,24 +71,51 @@ deriveAttempts [ ([t| MIMEType |], [t| Ascii        |])
                ]
 
 -- |Parse 'MIMEType' from an 'Ascii'. For MIME type literals, consider
--- using 'Network.HTTP.Lucu.MIMEType.TH.mimeType'.
+-- using 'mimeType' quasi-quoter.
 instance ConvertAttempt Ascii MIMEType where
     {-# INLINEABLE convertAttempt #-}
     convertAttempt str
-        = case parseOnly (finishOff mimeType) (cs str) of
+        = case parseOnly (finishOff parser) (cs str) of
             Right  t → return t
             Left err → fail ("Unparsable MIME Type: " ⧺ cs str ⧺ ": " ⧺ err)
 
--- |'Parser' for an 'MIMEType'.
-mimeType ∷ Parser MIMEType
-{-# INLINEABLE mimeType #-}
-mimeType = do media  ← cs <$> token
-              _      ← char '/'
-              sub    ← cs <$> token
-              params ← mimeParams
-              return $ MIMEType media sub params
+instance Parsable ByteString MIMEType where
+    {-# INLINEABLE parser #-}
+    parser = do media  ← cs <$> token
+                _      ← char '/'
+                sub    ← cs <$> token
+                params ← parser
+                return $ MIMEType media sub params
 
--- |'Parser' for a list of 'MIMEType's.
-mimeTypeList ∷ Parser [MIMEType]
-{-# INLINE mimeTypeList #-}
-mimeTypeList = listOf mimeType
+instance Parsable ByteString [MIMEType] where
+    {-# INLINE parser #-}
+    parser = listOf parser
+
+-- |'QuasiQuoter' for 'MIMEType' literals.
+--
+-- @
+--   textPlain :: 'MIMEType'
+--   textPlain = ['mimeType'| text/plain; charset=\"UTF-8\" |]
+-- @
+mimeType ∷ QuasiQuoter
+mimeType = QuasiQuoter {
+             quoteExp  = (lift =≪) ∘ (parseType =≪) ∘ toAscii
+           , quotePat  = const unsupported
+           , quoteType = const unsupported
+           , quoteDec  = const unsupported
+           }
+    where
+      parseType ∷ Monad m ⇒ Ascii → m MIMEType
+      parseType a
+          = case ca a of
+              Success t → return t
+              Failure e → fail (show e)
+
+      toAscii ∷ Monad m ⇒ String → m Ascii
+      toAscii (trim → s)
+          = case ca s of
+              Success a → return a
+              Failure e → fail (show e)
+
+      unsupported ∷ Monad m ⇒ m α
+      unsupported = fail "Unsupported usage of mimeType quasi-quoter."
index cd178dec2afb18402169b2f316dd8c067f65b88e..3149859026d9a28d3feeb99a2286961f1c74eca6 100644 (file)
@@ -1,6 +1,7 @@
 {-# LANGUAGE
     DeriveDataTypeable
   , GeneralizedNewtypeDeriving
+  , MultiParamTypeClasses
   , TemplateHaskell
   , UnicodeSyntax
   , ViewPatterns
 module Network.HTTP.Lucu.MIMEType.Guess
     ( ExtMap(..)
     , extMap
-    , parseExtMap
     , guessTypeByFileName
     )
     where
 import Control.Applicative
 import Data.Attoparsec.Char8
 import qualified Data.Attoparsec.Lazy as LP
+import Data.Attoparsec.Parsable
 import qualified Data.ByteString.Lazy.Char8 as Lazy
 import Data.Convertible.Base
 import Data.Convertible.Instances.Text ()
@@ -68,54 +69,56 @@ extMap = QuasiQuoter {
            , quoteDec  = const unsupported
          }
     where
+      parseExtMap ∷ Lazy.ByteString → ExtMap
+      parseExtMap = convertUnsafe
+
       unsupported ∷ Monad m ⇒ m α
       unsupported = fail "Unsupported usage of extMap quasi-quoter."
 
--- |Parse Apache @mime.types@.
-parseExtMap ∷ Lazy.ByteString → ExtMap
-parseExtMap src
-    = case LP.parse pairs src of
-        LP.Fail _ eCtx e
-            → error $ "Unparsable extension map: "
-                    ⧺ intercalate ", " eCtx
-                    ⧺ ": "
-                    ⧺ e
-        LP.Done _ xs
-            → case compile xs of
-                 Right m → ExtMap m
-                 Left  e → error ("Duplicate extension: " ⧺ show e)
-    where
-      pairs ∷ Parser [(MIMEType, [Text])]
-      pairs = do skipMany linebreak
-                 xs ← sepBy pair (skipMany1 linebreak)
-                 skipMany linebreak
-                 endOfInput
-                 return xs
-              <?>
-              "pairs"
+instance ConvertAttempt Lazy.ByteString ExtMap where
+    convertAttempt src
+        = case LP.parse pairs src of
+            LP.Fail _ eCtx e
+                → fail $ "Unparsable extension map: "
+                       ⊕ intercalate ", " eCtx
+                       ⊕ ": "
+                       ⊕ e
+            LP.Done _ xs
+                → case compile xs of
+                     Right m → return $ ExtMap m
+                     Left  e → fail $ "Duplicate extension: " ⊕ show e
+        where
+          pairs ∷ Parser [(MIMEType, [Text])]
+          pairs = do skipMany linebreak
+                     xs ← sepBy pair (skipMany1 linebreak)
+                     skipMany linebreak
+                     endOfInput
+                     return xs
+                  <?>
+                  "pairs"
 
-      pair ∷ Parser (MIMEType, [Text])
-      pair = do skipSpace
-                mime ← mimeType
-                skipSpace1
-                exts ← sepBy1 ext $ skipWhile1 (≡ '\x20')
-                return (mime, exts)
-             <?>
-             "pair"
+          pair ∷ Parser (MIMEType, [Text])
+          pair = do skipSpace
+                    mime ← parser
+                    skipSpace1
+                    exts ← sepBy1 ext $ skipWhile1 (≡ '\x20')
+                    return (mime, exts)
+                 <?>
+                 "pair"
 
-      ext ∷ Parser Text
-      ext = (decodeUtf8 <$> takeWhile1 isAlphaNum)
-            <?>
-            "ext"
+          ext ∷ Parser Text
+          ext = (decodeUtf8 <$> takeWhile1 isAlphaNum)
+                <?>
+                "ext"
 
-      linebreak ∷ Parser ()
-      linebreak
-          = ( endOfLine
-              <|>
-              try (skipSpace *> char '#' *> skipManyTill anyChar endOfLine)
-            )
-            <?>
-            "linebreak"
+          linebreak ∷ Parser ()
+          linebreak
+              = ( endOfLine
+                  <|>
+                  try (skipSpace *> char '#' *> skipManyTill anyChar endOfLine)
+                )
+                <?>
+                "linebreak"
 
 compile ∷ Ord k ⇒ [(v, [k])] → Either (k, v, v) (Map k v)
 compile = go (∅) ∘ concat ∘ (tr <$>)
diff --git a/Network/HTTP/Lucu/MIMEType/TH.hs b/Network/HTTP/Lucu/MIMEType/TH.hs
deleted file mode 100644 (file)
index 9e16efc..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-{-# LANGUAGE
-    UnicodeSyntax
-  , ViewPatterns
-  #-}
--- |A module to provide 'QuasiQuoter' for 'MIMEType' literals.
-module Network.HTTP.Lucu.MIMEType.TH
-    ( mimeType
-    )
-    where
-import Control.Monad.Unicode
-import Data.Ascii (Ascii)
-import Data.Attempt
-import Data.Convertible.Base
-import Language.Haskell.TH.Syntax
-import Language.Haskell.TH.Quote
-import Network.HTTP.Lucu.MIMEType hiding (mimeType)
-import Network.HTTP.Lucu.Utils
-import Prelude.Unicode
-
--- |'QuasiQuoter' for 'MIMEType' literals.
---
--- @
---   textPlain :: 'MIMEType'
---   textPlain = ['mimeType'| text/plain; charset=\"UTF-8\" |]
--- @
-mimeType ∷ QuasiQuoter
-mimeType = QuasiQuoter {
-             quoteExp  = (lift =≪) ∘ (parse =≪) ∘ toAscii
-           , quotePat  = const unsupported
-           , quoteType = const unsupported
-           , quoteDec  = const unsupported
-           }
-    where
-      parse ∷ Monad m ⇒ Ascii → m MIMEType
-      parse a
-          = case ca a of
-              Success t → return t
-              Failure e → fail (show e)
-
-      toAscii ∷ Monad m ⇒ String → m Ascii
-      toAscii (trim → s)
-          = case ca s of
-              Success a → return a
-              Failure e → fail (show e)
-
-      unsupported ∷ Monad m ⇒ m α
-      unsupported = fail "Unsupported usage of mimeType quasi-quoter."
index 2d1b3470f1cf62a797a1336c183e8d54999589b9..ecff350619b31c79c1e1b4ff1e960a3ed68cea9a 100644 (file)
@@ -26,6 +26,7 @@ import Data.Ascii (Ascii, CIAscii, AsciiBuilder)
 import Data.Attempt
 import Data.Attoparsec
 import qualified Data.Attoparsec.Lazy as LP
+import Data.Attoparsec.Parsable
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as LS
 import Data.ByteString.Lazy.Search
@@ -40,9 +41,7 @@ import Data.Sequence (Seq)
 import Data.Text (Text)
 import Network.HTTP.Lucu.Headers
 import Network.HTTP.Lucu.MIMEParams
-import Network.HTTP.Lucu.MIMEType (MIMEType)
-import qualified Network.HTTP.Lucu.MIMEType as MT
-import Network.HTTP.Lucu.MIMEType.TH
+import Network.HTTP.Lucu.MIMEType
 import Network.HTTP.Lucu.Parser
 import Network.HTTP.Lucu.Parser.Http
 import Network.HTTP.Lucu.Utils
@@ -87,8 +86,8 @@ deriveAttempts [ ([t| ContDispo |], [t| Ascii        |])
                , ([t| ContDispo |], [t| AsciiBuilder |])
                ]
 
--- |Parse \"multipart/form-data\" and return either @'Left' err@ or
--- @'Right' result@. Note that there are currently the following
+-- |Parse \"multipart/form-data\" to a list of @(name,
+-- formData)@. Note that there are currently the following
 -- limitations:
 --
 --   * Multiple files embedded as \"multipart/mixed\" within the
@@ -97,9 +96,9 @@ deriveAttempts [ ([t| ContDispo |], [t| Ascii        |])
 --   * \"Content-Transfer-Encoding\" is always ignored.
 --
 --   * RFC 2388 (<http://tools.ietf.org/html/rfc2388#section-3>) says
---   that non-ASCII field names are encoded according to the method in
---   RFC 2047 (<http://tools.ietf.org/html/rfc2047>), but they won't
---   be decoded.
+--     that non-ASCII field names are encoded according to the method
+--     in RFC 2047 (<http://tools.ietf.org/html/rfc2047>), but this
+--     function currently doesn't decode them.
 parseMultipartFormData ∷ Ascii -- ^boundary
                        → LS.ByteString -- ^input
                        → Either String [(Ascii, FormData)]
@@ -170,7 +169,7 @@ parsePart boundary src
         defaultCType = [mimeType| text/plain |]
 
 partHeader ∷ Parser Headers
-partHeader = crlf *> headers
+partHeader = crlf *> parser
 
 getContDispo ∷ MonadError String m ⇒ Headers → m ContDispo
 {-# INLINEABLE getContDispo #-}
@@ -188,7 +187,7 @@ getContDispo hdrs
 
 contentDisposition ∷ Parser ContDispo
 contentDisposition
-    = (ContDispo <$> (cs <$> token) ⊛ mimeParams)
+    = (ContDispo <$> (cs <$> token) ⊛ parser)
       <?>
       "contentDisposition"
 
@@ -199,7 +198,7 @@ getContType hdrs
         Nothing
             → return Nothing
         Just str
-            → case parseOnly (finishOff MT.mimeType) $ cs str of
+            → case parseOnly (finishOff parser) $ cs str of
                  Right  d → return $ Just d
                  Left err → throwError $ "malformed Content-Type: "
                                        ⊕ cs str
index 58286dbe6130733fcdc4b19057a061410df43a29..2fcfc9123e3c8e07f343ba3d9211556d4a4c2ed7 100644 (file)
@@ -1,5 +1,6 @@
 {-# LANGUAGE
-    OverloadedStrings
+    MultiParamTypeClasses
+  , OverloadedStrings
   , UnicodeSyntax
   , ViewPatterns
   #-}
@@ -10,13 +11,14 @@ module Network.HTTP.Lucu.Request
     ( Method(..)
     , Request(..)
     , reqMustHaveBody
-    , request
     )
     where
 import Control.Applicative
 import Control.Monad.Unicode
 import Data.Ascii (Ascii)
 import Data.Attoparsec.Char8
+import Data.Attoparsec.Parsable
+import Data.ByteString (ByteString)
 import qualified Data.ByteString.Char8 as C8
 import Network.HTTP.Lucu.Headers
 import Network.HTTP.Lucu.HttpVersion
@@ -61,28 +63,30 @@ reqMustHaveBody (reqMethod → m)
     | m ≡ PUT   = True
     | otherwise = False
 
--- |'Parser' for a 'Request'.
-request ∷ Parser Request
-request = do skipMany crlf
-             (meth, u, ver) ← requestLine
-             hdrs           ← headers
-             return Request {
-                          reqMethod  = meth
-                        , reqURI     = u
-                        , reqVersion = ver
-                        , reqHeaders = hdrs
-                        }
+instance Parsable ByteString Request where
+    {-# INLINEABLE parser #-}
+    parser = do skipMany crlf
+                (meth, u, ver) ← requestLine
+                hdrs           ← parser
+                return Request {
+                             reqMethod  = meth
+                           , reqURI     = u
+                           , reqVersion = ver
+                           , reqHeaders = hdrs
+                           }
 
 requestLine ∷ Parser (Method, URI, HttpVersion)
+{-# INLINEABLE requestLine #-}
 requestLine = do meth ← method
                  sp
                  u ← uri
                  sp
-                 ver ← httpVersion
+                 ver ← parser
                  crlf
                  return (meth, u, ver)
 
 method ∷ Parser Method
+{-# INLINEABLE method #-}
 method = choice
          [ string "OPTIONS" ≫ return OPTIONS
          , string "GET"     ≫ return GET
@@ -96,6 +100,7 @@ method = choice
          ]
 
 uri ∷ Parser URI
+{-# INLINEABLE uri #-}
 uri = do bs ← takeWhile1 (\c → (¬) (isCtl c ∨ c ≡ '\x20'))
          let str = C8.unpack bs
          case parseURIReference str of
index 6c5070b5738c0e54d4bce48f809219f6db97eb93..91aa86dc61be20313b4b672b59806177441d3399 100644 (file)
@@ -16,6 +16,7 @@ import Control.Exception hiding (block)
 import Control.Monad
 import Control.Monad.Trans.Maybe
 import qualified Data.Attoparsec.Lazy as LP
+import Data.Attoparsec.Parsable
 import qualified Data.ByteString as Strict
 import qualified Data.ByteString.Lazy as Lazy
 import Data.Convertible.Base
@@ -94,7 +95,7 @@ acceptRequest ctx@(Context {..}) input
          if Lazy.null input then
              return ()
          else
-             case LP.parse request input of
+             case LP.parse parser input of
                LP.Done input' req → acceptParsableRequest ctx req input'
                LP.Fail _ _ _      → acceptNonparsableRequest ctx
 
index 8585ceaf29ce993f6a1a002ed41c61d3f5c18f1e..41415290961632701c97e8b9bb2c1639ba729e19 100644 (file)
@@ -151,6 +151,7 @@ import Control.Monad.Unicode
 import Data.Ascii (Ascii, CIAscii, AsciiBuilder)
 import Data.Attempt
 import qualified Data.Attoparsec.Char8 as P
+import Data.Attoparsec.Parsable
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as Strict
 import qualified Data.ByteString.Lazy as Lazy
@@ -180,9 +181,7 @@ import Network.HTTP.Lucu.Parser
 import Network.HTTP.Lucu.Request
 import Network.HTTP.Lucu.Resource.Internal
 import Network.HTTP.Lucu.Response
-import Network.HTTP.Lucu.MIMEType (MIMEType(..))
-import qualified Network.HTTP.Lucu.MIMEType as MT
-import Network.HTTP.Lucu.MIMEType.TH
+import Network.HTTP.Lucu.MIMEType
 import Network.HTTP.Lucu.Utils
 import Network.Socket hiding (accept)
 import Network.URI hiding (path)
@@ -265,7 +264,7 @@ getAccept
            Nothing
                → return []
            Just accept
-               → case P.parseOnly (finishOff MT.mimeTypeList) (cs accept) of
+               → case P.parseOnly (finishOff parser) (cs accept) of
                     Right xs → return xs
                     Left  _  → abort $ mkAbortion' BadRequest
                                      $ "Unparsable Accept: " ⊕ cs accept
@@ -293,7 +292,7 @@ getAcceptEncoding
                       -- identity のみが許される。
                       return [("identity", Nothing)]
                  else
-                     case P.parseOnly (finishOff acceptEncodingList) (cs ae) of
+                     case P.parseOnly (finishOff parser) (cs ae) of
                        Right xs → return $ map toTuple $ reverse $ sort xs
                        Left  _  → abort $ mkAbortion' BadRequest
                                         $ "Unparsable Accept-Encoding: " ⊕ cs ae
@@ -317,7 +316,7 @@ getContentType
            Nothing
                → return Nothing
            Just cType
-               → case P.parseOnly (finishOff MT.mimeType) (cs cType) of
+               → case P.parseOnly (finishOff parser) (cs cType) of
                     Right t → return $ Just t
                     Left  _ → abort $ mkAbortion' BadRequest
                                     $ "Unparsable Content-Type: " ⊕ cs cType
@@ -331,7 +330,7 @@ getAuthorization
            Nothing
                → return Nothing
            Just auth
-               → case P.parseOnly (finishOff authCredential) (cs auth) of
+               → case P.parseOnly (finishOff parser) (cs auth) of
                     Right ac → return $ Just ac
                     Left  _  → return Nothing
 
@@ -394,11 +393,11 @@ foundETag tag
                → if value ≡ "*" then
                       return ()
                   else
-                      case P.parseOnly (finishOff eTagList) (cs value) of
+                      case P.parseOnly (finishOff parser) (cs value) of
                         Right tags
                             -- tags の中に一致するものが無ければ
                             -- PreconditionFailed で終了。
-                            → when ((¬) (any (≡ tag) tags))
+                            → when ((¬) (any (≡ tag) (tags ∷ [ETag])))
                                   $ abort
                                   $ mkAbortion' PreconditionFailed
                                   $ "The entity tag doesn't match: " ⊕ cs value
@@ -422,9 +421,9 @@ foundETag tag
                       abort $ mkAbortion' statusForNoneMatch
                             $ "The entity tag matches: *"
                   else
-                      case P.parseOnly (finishOff eTagList) (cs value) of
+                      case P.parseOnly (finishOff parser) (cs value) of
                         Right tags
-                            → when (any (≡ tag) tags)
+                            → when (any (≡ tag) (tags ∷ [ETag]))
                                   $ abort
                                   $ mkAbortion' statusForNoneMatch
                                   $ "The entity tag matches: " ⊕ cs value
index f560ae3494a31ca53a86e33510f2c96b29e6db6d..7d2ff79ac260a843673e40b2064208f5819d42b0 100644 (file)
@@ -22,9 +22,8 @@ import Data.String
 import qualified Data.Text.Encoding as T
 import Network.HTTP.Lucu.Abortion
 import Network.HTTP.Lucu.Config
-import Network.HTTP.Lucu.MIMEType hiding (mimeType)
+import Network.HTTP.Lucu.MIMEType
 import Network.HTTP.Lucu.MIMEType.Guess
-import Network.HTTP.Lucu.MIMEType.TH
 import Network.HTTP.Lucu.Resource
 import Network.HTTP.Lucu.Resource.Internal
 import Network.HTTP.Lucu.Response
index d33391be34730eec52cd955ec7682ced5dbbb2d8..3ff42d372feb4f79eef37f0a738e00fd5a90e0bf 100644 (file)
@@ -5,8 +5,8 @@ type: :task
 component: Lucu
 release: Lucu-1.0
 reporter: PHO <pho@cielonegro.org>
-status: :in_progress
-disposition: 
+status: :closed
+disposition: :fixed
 creation_time: 2011-12-19 13:01:51.401625 Z
 references: []
 
@@ -20,4 +20,8 @@ log_events:
   - PHO <pho@cielonegro.org>
   - changed status from unstarted to in_progress
   - ""
+- - 2011-12-19 14:29:32.083785 Z
+  - PHO <pho@cielonegro.org>
+  - closed with disposition fixed
+  - Done.
 git_branch: