module Network.HTTP.Lucu.Resource
- ( Resource
+ ( ResourceDef(..)
+ , Resource
+ , ResTree
+ , mkResTree -- [ ([String], ResourceDef) ] -> ResTree
+ , findResource -- ResTree -> URI -> Maybe ResourceDef
+ , runResource -- ResourceDef -> Interaction -> IO ThreadId
)
where
-import Control.Monad.State
+import Control.Concurrent
+import Control.Monad.Reader
import qualified Data.ByteString.Lazy.Char8 as B
import Data.ByteString.Lazy.Char8 (ByteString)
+import Data.List
+import qualified Data.Map as M
+import Data.Map (Map)
+import Network.HTTP.Lucu.Interaction
+import Network.HTTP.Lucu.Utils
+import Network.URI
-data ResState = ResState -- FIXME
-type ResourceT m a = StateT ResState m a
+type Resource a = ReaderT Interaction IO a
-type Resource a = ResourceT IO a
+
+{- /aaa/bbb/ccc にアクセスされた時、もし /aaa/bbb に貪欲なリソースがあ
+ れば、假に /aaa/bbb/ccc に何らかのリソースがあったとしても必ず
+ /aaa/bbb が撰ばれる。/aaa/bbb のリソースが貪欲でなければ、それは無視
+ される。 -}
+data ResourceDef = ResourceDef {
+ resUsesNativeThread :: Bool
+ , resIsGreedy :: Bool
+ , resResource :: Resource ()
+ }
+type ResTree = ResNode -- root だから Map ではない
+type ResSubtree = Map String ResNode
+data ResNode = ResNode (Maybe ResourceDef) ResSubtree
+
+
+mkResTree :: [ ([String], ResourceDef) ] -> ResTree
+mkResTree list = processRoot list
+ where
+ processRoot :: [ ([String], ResourceDef) ] -> ResTree
+ processRoot list
+ = let (roots, nonRoots) = partition (\ (path, _) -> path == []) list
+ children = processNonRoot nonRoots
+ in
+ if null roots then
+ -- / にリソースが定義されない。/foo とかにはあるかも。
+ ResNode Nothing children
+ else
+ -- / がある。
+ let (_, def) = last roots
+ in
+ ResNode (Just def) children
+
+ processNonRoot :: [ ([String], ResourceDef) ] -> ResSubtree
+ processNonRoot list
+ = let subtree = M.fromList [(name, node name)
+ | name <- childNames]
+ childNames = [name | (name:_, _) <- list]
+ node name = let defs = [def | (path, def) <- list, path == [name]]
+ in
+ if null defs then
+ -- この位置にリソースが定義されない。
+ -- もっと下にはあるかも。
+ ResNode Nothing children
+ else
+ -- この位置にリソースがある。
+ ResNode (Just $ last defs) children
+ children = processNonRoot [(path, def)
+ | (_:path, def) <- list, not (null path)]
+ in
+ subtree
+
+
+findResource :: ResTree -> URI -> Maybe ResourceDef
+findResource (ResNode rootDefM subtree) uri
+ = let pathStr = uriPath uri
+ path = [x | x <- splitBy (== '/') pathStr, x /= ""]
+ in
+ if null path then
+ rootDefM
+ else
+ walkTree subtree path
+ where
+ walkTree :: ResSubtree -> [String] -> Maybe ResourceDef
+
+ walkTree subtree (name:[])
+ = case M.lookup name subtree of
+ Nothing -> Nothing
+ Just (ResNode defM _) -> defM
+
+ walkTree subtree (x:xs)
+ = case M.lookup x subtree of
+ Nothing -> Nothing
+ Just (ResNode defM children) -> case defM of
+ Just (ResourceDef { resIsGreedy = True })
+ -> defM
+ _ -> walkTree children xs
+
+
+runResource :: ResourceDef -> Interaction -> IO ThreadId
+runResource def itr = fork $ runReaderT rsrc itr -- FIXME: 例外をcatch
+ where
+ fork = if (resUsesNativeThread def)
+ then forkOS
+ else forkIO
+ rsrc = resResource def
\ No newline at end of file