packages feed

salak 0.3.1 → 0.3.2

raw patch · 7 files changed

+120/−77 lines, 7 files

Files

LICENSE view
@@ -1,30 +1,21 @@-Copyright Daniel YU (c) 2018--All rights reserved.--Redistribution and use in source and binary forms, with or without-modification, are permitted provided that the following conditions are met:+MIT License -    * Redistributions of source code must retain the above copyright-      notice, this list of conditions and the following disclaimer.+Copyright (c) 2019 Daniel YU -    * Redistributions in binary form must reproduce the above-      copyright notice, this list of conditions and the following-      disclaimer in the documentation and/or other materials provided-      with the distribution.+Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions: -    * Neither the name of Daniel YU nor the names of other-      contributors may be used to endorse or promote products derived-      from this software without specific prior written permission.+The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
README.md view
@@ -1,16 +1,17 @@ # salak -[![Hackage](https://img.shields.io/hackage/v/salak.svg)](https://hackage.haskell.org/package/salak)+[![Hackage](https://img.shields.io/hackage/v/salak.svg?logo=haskell)](https://hackage.haskell.org/package/salak)+[![Build](https://img.shields.io/travis/leptonyu/salak.svg?logo=travis)](https://travis-ci.org/leptonyu/salak) [![stackage LTS package](http://stackage.org/package/salak/badge/lts)](http://stackage.org/lts/package/salak) [![stackage Nightly package](http://stackage.org/package/salak/badge/nightly)](http://stackage.org/nightly/package/salak)-[![Build Status](https://travis-ci.org/leptonyu/salak.svg?branch=master)](https://travis-ci.org/leptonyu/salak)+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/leptonyu/salak/blob/master/LICENSE)  Configuration (re)loader in Haskell.  ## salak-yaml-[![salak-yaml](https://img.shields.io/hackage/v/salak-yaml.svg)](https://hackage.haskell.org/package/salak-yaml)+[![salak-yaml](https://img.shields.io/hackage/v/salak-yaml.svg?logo=haskell)](https://hackage.haskell.org/package/salak-yaml) ## salak-toml-[![salak-toml](https://img.shields.io/hackage/v/salak-toml.svg)](https://hackage.haskell.org/package/salak-toml)+[![salak-toml](https://img.shields.io/hackage/v/salak-toml.svg?logo=haskell)](https://hackage.haskell.org/package/salak-toml)  ## Introduction This library defines a universal procedure to load configurations and parse properties, also supports reload configuration files.@@ -33,7 +34,7 @@ > 1. loadCommandLine > 2. loadEnvironment > 3. loadConfFiles-> 4. load file from folder `salak.conf.dir` if defined+> 4. load file from folder `application.dir` if defined > 5. load file from current folder if enabled > 6. load file from home folder if enabled > 7. file extension matching, support yaml or toml or any other loader.
salak.cabal view
@@ -1,9 +1,9 @@ cabal-version: 1.12 name: salak-version: 0.3.1-license: BSD3+version: 0.3.2+license: MIT license-file: LICENSE-copyright: (c) 2018 Daniel YU+copyright: 2019 Daniel YU maintainer: Daniel YU <leptonyu@gmail.com> author: Daniel YU homepage: https://github.com/leptonyu/salak#readme
src/Salak.hs view
@@ -1,10 +1,11 @@-{-# LANGUAGE FlexibleInstances   #-}-{-# LANGUAGE NoOverloadedLists   #-}-{-# LANGUAGE OverloadedStrings   #-}-{-# LANGUAGE RankNTypes          #-}-{-# LANGUAGE RecordWildCards     #-}-{-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeOperators       #-}+{-# LANGUAGE FlexibleInstances     #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE NoOverloadedLists     #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RankNTypes            #-}+{-# LANGUAGE RecordWildCards       #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeOperators         #-} -- | -- Module:      Salak -- Copyright:   2019 Daniel YU@@ -36,6 +37,7 @@   , readPrimitive   , readEnum   , SourcePack+  , Salak   , SalakException(..)   -- * Load Functions   -- ** Monad for Loader@@ -67,6 +69,7 @@ import           Control.Monad.IO.Unlift import           Control.Monad.Reader import           Data.Default+import           Data.Maybe import           Data.Text               (Text) import           Salak.Internal import           Salak.Internal.Prop@@ -74,29 +77,40 @@ import           System.Directory import           System.FilePath         ((</>)) ---- | Configuration file name-type FileName = String+-- | Type synonyms of 'SourcePack'+type Salak = SourcePack  -- | Prop load configuration data PropConfig = PropConfig-  { configName    :: Maybe FileName  -- ^ Config name-  , configDirKey  :: Text          -- ^ Specify config dir-  , searchCurrent :: Bool          -- ^ Search current directory, default true+  { configKey     :: Text          -- ^ Specify config key, default is @application@.+  , configName    :: String        -- ^ Specify config name, default is @application@.+  , searchCurrent :: Bool          -- ^ Search current directory, default true.   , searchHome    :: Bool          -- ^ Search home directory, default false.-  , commandLine   :: ParseCommandLine -- ^ How to parse commandline+  , commandLine   :: ParseCommandLine -- ^ How to parse commandline.+  , loggerF       :: LFunc   , loadExt       :: FilePath -> LoadSalak ()   }  instance Default PropConfig where   def = PropConfig-    Nothing-    "salak.conf.dir"+    "application"+    "application"     True     False     defaultParseCommandLine+    putStrLn     (\_ -> return ()) +data FileConfig = FileConfig+  { configNm  :: Maybe String+  , configDir :: Maybe FilePath+  }++instance Monad m => FromProp m FileConfig where+  fromProp = FileConfig+    <$> "name" .?= Nothing+    <*> "dir"  .?= Nothing+ -- | Load file by extension type ExtLoad = (String, FilePath -> LoadSalak ()) @@ -128,21 +142,22 @@ -- loadSalak :: (MonadThrow m, MonadIO m) => PropConfig -> LoadSalakT m () loadSalak PropConfig{..} = do+  setLogF loggerF   loadCommandLine commandLine   loadEnv-  dir <- require configDirKey-  forM_ configName $ forM_-    [ return dir+  FileConfig{..} <- require configKey+  forM_+    [ return configDir     , ifS searchCurrent getCurrentDirectory     , ifS searchHome    getHomeDirectory-    ] . loadConf+    ] (loadConf $ fromMaybe configName configNm)   where     ifS True gxd = Just <$> liftIO gxd     ifS _    _   = return Nothing     loadConf n mf = lift mf >>= mapM_ (liftNT . loadExt . (</> n)) -loadSalakWith :: (MonadThrow m, MonadIO m, HasLoad file) => file -> FileName -> LoadSalakT m ()-loadSalakWith file name = loadSalak def { configName = Just name, loadExt = loadByExt file }+loadSalakWith :: (MonadThrow m, MonadIO m, HasLoad file) => file -> String -> LoadSalakT m ()+loadSalakWith file name = loadSalak def { configName = name, loadExt = loadByExt file }  -- | Standard salak functions, by load and run with `RunSalakT`. loadAndRunSalak :: (MonadThrow m, MonadIO m) => LoadSalakT m () -> RunSalakT m a -> m a@@ -153,7 +168,7 @@ runSalak c = loadAndRunSalak (loadSalak c)  -- | Run salak, load strategy refer to `loadSalakWith`-runSalakWith :: (MonadCatch m, MonadIO m, HasLoad file) => FileName -> file -> RunSalakT m a -> m a+runSalakWith :: (MonadCatch m, MonadIO m, HasLoad file) => String -> file -> RunSalakT m a -> m a runSalakWith name file = loadAndRunSalak (loadSalakWith file name)  -- $use@@ -169,7 +184,7 @@ -- > 1. loadCommandLine -- > 2. loadEnvironment -- > 3. loadConfFiles--- > 4. load file from folder `salak.conf.dir` if defined+-- > 4. load file from folder `application.dir` if defined -- > 5. load file from current folder if enabled -- > 6. load file from home folder if enabled -- > 7. file extension matching, support yaml or toml or any other loader.
src/Salak/Internal.hs view
@@ -78,6 +78,7 @@   {  ref    :: MVar Source   ,  refNo  :: Int   ,  refMap :: HashMap Int String+  ,  lfunc  :: MVar LFunc   ,  qfunc  :: MVar QFunc   ,  update :: MVar (IO ( TraceSource -- Updated Tries                   , IO ()))    -- Confirm action@@ -91,13 +92,21 @@ type LoadSalak = LoadSalakT IO  runLoad :: Monad m => LoadSalakT m a -> UpdateSource -> m a-runLoad (LoadSalakT ma) us = MS.evalStateT ma us+runLoad (LoadSalakT ma) = MS.evalStateT ma  liftNT :: MonadIO m => LoadSalak () -> LoadSalakT m () liftNT a = MS.get >>= liftIO . runLoad a  instance MonadIO m => MonadSalak (LoadSalakT m) where   askSalak = MS.get >>= toSourcePack+  setLogF f = do+    UpdateSource{..} <- MS.get+    liftIO $ void $ swapMVar lfunc f+  logSalak msg = do+    UpdateSource{..} <- MS.get+    liftIO $ do+      f <- readMVar lfunc+      f msg  instance (MonadThrow m, IU.MonadUnliftIO m) => IU.MonadUnliftIO (LoadSalakT m) where   askUnliftIO = do@@ -112,7 +121,7 @@ type RunSalak = RunSalakT IO  runRun :: Monad m => RunSalakT m a -> SourcePack -> m a-runRun (RunSalakT ma) us = runReaderT ma us+runRun (RunSalakT ma) = runReaderT ma  instance Monad m => MonadSalak (RunSalakT m) where   askSalak = RunSalakT ask@@ -125,6 +134,7 @@ -- | Basic loader loadTrie :: (MonadThrow m, MonadIO m) => Bool -> String -> (Int -> IO TraceSource) -> LoadSalakT m () loadTrie canReload name f = do+  logSalak $ "Loading " ++ (if canReload then "[reloadable]" else "") ++ name   UpdateSource{..} <- MS.get   v              <- liftIO $ readMVar ref   ts             <- liftIO $ loadSource f refNo (fmap ([],) v)@@ -132,7 +142,7 @@   if null es     then do       liftIO $ modifyMVar_ update $ \u -> go ts u refNo-      let nut = UpdateSource ref (refNo + 1) (HM.insert refNo name refMap) qfunc update+      let nut = UpdateSource ref (refNo + 1) (HM.insert refNo name refMap) lfunc qfunc update       _ <- liftIO $ swapMVar ref t       MS.put nut     else throwM $ PropException $ unlines es@@ -156,10 +166,11 @@   r <- liftIO $ newMVar T.empty   q <- liftIO $ newMVar $ \s -> Right $ void $ swapMVar r s   u <- liftIO $ newMVar $ return (T.empty, return ())-  runLoad (lm >> MS.get) (UpdateSource r 0 HM.empty q u) >>= toSourcePack+  l <- liftIO $ newMVar $ \_ -> return ()+  runLoad (lm >> MS.get) (UpdateSource r 0 HM.empty l q u) >>= toSourcePack  toSourcePack :: MonadIO m => UpdateSource -> m SourcePack-toSourcePack UpdateSource{..} = liftIO (readMVar ref) >>= \s -> return $ SourcePack s [] qfunc go+toSourcePack UpdateSource{..} = liftIO (readMVar ref) >>= \s -> return $ SourcePack s [] qfunc lfunc go   where     go = do       t        <- readMVar ref@@ -204,7 +215,7 @@ tryLoadFile :: MonadIO m => (FilePath -> LoadSalakT m ()) -> FilePath -> LoadSalakT m () tryLoadFile f file = do   b <- liftIO $ doesFileExist file-  when b $ do-    liftIO $ putStrLn $ "Load " ++ file-    f file+  if b+    then f file+    else logSalak $ "File does not exist, ignore load " ++ file 
src/Salak/Internal/Prop.hs view
@@ -30,7 +30,7 @@ import           Data.Menshen import           Data.Scientific import           Data.Semigroup-import           Data.Text               (Text)+import           Data.Text               (Text, unpack) import qualified Data.Text               as T import qualified Data.Text.Encoding      as TB import qualified Data.Text.Lazy          as TL@@ -72,9 +72,21 @@   askSalak :: m SourcePack    -- | Get reload action which used for reload profiles-  askReload :: MonadSalak m => m (IO ReloadResult)+  askReload :: m (IO ReloadResult)   askReload = reload <$> askSalak +  setLogF :: MonadIO m => (String -> IO ()) -> m ()+  setLogF f = do+    SourcePack{..} <- askSalak+    liftIO $ void $ swapMVar lref f++  logSalak :: MonadIO m => String -> m ()+  logSalak msg = do+    SourcePack{..} <- askSalak+    liftIO $ do+      f <- readMVar lref+      f msg+   -- | Parse properties using `FromProp`. For example:   --   -- > a :: Bool              <- require "bool.key"@@ -88,8 +100,15 @@   require ks = do     sp@SourcePack{..} <- askSalak     case search ks source of-      Left  e     -> throwM $ PropException e-      Right (k,t) -> runProp1 sp { source = t, pref = pref ++ unKeys k} fromProp+      Left  e     -> throwM $ SalakException (unpack ks) (toException $ PropException e)+      Right (k,t) -> do+        v <- runProp2 sp { source = t, pref = pref ++ unKeys k} fromProp+        case v of+          Left  e -> case fromException e of+            Just (SalakException a b) -> throwM $ SalakException a  b+            Just pe                   -> throwM $ SalakException (unpack ks) (toException pe)+            _                         -> throwM $ SalakException (unpack ks) e+          Right x -> return x  instance {-# OVERLAPPABLE #-} (m ~ t m', Monad m', Monad m, MonadTrans t, MonadSalak m') => MonadSalak m where   askSalak = lift askSalak@@ -132,7 +151,10 @@     v <- try fromProp     case v of       Left  e -> case fromException e of-        Just NullException -> return Nothing+        Just NullException         -> return Nothing+        Just (SalakException _ e2) -> case fromException e2 of+          Just NullException -> return Nothing+          _                  -> throwM e         _                  -> throwM e       Right a -> return (Just a) @@ -141,10 +163,7 @@     SourcePack{..} <- ask     v <- try fromProp     return $ case v of-      Left  e -> case fromException e of-        Just (PropException x) -> Left x-        Just NullException     -> Left $ show (Keys pref) ++ " is null"-        _                      -> Left $ show e+      Left  e -> Left $ show (e :: SomeException)       Right a -> Right a  instance {-# OVERLAPPABLE #-} FromProp m a => FromProp m [a] where@@ -178,6 +197,7 @@ data SalakException   = PropException String -- ^ Parse failed   | NullException        -- ^ Not found+  | SalakException String SomeException   deriving Show  instance Exception SalakException@@ -187,13 +207,15 @@   fromString ks = do     sp@SourcePack{..} <- askSalak     case search ks source of-      Left  e     -> throwM $ PropException e+      Left  e     -> throwM $ SalakException+        (if null pref then ks else show (Keys pref) ++ "." ++ ks)+        (toException $ PropException e)       Right (k,t) -> runProp sp { source = t, pref = pref ++ unKeys k} fromProp  notFound :: Monad m => Prop m a notFound = do   SourcePack{..} <- askSalak-  throwM NullException+  throwM $ SalakException (show (Keys pref)) $ toException NullException  err :: Monad m => String -> Prop m a err e = do
src/Salak/Internal/Source.hs view
@@ -23,10 +23,13 @@  type QFunc = Source -> Either String (IO ()) +type LFunc = String -> IO ()+ data SourcePack = SourcePack   { source :: Source   , pref   :: [Key]   , qref   :: MVar QFunc+  , lref   :: MVar LFunc   , reload :: IO ReloadResult   }