194 lines
6.9 KiB
Haskell
194 lines
6.9 KiB
Haskell
module Settings.WellKnownFiles.TH
|
|
( mkWellKnown
|
|
) where
|
|
|
|
import ClassyPrelude.Yesod
|
|
import Utils
|
|
|
|
import Language.Haskell.TH
|
|
import Language.Haskell.TH.Syntax hiding (Lift(..))
|
|
import qualified Language.Haskell.TH.Syntax as TH (Lift(..))
|
|
|
|
import System.Directory.Tree
|
|
|
|
import qualified Data.ByteString as BS
|
|
|
|
import Utils.Lens.TH
|
|
import Control.Lens
|
|
import Data.Set.Lens
|
|
|
|
import qualified Data.Text as Text
|
|
import qualified Data.Text.Encoding as Text
|
|
import Data.Char as Char (isAlphaNum, toUpper)
|
|
|
|
import qualified Data.Map as Map
|
|
import qualified Data.Set as Set
|
|
|
|
import Data.List.NonEmpty (NonEmpty(..))
|
|
|
|
import qualified Data.HashMap.Strict as HashMap
|
|
|
|
import qualified Data.HashSet as HashSet
|
|
|
|
import System.FilePath (splitDirectories, makeRelative)
|
|
|
|
import Settings.Mime
|
|
|
|
import Text.Blaze.Html (preEscapedToHtml)
|
|
|
|
import Control.Monad.Fail
|
|
|
|
nWellKnownFileName :: Name
|
|
nWellKnownFileName = mkName "WellKnownFileName"
|
|
|
|
nwellKnownFileNames :: Name
|
|
nwellKnownFileNames = mkName "wellKnownFileNames"
|
|
|
|
ngetWellKnownR :: Name
|
|
ngetWellKnownR = mkName "getWellKnownR"
|
|
|
|
nwellKnownHtmlLinks :: Name
|
|
nwellKnownHtmlLinks = mkName "wellKnownHtmlLinks"
|
|
|
|
|
|
|
|
mkWellKnown :: Lang -- ^ Default language
|
|
-> FilePath -- ^ Base directory
|
|
-> FilePath -- ^ Link file (@html_code.html@)
|
|
-> DecsQ
|
|
mkWellKnown defLang wellKnownBase wellKnownLinks = do
|
|
inputFiles <- fmap dirTree . liftIO $ readDirectoryWith (\f -> (f, ) <$> BS.readFile f) wellKnownBase
|
|
|
|
mapM_ qAddDependentFile $ inputFiles ^.. folded . _1
|
|
|
|
-- languageFiles :: Map Lang [(FilePath, ByteString)]
|
|
languageFiles <- if
|
|
| Dir{contents} <- inputFiles
|
|
-> return . Map.fromList $ do
|
|
(language, lContents) <- contents ^.. folded . $(multifocusL 2) _name id
|
|
Dir{} <- pure lContents
|
|
let
|
|
lContents' :: [(FilePath, ByteString)]
|
|
lContents' = flip mapMaybe (flattenDir lContents) $ \pFile -> do
|
|
File{..} <- pure pFile
|
|
guard $ name /= wellKnownLinks
|
|
return $ file & _1 %~ makeRelative (wellKnownBase </> language)
|
|
return (Text.pack language, lContents')
|
|
| otherwise
|
|
-> fail "wellKnownBase is not a directory"
|
|
|
|
fLanguages <- if
|
|
| defLang `Set.member` Map.keysSet languageFiles
|
|
, let languages' = Set.delete defLang $ Map.keysSet languageFiles
|
|
-> return $ defLang :| Set.toList languages'
|
|
| otherwise
|
|
-> fail "default language is missing in wellKnownBase"
|
|
|
|
-- languageLinks :: Map Lang ByteString
|
|
languageLinks <- if
|
|
| Dir{contents} <- inputFiles
|
|
-> return . Map.fromList $ do
|
|
(language, lContents) <- contents ^.. folded . $(multifocusL 2) _name id
|
|
Dir{} <- pure lContents
|
|
let
|
|
lContents' :: [ByteString]
|
|
lContents' = flip mapMaybe (flattenDir lContents) $ \pFile -> do
|
|
File{..} <- pure pFile
|
|
guard $ name == wellKnownLinks
|
|
return $ file ^. _2
|
|
c <- lContents'
|
|
return (Text.pack language, c)
|
|
| otherwise
|
|
-> fail "wellKnownBase is not a directory"
|
|
|
|
lLanguages <- if
|
|
| defLang `Set.member` Map.keysSet languageLinks
|
|
, let languages' = Set.delete defLang $ Map.keysSet languageLinks
|
|
-> return $ defLang :| Set.toList languages'
|
|
| otherwise
|
|
-> fail "default language is missing in wellKnownBase"
|
|
|
|
|
|
fVar <- newName "f"
|
|
hVar <- newName "h"
|
|
lVar <- newName "l"
|
|
|
|
let fileNames = setOf (folded . folded . _1) languageFiles
|
|
fileContents = Map.fromListWith (<>) $ do
|
|
(lang, fs) <- Map.toList languageFiles
|
|
(fName, fContent) <- fs
|
|
return ((fContent, mimeLookup $ Text.pack fName), Set.singleton (lang, fName))
|
|
|
|
wellKnownFileName = dataD
|
|
(cxt [])
|
|
nWellKnownFileName
|
|
[]
|
|
Nothing
|
|
[ normalC (mkName $ fNameManip fName) []
|
|
| fName <- Set.toList fileNames
|
|
]
|
|
(pure $ derivClause Nothing [[t|Eq|], [t|Ord|], [t|Bounded|], [t|Enum|], [t|Read|], [t|Show|], [t|Generic|], [t|Typeable|]])
|
|
wellKnownFileNameMapSig = sigD
|
|
nwellKnownFileNames
|
|
[t|HashMap [Text] $(conT nWellKnownFileName)|]
|
|
wellKnownFileNameMap = funD
|
|
nwellKnownFileNames
|
|
[ clause [] (normalB $ [e|HashMap.fromList|] `appE` listE [ [e|($(TH.lift . map Text.pack $ splitDirectories fName), $(conE . mkName $ fNameManip fName))|] | fName <- Set.toList fileNames ]) []
|
|
]
|
|
wellKnownFileNamePathMultiPiece = instanceD
|
|
(cxt [])
|
|
(conT ''PathMultiPiece `appT` conT nWellKnownFileName)
|
|
[ funD 'toPathMultiPiece
|
|
[ clause [conP (mkName $ fNameManip fName) []] (normalB . TH.lift . map Text.pack $ splitDirectories fName) []
|
|
| fName <- Set.toList fileNames
|
|
]
|
|
, funD 'fromPathMultiPiece
|
|
[ clause [] (normalB [e|flip HashMap.lookup $(varE nwellKnownFileNames)|]) []
|
|
]
|
|
]
|
|
wellKnownFileNameHashable = instanceD
|
|
(cxt [])
|
|
(conT ''Hashable `appT` conT nWellKnownFileName)
|
|
[]
|
|
|
|
getWellKnownRSig = sigD
|
|
ngetWellKnownR
|
|
[t|forall m. MonadHandler m => $(conT nWellKnownFileName) -> m TypedContent|]
|
|
getWellKnownR = funD
|
|
ngetWellKnownR
|
|
[ clause [varP fVar] (normalB [e|$(varE hVar) =<< selectLanguage fLanguages|])
|
|
[ funD hVar $
|
|
[ clause [varP lVar] (guardedB
|
|
[ (,) <$> normalG [e|HashSet.member ($(varE lVar), $(varE fVar)) $ HashSet.fromList $(listE [ tupE [TH.lift l, conE . mkName $ fNameManip fName] | (l, fName) <- Set.toList xs ])|]
|
|
<*> [e|TypedContent mime (toContent fContent) <$ setEtag $(TH.lift $ hashToText (mime, fContent))|]
|
|
]) []
|
|
| ((fContent, mime), xs) <- Map.toList fileContents
|
|
] ++ pure (clause [wildP] (normalB [e|notFound|]) [])
|
|
]
|
|
]
|
|
|
|
wellKnownHtmlLinksSig = sigD
|
|
nwellKnownHtmlLinks
|
|
[t|forall m. MonadWidget m => m ()|]
|
|
wellKnownHtmlLinks = funD
|
|
nwellKnownHtmlLinks
|
|
[ clause [] (normalB [e|toWidgetHead . preEscapedToHtml . $(varE hVar) =<< selectLanguage lLanguages|])
|
|
[ sigD hVar [t|Text -> Text|]
|
|
, funD hVar $
|
|
[ clause [varP lVar] (guardedB
|
|
[ (,) <$> normalG [|$(varE lVar) == lang|]
|
|
<*> TH.lift (Text.filter (`notElem` ['\r', '\n']) $ Text.decodeUtf8 c)
|
|
]) []
|
|
| (lang, c) <- Map.toList languageLinks
|
|
] ++ pure (clause [wildP] (normalB [e|mempty|]) [])
|
|
]
|
|
]
|
|
|
|
sequence
|
|
[ wellKnownFileName, wellKnownFileNameMapSig, wellKnownFileNameMap, wellKnownFileNamePathMultiPiece, wellKnownFileNameHashable
|
|
, getWellKnownRSig, getWellKnownR
|
|
, wellKnownHtmlLinksSig, wellKnownHtmlLinks
|
|
]
|
|
where
|
|
fNameManip = Text.unpack . mconcat . over (traverse . _head) Char.toUpper . filter (not . null) . Text.split (not . isAlphaNum) . Text.pack
|