emanote-1.2.0.0: src/Emanote/Model/StaticFile.hs
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TemplateHaskell #-}
module Emanote.Model.StaticFile where
import Commonmark.Extensions.WikiLink qualified as WL
import Data.Aeson qualified as Aeson
import Data.IxSet.Typed (Indexable (..), IxSet, ixFun, ixList)
import Data.Map.Strict qualified as Map
import Data.Time (UTCTime)
import Emanote.Route qualified as R
import Optics.TH (makeLenses)
import Relude
import System.FilePath (takeExtension)
data StaticFile = StaticFile
{ _staticFileRoute :: R.R 'R.AnyExt
, _staticFilePath :: FilePath
, _staticFileTime :: UTCTime
-- ^ Indicates that this file was updated no latter than the given time.
, _staticFileInfo :: Maybe StaticFileInfo
-- ^ This file might have its content read
}
deriving stock (Eq, Ord, Show, Generic)
deriving anyclass (Aeson.ToJSON)
type StaticFileIxs = '[R.R 'R.AnyExt, WL.WikiLink]
type IxStaticFile = IxSet StaticFileIxs StaticFile
instance Indexable StaticFileIxs StaticFile where
indices =
ixList
(ixFun $ one . _staticFileRoute)
(ixFun $ toList . staticFileSelfRefs)
staticFileSelfRefs :: StaticFile -> NonEmpty WL.WikiLink
staticFileSelfRefs =
fmap snd
. WL.allowedWikiLinks
. R.unRoute
. _staticFileRoute
data StaticFileInfo where
StaticFileInfoImage :: StaticFileInfo
StaticFileInfoAudio :: StaticFileInfo
StaticFileInfoVideo :: StaticFileInfo
StaticFileInfoPDF :: StaticFileInfo
StaticFileInfoCode ::
-- | File code language name
CodeLanguage ->
-- | File content
Text ->
StaticFileInfo
deriving stock (Eq, Show, Ord, Generic)
deriving anyclass (Aeson.ToJSON)
newtype CodeLanguage = CodeLanguage Text
deriving stock (Eq, Show, Ord, Generic)
deriving anyclass (Aeson.ToJSON)
-- | Return the ${name} in the corresponding templates/filters/embed-${name}.tpl
staticFileInfoTemplateName :: (IsString s) => StaticFileInfo -> s
staticFileInfoTemplateName = \case
StaticFileInfoImage -> "image"
StaticFileInfoAudio -> "audio"
StaticFileInfoVideo -> "video"
StaticFileInfoPDF -> "pdf"
StaticFileInfoCode _ _ -> "code"
readStaticFileInfo ::
(Monad m) =>
FilePath ->
(FilePath -> m Text) ->
m (Maybe StaticFileInfo)
readStaticFileInfo fp readFilePath = do
let extension = toText (takeExtension fp)
if
| extension `elem` imageExts ->
pure $ Just StaticFileInfoImage
| extension `elem` videoExts ->
pure $ Just StaticFileInfoVideo
| extension `elem` audioExts ->
pure $ Just StaticFileInfoAudio
| extension == ".pdf" ->
pure $ Just StaticFileInfoPDF
| Just lang <- Map.lookup extension codeExts -> do
code <- readFilePath fp
pure $ Just $ StaticFileInfoCode lang code
| otherwise -> return Nothing
where
imageExts = [".jpg", ".jpeg", ".png", ".svg", ".gif", ".bmp", ".webp"]
videoExts = [".mp4", ".webm", ".ogv"]
audioExts = [".aac", ".caf", ".flac", ".mp3", ".ogg", ".wav", ".wave"]
codeExts =
CodeLanguage
<$> Map.fromList
[ (".hs", "haskell")
, (".nix", "nix")
, (".sh", "bash")
, (".py", "python")
, (".js", "javascript")
, (".java", "java")
, (".c", "c")
, (".cpp", "cpp")
, (".cs", "cs")
, (".rb", "ruby")
, (".go", "go")
, (".swift", "swift")
, (".kt", "kotlin")
, (".rs", "rust")
, (".ts", "typescript")
, (".php", "php")
]
makeLenses ''StaticFile