]> gitweb @ CieloNegro.org - Rakka.git/blobdiff - Rakka/SystemConfig.hs
The experiment has succeeded
[Rakka.git] / Rakka / SystemConfig.hs
index 966ecf10d6c6ca1c2e4dc1af5a7d02279b9dfd9e..9e8be67c5bd59bf16cfdc9f56e12e02e5564dfaa 100644 (file)
@@ -6,6 +6,12 @@ module Rakka.SystemConfig
 
     , getSysConf
     , getSysConfA
+
+    , SiteName(..)
+    , BaseURI(..)
+    , DefaultPage(..)
+    , StyleSheet(..)
+    , Languages(..)
     )
     where
 
@@ -13,6 +19,7 @@ import           Control.Arrow.ArrowIO
 import           Control.Concurrent.STM
 import           Control.Monad.Trans
 import qualified Data.ByteString.Char8 as C8
+import           Data.Dynamic
 import           Data.Encoding
 import           Data.Encoding.UTF8
 import           Data.Map (Map)
@@ -38,17 +45,15 @@ logger = "Rakka.SystemConfig"
 data SystemConfig = SystemConfig {
       scLucuConf   :: !LC.Config
     , scRepository :: !Repository
-    , scCache      :: !(TVar (Map FilePath SysConfValue))
+    , scCache      :: !(TVar (Map FilePath Dynamic))
     }
 
 
-data SysConfValue
-    = SiteName String
-    | BaseURI URI
-    | DefaultPage String
-    | StyleSheet String
-    | Languages (Map LanguageTag LanguageName)
-    deriving (Eq, Show)
+class (Typeable a, Show a) => SysConfValue a where
+    confPath     :: a -> FilePath
+    serialize    :: a -> String
+    deserialize  :: String -> Maybe a
+    defaultValue :: SystemConfig -> a
 
 
 mkSystemConfig :: LC.Config -> Repository -> IO SystemConfig
@@ -60,25 +65,24 @@ mkSystemConfig lc repos
                     , scCache      = cache
                     }
 
-
-getSysConf :: MonadIO m => SystemConfig -> SysConfValue -> m SysConfValue
-getSysConf sc key
+getSysConf :: forall m a. (MonadIO m, SysConfValue a) => SystemConfig -> m a
+getSysConf sc
     = liftIO $
       atomically $
-      do let path = sysConfPath key
+      do let path = confPath (undefined :: a)
 
          cache <- readTVar (scCache sc)
 
          case M.lookup path cache of
