on master help sign in
executable-config: support inject on all platforms
commit076587ab6efdf81d6cdc04ea4d1ef73d0767b576 authorJames Deikun <james.deikun@obsidian.systems> dateFri 29 Mar 2019, 13:31 (8 years ago) parent20ba787
12 files changed+185−39
lib/executable-config/default.nix+1 lib/executable-config/inject/obelisk-executable-config-inject.cabal+1−1 lib/executable-config/inject/src/Obelisk/ExecutableConfig/Inject.hs+3−20 lib/executable-config/lookup/obelisk-executable-config.cabal+3 lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig.hs+2−15 lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig/Internal.hs+62 lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig.hs+7−3 lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig/Internal.hs+32 lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig.hs+2 lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig/Internal.hs+37 lib/executable-config/lookup/src/Obelisk/ExecutableConfig.hs+3 lib/executable-config/lookup/src/Obelisk/ExecutableConfig/Internal.hs+32
lib/executable-config/default.nix+1
@@ -16,6 +16,7 @@ let injectConfig = config: assets: nixpkgs.runCommand "inject-config" {} (''
1616 exit 1
1717 fi
1818 cp -a "${config}"/* "$out/config"
19+ (cd $out && find config -type f -print0 >config.files)
1920 '');
2021in with nixpkgs.haskell.lib; {
2122 haskellPackage = self: self.callPackage (filterGitSource ./lookup) {};
lib/executable-config/inject/obelisk-executable-config-inject.cabal+1−1
@@ -9,8 +9,8 @@ library
99 build-depends:
1010 base,
1111 bytestring,
12− directory,
1312 filepath,
13+ obelisk-executable-config,
1414 reflex-dom-core,
1515 text
1616
lib/executable-config/inject/src/Obelisk/ExecutableConfig/Inject.hs+3−20
@@ -8,9 +8,8 @@ import Data.ByteString (ByteString)
88import Data.Text (Text)
99import qualified Data.Text as T
1010import qualified Data.Text.IO as T
11+import Obelisk.ExecutableConfig.Internal (getFrontendConfigs)
1112import Reflex.Dom.Core hiding (value)
12−import System.Directory
13−import System.FilePath ((</>))
1413
1514-- | Produces a @<script>@ tag containing the contents of the config item,
1615-- rendered as a 'ByteString'.
@@ -31,21 +30,5 @@ injectPure key value =
3130-- values.
3231injectExecutableConfigs :: (MonadIO m, DomBuilder t m) => m ()
3332injectExecutableConfigs = do
34− cfgC <- getConfigs "config/common"
35− cfgF <- getConfigs "config/frontend"
36− mapM_ (uncurry injectPure) (cfgC <> cfgF)
37−
38−getConfigs :: MonadIO m => FilePath -> m [(Text, Text)]
39−getConfigs fp = liftIO $ do
40− dir <- doesDirectoryExist fp
41− if dir
42− then do
43− ps <- listDirectory fp
44− fmap concat $ mapM (\p -> getConfigs $ fp </> p) ps
45− else do
46− file <- doesFileExist fp
47− if file
48− then do
49− f <- T.readFile fp
50− return [(T.pack fp, f)]
51− else return []
33+ cfg <- liftIO getFrontendConfigs
34+ mapM_ (uncurry injectPure) cfg
lib/executable-config/lookup/obelisk-executable-config.cabal+3
@@ -5,9 +5,11 @@ build-type: Simple
55
66library
77 exposed-modules: Obelisk.ExecutableConfig
8+ exposed-modules: Obelisk.ExecutableConfig.Internal
89 if os(ios)
910 hs-source-dirs: src-ios
1011 build-depends: jsaddle-wkwebview
12+ build-depends: directory
1113 else
1214 if os(linux-android)
1315 hs-source-dirs: src-android
@@ -21,6 +23,7 @@ library
2123 build-depends: ghcjs-dom
2224 else
2325 hs-source-dirs: src
26+ build-depends: directory
2427
2528 build-depends:
2629 base,
lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig.hs+2−15
@@ -1,27 +1,14 @@
11module Obelisk.ExecutableConfig (get) where
22
33import Control.Exception (bracket)
4−import qualified Data.ByteString as BS
54import Data.Text (Text)
65import qualified Data.Text as T
7−import qualified Data.Text.Encoding as T
86import Foreign.C.String (withCString)
9−import Foreign.Ptr (nullPtr)
10−import System.FilePath.Posix ((</>))
117
8+import Obelisk.ExecutableConfig.Internal
129import Obelisk.ExecutableConfig.Internal.AssetManager
1310
1411get :: Text -> IO (Maybe Text)
1512get name = bracket getAssets freeAssetManager $ \mgrObj -> do
1613 mgr <- assetManagerFromJava mgrObj
17− let open = do
18− a <- withCString (T.unpack name) $ \fn ->
19− assetManager_open mgr fn 3
20− return $ if unAAsset a == nullPtr
21− then Nothing
22− else Just a
23− close = mapM_ asset_close
24− bracket open close $ mapM $ \asset -> do
25− b <- asset_getBuffer asset
26− l <- asset_getLength asset
27− fmap T.decodeUtf8 $ BS.packCStringLen (b, fromIntegral l)
14+ getFromMgr mgr (withCString . T.unpack) name
lib/executable-config/lookup/src-android/Obelisk/ExecutableConfig/Internal.hsnew file+62
@@ -0,0 +1,62 @@
1+{-# LANGUAGE LambdaCase #-}
2+module Obelisk.ExecutableConfig.Internal where
3+
4+import Control.Exception (bracket)
5+import Control.Monad (forM)
6+import qualified Data.ByteString as BS
7+import Data.List (sortOn)
8+import Data.Maybe (catMaybes, maybe)
9+import Data.Text (Text)
10+import qualified Data.Text as T
11+import qualified Data.Text.Encoding as T
12+import Foreign.C.String (withCString)
13+import Foreign.Ptr (nullPtr)
14+import System.FilePath.Posix ((</>))
15+
16+import Obelisk.ExecutableConfig.Internal.AssetManager
17+
18+getFromMgr mgr withCS name = do
19+ let open = do
20+ a <- withCS name $ \fn ->
21+ assetManager_open mgr fn 3
22+ return $ if unAAsset a == nullPtr
23+ then Nothing
24+ else Just a
25+ close = mapM_ asset_close
26+ bracket open close $ mapM $ \asset -> do
27+ b <- asset_getBuffer asset
28+ l <- asset_getLength asset
29+ fmap T.decodeUtf8 $ BS.packCStringLen (b, fromIntegral l)
30+
31+getFrontendConfigs :: IO [(Text, Text)]
32+getFrontendConfigs
33+ = fmap (sortOn fst)
34+ $ getConfigs
35+ $ \fp -> BS.isPrefixOf (BS.pack $ fmap (fromIntegral . fromEnum) "config/common/") fp
36+ || BS.isPrefixOf (BS.pack $ fmap (fromIntegral . fromEnum) "config/frontend/") fp
37+
38+getConfigs :: (BS.ByteString -> Bool) -> IO [(Text, Text)]
39+getConfigs p = bracket getAssets freeAssetManager $ \mgrObj -> do
40+ mgr <- assetManagerFromJava mgrObj
41+ let openDir = do
42+ d <- withCString "config.files" $ \fn ->
43+ assetManager_open mgr fn 3
44+ return $ if unAAsset d == nullPtr
45+ then Nothing
46+ else Just d
47+ closeDir = mapM_ asset_close
48+ configPaths <- bracket openDir closeDir $ \case
49+ Just asset -> do
50+ b <- asset_getBuffer asset
51+ l <- asset_getLength asset
52+ lines0 <$> BS.packCStringLen (b, fromIntegral l)
53+ Nothing -> error "could not open configuration manifest 'config.files'"
54+ fmap catMaybes $ forM (filter p configPaths) $ \fp ->
55+ fmap (\v -> (T.decodeUtf8 fp,v)) <$> getFromMgr mgr BS.useAsCString fp
56+
57+lines0 :: BS.ByteString -> [BS.ByteString]
58+lines0 ps
59+ | BS.null ps = []
60+ | otherwise = case BS.elemIndex 0 ps of
61+ Nothing -> [ps]
62+ Just n -> BS.take n ps : lines0 (BS.drop (n+1) ps)
lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig.hs+7−3
@@ -2,11 +2,16 @@
22module Obelisk.ExecutableConfig where
33
44import Control.Monad.Trans.Maybe
5+import Data.List (sortOn)
6+import Data.Maybe (catMaybes)
57import Data.Semigroup ((<>))
68import Data.Text (Text)
9+import qualified Data.Text as T
10+import Data.Traversable (for)
711import GHCJS.DOM
8−import GHCJS.DOM.Document
9−import GHCJS.DOM.Element
12+import GHCJS.DOM.Document (getHead)
13+import GHCJS.DOM.Element (Element, getElementsByTagName, getId, getInnerHTML)
14+import GHCJS.DOM.HTMLCollection (HTMLCollection, item, getLength)
1015import GHCJS.DOM.NonElementParentNode
1116
1217get :: Text -> IO (Maybe Text)
@@ -14,4 +19,3 @@ get item = runMaybeT $ do
1419 doc <- MaybeT currentDocument
1520 e <- MaybeT $ getElementById doc $ "config-" <> item
1621 getInnerHTML e
17−
lib/executable-config/lookup/src-ghcjs/Obelisk/ExecutableConfig/Internal.hsnew file+32
@@ -0,0 +1,32 @@
1+{-# LANGUAGE OverloadedStrings #-}
2+module Obelisk.ExecutableConfig.Internal where
3+
4+import Control.Monad.Trans.Maybe
5+import Data.List (sortOn)
6+import Data.Maybe (catMaybes)
7+import Data.Semigroup ((<>))
8+import Data.Text (Text)
9+import qualified Data.Text as T
10+import Data.Traversable (for)
11+import GHCJS.DOM
12+import GHCJS.DOM.Document (getHead)
13+import GHCJS.DOM.Element (Element, getElementsByTagName, getId, getInnerHTML)
14+import GHCJS.DOM.HTMLCollection (HTMLCollection, item, getLength)
15+import GHCJS.DOM.NonElementParentNode
16+
17+getFrontendConfigs :: IO [(Text, Text)]
18+getFrontendConfigs = fmap concat $ runMaybeT $ do
19+ doc <- MaybeT currentDocument
20+ hd <- MaybeT $ getHead doc
21+ es <- collToList =<< getElementsByTagName hd ("script" :: Text)
22+ cfg <- for es $ \e -> runMaybeT $ do
23+ ident <- getId e
24+ k <- MaybeT $ pure $ T.stripPrefix "config-" ident
25+ v <- getInnerHTML e
26+ pure (k,v)
27+ pure $ sortOn fst $ catMaybes cfg
28+ where
29+ collToList es = do
30+ len <- getLength es
31+ list <- traverse (item es) [0..len-1]
32+ pure $ catMaybes list
lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig.hs+2
@@ -4,11 +4,13 @@ import Control.Exception
44import Control.Monad
55import Control.Monad.Trans.Maybe
66import Data.ByteString as BS
7+import Data.List (sortOn)
78import Data.Text (Text)
89import qualified Data.Text as T
910import qualified Data.Text.IO as T
1011import qualified Data.Text.Encoding as T
1112import Language.Javascript.JSaddle.WKWebView
13+import System.Directory
1214import System.FilePath.Posix ((</>))
1315import System.IO.Error
1416
lib/executable-config/lookup/src-ios/Obelisk/ExecutableConfig/Internal.hsnew file+37
@@ -0,0 +1,37 @@
1+module Obelisk.ExecutableConfig.Internal where
2+
3+import Control.Exception
4+import Control.Monad
5+import Control.Monad.Trans.Maybe
6+import Data.ByteString as BS
7+import Data.List (sortOn)
8+import Data.Text (Text)
9+import qualified Data.Text as T
10+import qualified Data.Text.IO as T
11+import qualified Data.Text.Encoding as T
12+import Language.Javascript.JSaddle.WKWebView
13+import System.Directory
14+import System.FilePath.Posix ((</>))
15+import System.IO.Error
16+
17+getFrontendConfigs :: IO [(Text, Text)]
18+getFrontendConfigs = fmap Prelude.concat $ mainBundleResourcePath >>= \mp -> forM mp $ \p -> do
19+ let base = T.unpack (T.decodeUtf8 p)
20+ cfgC <- getConfigs base "config/common"
21+ cfgF <- getConfigs base "config/frontend"
22+ return $ sortOn fst $ cfgC <> cfgF
23+
24+getConfigs :: FilePath -> FilePath -> IO [(Text, Text)]
25+getConfigs base fp = do
26+ dir <- doesDirectoryExist (base </> fp)
27+ if dir
28+ then do
29+ ps <- listDirectory (base </> fp)
30+ fmap Prelude.concat $ mapM (\p -> getConfigs base $ fp </> p) ps
31+ else do
32+ file <- doesFileExist (base </> fp)
33+ if file
34+ then do
35+ f <- T.readFile (base </> fp)
36+ return [(T.pack fp, f)]
37+ else return []
lib/executable-config/lookup/src/Obelisk/ExecutableConfig.hs+3
@@ -1,12 +1,15 @@
11module Obelisk.ExecutableConfig (get) where
22
33import Control.Exception
4+import Data.List (sortOn)
45import Data.Text (Text)
56import Data.Text as T
67import Data.Text.IO as T
8+import System.Directory
79import System.FilePath.Posix ((</>))
810import System.IO.Error
911
12+
1013get :: Text -> IO (Maybe Text)
1114get path = do
1215 let doesNotExist = \e -> if isDoesNotExistError e then Just () else Nothing
lib/executable-config/lookup/src/Obelisk/ExecutableConfig/Internal.hsnew file+32
@@ -0,0 +1,32 @@
1+module Obelisk.ExecutableConfig.Internal where
2+
3+import Control.Exception
4+import Data.List (sortOn)
5+import Data.Text (Text)
6+import Data.Text as T
7+import Data.Text.IO as T
8+import System.Directory
9+import System.FilePath.Posix ((</>))
10+import System.IO.Error
11+
12+
13+getFrontendConfigs :: IO [(Text, Text)]
14+getFrontendConfigs = do
15+ cfgC <- getConfigs "config/common"
16+ cfgF <- getConfigs "config/frontend"
17+ return $ sortOn fst $ cfgC <> cfgF
18+
19+getConfigs :: FilePath -> IO [(Text, Text)]
20+getConfigs fp = do
21+ dir <- doesDirectoryExist fp
22+ if dir
23+ then do
24+ ps <- listDirectory fp
25+ fmap Prelude.concat $ mapM (\p -> getConfigs $ fp </> p) ps
26+ else do
27+ file <- doesFileExist fp
28+ if file
29+ then do
30+ f <- T.readFile fp
31+ return [(T.pack fp, f)]
32+ else return []