, deleteIfEmpty
, chomp
, guessMIMEType
- , isSafeChar
, mkQueryString
)
where
-import qualified Codec.Binary.Url as Url
import Control.Arrow
import Control.Arrow.ArrowList
+import Data.Ascii (Ascii)
+import qualified Data.Ascii as A
import qualified Data.ByteString as BS
+import qualified Data.ByteString.Char8 as C8
+import qualified Data.ByteString.Unsafe as BS
import qualified Data.ByteString.Lazy as LS
-import qualified Data.ByteString.Lazy.Char8 as L8
-import Data.List
+import Data.Char
import qualified Data.Text as T
import Data.Text.Encoding
+import Magic
import Network.HTTP.Lucu
import Network.URI
+import Numeric
import Prelude.Unicode
import System.IO.Unsafe
-
yesOrNo ∷ Bool → String
yesOrNo True = "yes"
yesOrNo False = "no"
-
trueOrFalse ∷ Bool → String
trueOrFalse True = "true"
trueOrFalse False = "false"
-
-parseYesOrNo ∷ ArrowChoice a => a String Bool
+parseYesOrNo ∷ ArrowChoice a ⇒ a String Bool
parseYesOrNo
= proc str → do case str of
- "yes" → returnA -< True
- "no" → returnA -< False
- _ → returnA -< error ("Expected yes or no: " ⧺ str)
+ "yes" → returnA ⤙ True
+ "no" → returnA ⤙ False
+ _ → returnA ⤙ error ("Expected yes or no: " ⧺ str)
-
-maybeA ∷ (ArrowList a, ArrowChoice a) => a b c → a b (Maybe c)
+maybeA ∷ (ArrowList a, ArrowChoice a) ⇒ a b c → a b (Maybe c)
maybeA a = listA a
>>>
proc xs → case xs of
- [] → returnA -< Nothing
- (x:_) → returnA -< Just x
-
+ [] → returnA ⤙ Nothing
+ (x:_) → returnA ⤙ Just x
-deleteIfEmpty ∷ (ArrowList a, ArrowChoice a) => a String String
+deleteIfEmpty ∷ (ArrowList a, ArrowChoice a) ⇒ a String String
deleteIfEmpty
= proc str → do case str of
- "" → none -< ()
- _ → returnA -< str
-
+ "" → none ⤙ ()
+ _ → returnA ⤙ str
chomp ∷ String → String
-chomp = reverse . snd . break (/= '\n') . reverse
-
+{-# INLINE chomp #-}
+chomp = reverse . snd . break (≢ '\n') . reverse
guessMIMEType ∷ LS.ByteString → MIMEType
-guessMIMEType = read . unsafePerformIO . magicString magic . L8.unpack
+{-# INLINEABLE guessMIMEType #-}
+guessMIMEType = read
+ ∘ unsafePerformIO
+ ∘ flip BS.unsafeUseAsCStringLen (magicCString magic)
+ ∘ BS.concat
+ ∘ LS.toChunks
where
magic ∷ Magic
+ {-# NOINLINE magic #-}
magic = unsafePerformIO
- $ do m <- magicOpen [MagicMime]
+ $ do m ← magicOpen [MagicMime]
magicLoadDefault m
return m
-
+{-
isSafeChar ∷ Char → Bool
+{-# INLINEABLE isSafeChar #-}
isSafeChar c
| c ≡ '/' = True
| isReserved c = False
- | isUnreserved c = True
+ | c > ' ' ∧ c ≤ '~' = True
| otherwise = False
+-}
-
-mkQueryString ∷ [(T.Text, T.Text)] → String
+mkQueryString ∷ [(T.Text, T.Text)] → Ascii
{-# INLINEABLE mkQueryString #-}
-mkQueryString = intercalate ";" ∘ map pairToStr
+mkQueryString = A.unsafeFromByteString
+ ∘ BS.intercalate (C8.singleton ';')
+ ∘ map encodePair
where
- pairToStr ∷ (T.Text, T.Text) → String
- {-# INLINE pairToStr #-}
- pairToStr (k, v)
- = encode k ⧺ ('=':encode v)
-
- encode ∷ T.Text → String
- {-# INLINE encode #-}
- encode = Url.encode ∘ BS.unpack ∘ encodeUtf8
+ encodePair ∷ (T.Text, T.Text) → BS.ByteString
+ {-# INLINE encodePair #-}
+ encodePair (k, v)
+ = BS.intercalate (C8.singleton '=') [encodeText k, encodeText v]
+
+ encodeText ∷ T.Text → BS.ByteString
+ {-# INLINE encodeText #-}
+ encodeText = toURLEncoded ∘ encodeUtf8
+
+toURLEncoded ∷ BS.ByteString → BS.ByteString
+{-# INLINEABLE toURLEncoded #-}
+toURLEncoded = C8.concatMap go
+ where
+ go ∷ Char → BS.ByteString
+ {-# INLINE go #-}
+ go c | c ≡ ' ' = C8.singleton '+'
+ | isReserved c = urlEncode c
+ | isUnreserved c = C8.singleton c
+ | otherwise = urlEncode c
+
+ urlEncode ∷ Char → BS.ByteString
+ {-# INLINE urlEncode #-}
+ urlEncode c = C8.pack ('%':toHex (ord c))
+
+ toHex ∷ Int → String
+ {-# INLINE toHex #-}
+ toHex n
+ = case showIntAtBase 16 toChrHex n "" of
+ [] → "00"
+ [c] → ['0', c]
+ cs → cs
+
+ toChrHex ∷ Int → Char
+ {-# INLINE toChrHex #-}
+ toChrHex d
+ | d < 10 = chr (ord '0' + fromIntegral d )
+ | otherwise = chr (ord 'A' + fromIntegral (d-10))