-           Just val -> return val
-           Nothing  -> do val <- unsafeIOToSTM (getSysConf' sc key)
-                          writeTVar (scCache sc) (M.insert path val cache)
+           Just val -> return $ fromJust $ fromDynamic val
+           Nothing  -> do val <- unsafeIOToSTM (getSysConf' sc)
+                          writeTVar (scCache sc) (M.insert path (toDyn val) cache)
                           return val
 
 
-getSysConf' :: SystemConfig -> SysConfValue -> IO SysConfValue
-getSysConf' sc key
-    = do let path = fromConfPath (sysConfPath key)
+getSysConf' :: forall a. SysConfValue a => SystemConfig -> IO a
+getSysConf' sc
+    = do let path = fromConfPath $ confPath (undefined :: a)
 
          fs    <- getRepositoryFS (scRepository sc)
          rev   <- getYoungestRev fs
@@ -93,104 +97,103 @@ getSysConf' sc key
 
          case value of
            Just str
-               -> do let val = unmarshalSysConf key str
-                     debugM logger ("Got a config value at `" ++ path ++ "': " ++ show val)
-                     return val
+               -> case deserialize str of
+                    Just val
+                        -> do debugM logger ("Got a config value at `" ++ path ++ "': " ++ show val)
+                              return val
+                    Nothing
+                        -> fail ("Got an invalid config value at `" ++ path ++ "': " ++ str)
            Nothing
-               -> do val <- sysConfDefault sc key
+               -> do let val = defaultValue sc
                      debugM logger ("Got no config value at `" ++ path ++ "'. Defaulting to " ++ show val)
                      return val
 
 
-getSysConfA :: ArrowIO a => SystemConfig -> SysConfValue -> a b SysConfValue
-getSysConfA = (arrIO0 .) . getSysConf
+getSysConfA :: (ArrowIO a, SysConfValue c) => SystemConfig -> a b c
+getSysConfA = arrIO0 . getSysConf
 
 
 fromConfPath :: FilePath -> FilePath
 fromConfPath = combine "/config"
 
 
-marshalStringPairs :: [(String, String)] -> String
-marshalStringPairs = joinWith "\n" . map marshalPair'
+serializeStringPairs :: [(String, String)] -> String
+serializeStringPairs = joinWith "\n" . map serializePair'
     where
-      marshalPair' :: (String, String) -> String
-      marshalPair' (a, b) = a ++ " " ++ b
+      serializePair' :: (String, String) -> String
+      serializePair' (a, b) = a ++ " " ++ b
 
 
-unmarshalStringPairs :: String -> [(String, String)]
-unmarshalStringPairs = catMaybes . map unmarshalPair' . lines
+deserializeStringPairs :: String -> Maybe [(String, String)]
+deserializeStringPairs = sequence . map deserializePair' . lines
     where
-      unmarshalPair' :: String -> Maybe (String, String)
-      unmarshalPair' s = case break (/= ' ') s of
-                           (a, ' ':b) -> Just (a, b)
-                           _          -> Nothing
-
-
-{- paths -}
-sysConfPath :: SysConfValue -> FilePath
-sysConfPath (SiteName    _) = "siteName"
-sysConfPath (BaseURI     _) = "baseURI"
-sysConfPath (DefaultPage _) = "defaultPage"
-sysConfPath (StyleSheet  _) = "styleSheet"
-sysConfPath (Languages   _) = "languages"
-
-
-{- marshalling -}
-marshalSysConf :: SysConfValue -> String
-marshalSysConf (SiteName    name ) = name
-marshalSysConf (BaseURI     uri  ) = uriToString id uri ""
-marshalSysConf (DefaultPage name ) = name
-marshalSysConf (StyleSheet  name ) = name
-marshalSysConf (Languages   langs) = marshalStringPairs (M.toList langs)
-
-
-{- unmarshalling -}
-unmarshalSysConf :: SysConfValue -> String -> SysConfValue
-unmarshalSysConf (SiteName    _) name  = SiteName name
-unmarshalSysConf (BaseURI     _) uri   = BaseURI $ fromJust $ parseURI uri
-unmarshalSysConf (DefaultPage _) name  = DefaultPage name
-unmarshalSysConf (StyleSheet  _) name  = StyleSheet name
-unmarshalSysConf (Languages   _) langs = Languages $ M.fromList $ unmarshalStringPairs langs
-
-
-{- getting default value -}
-sysConfDefault :: SystemConfig -> SysConfValue -> IO SysConfValue
-
-sysConfDefault _ (SiteName _)
-    = return $ SiteName "Rakka"
-
-sysConfDefault sc (BaseURI _)
-    = do let conf = scLucuConf sc
-             host = C8.unpack $ LC.cnfServerHost conf
-             port = case LC.cnfServerPort conf of
-                      PortNumber num -> fromIntegral num
-             
-             defaultURI
+      deserializePair' :: String -> Maybe (String, String)
+      deserializePair' s = case break (/= ' ') s of
+                             (a, ' ':b) -> Just (a, b)
+                             _          -> Nothing
+
+
+
+{- config values -}
+
+newtype SiteName = SiteName String deriving (Show, Typeable)
+instance SysConfValue SiteName where
+    confPath _                = "siteName"
+    serialize (SiteName name) = name
+    deserialize name          = Just (SiteName name)
+    defaultValue _            = SiteName "Rakka"
+
+
+newtype BaseURI = BaseURI URI deriving (Show, Typeable)
+instance SysConfValue BaseURI where
+    confPath _              = "baseURI"
+    serialize (BaseURI uri) = uriToString id uri ""
+    deserialize uri         = fmap BaseURI (parseURI uri)
+    defaultValue sc
+        = let conf = scLucuConf sc
+              host = C8.unpack $ LC.cnfServerHost conf
+              port = case LC.cnfServerPort conf of
+                       PortNumber num -> fromIntegral num
+
+              defaultURI
                   = "http://" ++ host ++
                     (if port == 80
                      then ""
                      else ':' : show port) ++ "/"
-
-         return $ BaseURI $ fromJust $ parseURI defaultURI
-
-sysConfDefault _ (DefaultPage _)
-    = return $ DefaultPage "MainPage"
-
-sysConfDefault _ (StyleSheet _)
-    = return $ StyleSheet "StyleSheet/Default"
-
-sysConfDefault _ (Languages _)
-    = return
-      $ Languages
-      $ M.fromList [ ("en", "English"  )
-                   , ("es", "Español"  )
-                   , ("de", "Deutsch"  )
-                   , ("fi", "Suomi"    )
-                   , ("fr", "Français" )
-                   , ("ga", "Gaeilge"  )
-                   , ("gd", "Gàidhlig" )
-                   , ("ja", "日本語"  )
-                   , ("pt", "Português")
-                   , ("sv", "Svenska"  )
-                   ]
-
+          in
+            BaseURI $ fromJust $ parseURI defaultURI
+
+
+newtype DefaultPage = DefaultPage String deriving (Show, Typeable)
+instance SysConfValue DefaultPage where
+    confPath _                   = "defaultPage"
+    serialize (DefaultPage name) = name
+    deserialize name             = Just (DefaultPage name)
+    defaultValue _               = DefaultPage "MainPage"
+
+
+newtype StyleSheet = StyleSheet String deriving (Show, Typeable)
+instance SysConfValue StyleSheet where
+    confPath _                  = "styleSheet"
+    serialize (StyleSheet name) = name
+    deserialize name            = Just (StyleSheet name)
+    defaultValue _              = StyleSheet "StyleSheet/Default"
+
+
+newtype Languages = Languages (Map LanguageTag LanguageName) deriving (Show, Typeable)
+instance SysConfValue Languages where
+    confPath _                  = "languages"
+    serialize (Languages langs) = serializeStringPairs (M.toList langs)
+    deserialize langs           = fmap (Languages . M.fromList) (deserializeStringPairs langs)
+    defaultValue _         
+        = Languages $ M.fromList [ ("en", "English"  )
+                                 , ("es", "Español"  )
+                                 , ("de", "Deutsch"  )
+                                 , ("fi", "Suomi"    )
+                                 , ("fr", "Français" )
+                                 , ("ga", "Gaeilge"  )
+                                 , ("gd", "Gàidhlig" )
+                                 , ("ja", "日本語"  )
+                                 , ("pt", "Português")
+                                 , ("sv", "Svenska"  )
+                                 ]