6 -- |Utility functions used internally in this package.
7 module Network.HTTP.Lucu.Utils
10 , parseWWWFormURLEncoded
22 import Control.Applicative hiding (empty)
23 import Control.Monad hiding (mapM)
24 import Data.Ascii (Ascii, AsciiBuilder)
25 import qualified Data.Ascii as A
26 import Data.ByteString (ByteString)
27 import qualified Data.ByteString.Char8 as Strict
29 import Data.Collections
30 import Data.Collections.BaseInstances ()
31 import Data.Monoid.Unicode
34 import Data.Time.Clock.POSIX
36 import Prelude hiding (last, mapM, null, reverse)
37 import Prelude.Unicode
38 import System.Directory
39 import System.Time (ClockTime(..))
41 -- |>>> splitBy (== ':') "ab:c:def"
43 splitBy ∷ (a → Bool) → [a] → [[a]]
44 {-# INLINEABLE splitBy #-}
46 = case break isSep src of
48 (first, _sep:rest) → first : splitBy isSep rest
50 -- |>>> quoteStr "abc"
53 -- >>> quoteStr "ab\"c"
55 quoteStr ∷ Ascii → AsciiBuilder
56 quoteStr str = A.toAsciiBuilder "\"" ⊕
57 go (A.toByteString str) (∅) ⊕
60 go ∷ Strict.ByteString → AsciiBuilder → AsciiBuilder
62 = case Strict.break (≡ '"') bs of
68 (ab ⊕ b2ab x ⊕ A.toAsciiBuilder "\\\"")
70 b2ab ∷ Strict.ByteString → AsciiBuilder
71 b2ab = A.toAsciiBuilder ∘ A.unsafeFromByteString
73 -- |>>> parseWWWFormURLEncoded "aaa=bbb&ccc=ddd"
74 -- [("aaa", "bbb"), ("ccc", "ddd")]
75 parseWWWFormURLEncoded ∷ Ascii → [(ByteString, ByteString)]
76 parseWWWFormURLEncoded src
77 -- THINKME: We could gain some performance by using attoparsec
80 | otherwise = do pairStr ← splitBy (\ c → c ≡ ';' ∨ c ≡ '&') (A.toString src)
81 let (key, value) = break (≡ '=') pairStr
83 , unescape $ case value of
88 unescape ∷ String → ByteString
89 unescape = Strict.pack ∘ unEscapeString ∘ (plusToSpace <$>)
91 plusToSpace ∷ Char → Char
95 -- |>>> splitPathInfo "http://example.com/foo/bar"
97 splitPathInfo ∷ URI → [ByteString]
99 = let reqPathStr = uriPath uri
100 reqPath = [unEscapeString x | x ← splitBy (≡ '/') reqPathStr, (¬) (null x)]
102 Strict.pack <$> reqPath
104 -- |>>> trim " ab c d "
106 trim ∷ String → String
107 trim = reverse ∘ f ∘ reverse ∘ f
109 f = dropWhile isSpace
112 -- | (B2;) = ('<|')
114 -- U+22B2, NORMAL SUBGROUP OF
115 (⊲) ∷ Sequence α a ⇒ a → α → α
119 -- | (B3;) = ('|>')
121 -- U+22B3, CONTAINS AS NORMAL SUBGROUP
122 (⊳) ∷ Sequence α a ⇒ α → a → α
126 -- | (C8;) = ('><')
129 (⋈) ∷ Sequence α a ⇒ α → α → α
132 -- |Generalised @mapM@ from any 'Foldable' to 'Unfoldable'. Why isn't
133 -- this in the @collections-api@?
134 mapM ∷ (Foldable α a, Unfoldable β b, Functor m, Monad m)
135 ⇒ (a → m b) → α → m β
137 mapM = flip foldrM empty ∘ (flip ((<$>) ∘ flip insert) ∘)
139 -- |Get the modification time of a given file.
140 getLastModified ∷ FilePath → IO UTCTime
141 getLastModified = (clockTimeToUTC <$>) ∘ getModificationTime
143 clockTimeToUTC ∷ ClockTime → UTCTime
144 clockTimeToUTC (TOD sec picoSec)
145 = posixSecondsToUTCTime ∘ fromRational
146 $ sec % 1 + picoSec % (1000 ⋅ 1000 ⋅ 1000 ⋅ 1000)