{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Text.Pandoc.MediaBag (
MediaItem(..),
MediaBag,
deleteMedia,
lookupMedia,
insertMedia,
mediaDirectory,
mediaItems
) where
import qualified Data.ByteString.Lazy as BL
import Data.Data (Data)
import qualified Data.Map as M
import Data.Maybe (fromMaybe, isNothing)
import Data.Typeable (Typeable)
import Network.URI (unEscapeString)
import System.FilePath
import qualified System.FilePath.Posix as Posix
import qualified System.FilePath.Windows as Windows
import Text.Pandoc.MIME (MimeType, getMimeTypeDef, extensionFromMimeType)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Digest.Pure.SHA (sha1, showDigest)
import Network.URI (URI (..), parseURI, isURI)
import Data.List (isInfixOf)
data MediaItem =
MediaItem
{ MediaItem -> Text
mediaMimeType :: MimeType
, MediaItem -> FilePath
mediaPath :: FilePath
, MediaItem -> ByteString
mediaContents :: BL.ByteString
} deriving (MediaItem -> MediaItem -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: MediaItem -> MediaItem -> Bool
$c/= :: MediaItem -> MediaItem -> Bool
== :: MediaItem -> MediaItem -> Bool
$c== :: MediaItem -> MediaItem -> Bool
Eq, Eq MediaItem
MediaItem -> MediaItem -> Bool
MediaItem -> MediaItem -> Ordering
MediaItem -> MediaItem -> MediaItem
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: MediaItem -> MediaItem -> MediaItem
$cmin :: MediaItem -> MediaItem -> MediaItem
max :: MediaItem -> MediaItem -> MediaItem
$cmax :: MediaItem -> MediaItem -> MediaItem
>= :: MediaItem -> MediaItem -> Bool
$c>= :: MediaItem -> MediaItem -> Bool
> :: MediaItem -> MediaItem -> Bool
$c> :: MediaItem -> MediaItem -> Bool
<= :: MediaItem -> MediaItem -> Bool
$c<= :: MediaItem -> MediaItem -> Bool
< :: MediaItem -> MediaItem -> Bool
$c< :: MediaItem -> MediaItem -> Bool
compare :: MediaItem -> MediaItem -> Ordering
$ccompare :: MediaItem -> MediaItem -> Ordering
Ord, Int -> MediaItem -> ShowS
[MediaItem] -> ShowS
MediaItem -> FilePath
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [MediaItem] -> ShowS
$cshowList :: [MediaItem] -> ShowS
show :: MediaItem -> FilePath
$cshow :: MediaItem -> FilePath
showsPrec :: Int -> MediaItem -> ShowS
$cshowsPrec :: Int -> MediaItem -> ShowS
Show, Typeable MediaItem
MediaItem -> DataType
MediaItem -> Constr
(forall b. Data b => b -> b) -> MediaItem -> MediaItem
forall a.
Typeable a
-> (forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> MediaItem -> u
forall u. (forall d. Data d => d -> u) -> MediaItem -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaItem
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaItem -> c MediaItem
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaItem)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaItem)
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaItem -> m MediaItem
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> MediaItem -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> MediaItem -> u
gmapQ :: forall u. (forall d. Data d => d -> u) -> MediaItem -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> MediaItem -> [u]
gmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
gmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaItem -> r
gmapT :: (forall b. Data b => b -> b) -> MediaItem -> MediaItem
$cgmapT :: (forall b. Data b => b -> b) -> MediaItem -> MediaItem
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaItem)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaItem)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaItem)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaItem)
dataTypeOf :: MediaItem -> DataType
$cdataTypeOf :: MediaItem -> DataType
toConstr :: MediaItem -> Constr
$ctoConstr :: MediaItem -> Constr
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaItem
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaItem
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaItem -> c MediaItem
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaItem -> c MediaItem
Data, Typeable)
newtype MediaBag = MediaBag (M.Map Text MediaItem)
deriving (NonEmpty MediaBag -> MediaBag
MediaBag -> MediaBag -> MediaBag
forall b. Integral b => b -> MediaBag -> MediaBag
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> MediaBag -> MediaBag
$cstimes :: forall b. Integral b => b -> MediaBag -> MediaBag
sconcat :: NonEmpty MediaBag -> MediaBag
$csconcat :: NonEmpty MediaBag -> MediaBag
<> :: MediaBag -> MediaBag -> MediaBag
$c<> :: MediaBag -> MediaBag -> MediaBag
Semigroup, Semigroup MediaBag
MediaBag
[MediaBag] -> MediaBag
MediaBag -> MediaBag -> MediaBag
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [MediaBag] -> MediaBag
$cmconcat :: [MediaBag] -> MediaBag
mappend :: MediaBag -> MediaBag -> MediaBag
$cmappend :: MediaBag -> MediaBag -> MediaBag
mempty :: MediaBag
$cmempty :: MediaBag
Monoid, Typeable MediaBag
MediaBag -> DataType
MediaBag -> Constr
(forall b. Data b => b -> b) -> MediaBag -> MediaBag
forall a.
Typeable a
-> (forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> MediaBag -> u
forall u. (forall d. Data d => d -> u) -> MediaBag -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaBag
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaBag -> c MediaBag
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaBag)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaBag)
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> MediaBag -> m MediaBag
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> MediaBag -> u
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> MediaBag -> u
gmapQ :: forall u. (forall d. Data d => d -> u) -> MediaBag -> [u]
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> MediaBag -> [u]
gmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
gmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> MediaBag -> r
gmapT :: (forall b. Data b => b -> b) -> MediaBag -> MediaBag
$cgmapT :: (forall b. Data b => b -> b) -> MediaBag -> MediaBag
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaBag)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c MediaBag)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaBag)
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c MediaBag)
dataTypeOf :: MediaBag -> DataType
$cdataTypeOf :: MediaBag -> DataType
toConstr :: MediaBag -> Constr
$ctoConstr :: MediaBag -> Constr
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaBag
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c MediaBag
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaBag -> c MediaBag
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> MediaBag -> c MediaBag
Data, Typeable)
instance Show MediaBag where
show :: MediaBag -> FilePath
show MediaBag
bag = FilePath
"MediaBag " forall a. [a] -> [a] -> [a]
++ forall a. Show a => a -> FilePath
show (MediaBag -> [(FilePath, Text, Int)]
mediaDirectory MediaBag
bag)
canonicalize :: FilePath -> Text
canonicalize :: FilePath -> Text
canonicalize FilePath
fp
| FilePath -> Bool
isURI FilePath
fp = FilePath -> Text
T.pack FilePath
fp
| Bool
otherwise = Text -> Text -> Text -> Text
T.replace Text
"\\" Text
"/" forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Text
T.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
normalise forall a b. (a -> b) -> a -> b
$ FilePath
fp
deleteMedia :: FilePath
-> MediaBag
-> MediaBag
deleteMedia :: FilePath -> MediaBag -> MediaBag
deleteMedia FilePath
fp (MediaBag Map Text MediaItem
mediamap) =
Map Text MediaItem -> MediaBag
MediaBag forall a b. (a -> b) -> a -> b
$ forall k a. Ord k => k -> Map k a -> Map k a
M.delete (FilePath -> Text
canonicalize FilePath
fp) Map Text MediaItem
mediamap
insertMedia :: FilePath
-> Maybe MimeType
-> BL.ByteString
-> MediaBag
-> MediaBag
insertMedia :: FilePath -> Maybe Text -> ByteString -> MediaBag -> MediaBag
insertMedia FilePath
fp Maybe Text
mbMime ByteString
contents (MediaBag Map Text MediaItem
mediamap) =
Map Text MediaItem -> MediaBag
MediaBag (forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Text
fp' MediaItem
mediaItem Map Text MediaItem
mediamap)
where mediaItem :: MediaItem
mediaItem = MediaItem{ mediaPath :: FilePath
mediaPath = FilePath
newpath
, mediaContents :: ByteString
mediaContents = ByteString
contents
, mediaMimeType :: Text
mediaMimeType = Text
mt }
fp' :: Text
fp' = FilePath -> Text
canonicalize FilePath
fp
fp'' :: FilePath
fp'' = ShowS
unEscapeString forall a b. (a -> b) -> a -> b
$ Text -> FilePath
T.unpack Text
fp'
uri :: Maybe URI
uri = FilePath -> Maybe URI
parseURI FilePath
fp
newpath :: FilePath
newpath = if FilePath -> Bool
Posix.isRelative FilePath
fp''
Bool -> Bool -> Bool
&& FilePath -> Bool
Windows.isRelative FilePath
fp''
Bool -> Bool -> Bool
&& forall a. Maybe a -> Bool
isNothing Maybe URI
uri
Bool -> Bool -> Bool
&& Bool -> Bool
not (FilePath
".." forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` FilePath
fp'')
Bool -> Bool -> Bool
&& Char
'%' forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` FilePath
fp''
then FilePath
fp''
else forall t. Digest t -> FilePath
showDigest (ByteString -> Digest SHA1State
sha1 ByteString
contents) forall a. Semigroup a => a -> a -> a
<> FilePath
ext
fallback :: Text
fallback = case ShowS
takeExtension FilePath
fp'' of
FilePath
".gz" -> FilePath -> Text
getMimeTypeDef forall a b. (a -> b) -> a -> b
$ ShowS
dropExtension FilePath
fp''
FilePath
_ -> FilePath -> Text
getMimeTypeDef FilePath
fp''
mt :: Text
mt = forall a. a -> Maybe a -> a
fromMaybe Text
fallback Maybe Text
mbMime
path :: FilePath
path = forall b a. b -> (a -> b) -> Maybe a -> b
maybe FilePath
fp'' (ShowS
unEscapeString forall b c a. (b -> c) -> (a -> b) -> a -> c
. URI -> FilePath
uriPath) Maybe URI
uri
ext :: FilePath
ext = case ShowS
takeExtension FilePath
path of
Char
'.':FilePath
e | Char
'%' forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`notElem` FilePath
e -> Char
'.'forall a. a -> [a] -> [a]
:FilePath
e
FilePath
_ -> forall b a. b -> (a -> b) -> Maybe a -> b
maybe FilePath
"" (\Text
x -> Char
'.'forall a. a -> [a] -> [a]
:Text -> FilePath
T.unpack Text
x) forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text
extensionFromMimeType Text
mt
lookupMedia :: FilePath
-> MediaBag
-> Maybe MediaItem
lookupMedia :: FilePath -> MediaBag -> Maybe MediaItem
lookupMedia FilePath
fp (MediaBag Map Text MediaItem
mediamap) = forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup (FilePath -> Text
canonicalize FilePath
fp) Map Text MediaItem
mediamap
mediaDirectory :: MediaBag -> [(FilePath, MimeType, Int)]
mediaDirectory :: MediaBag -> [(FilePath, Text, Int)]
mediaDirectory MediaBag
mediabag =
forall a b. (a -> b) -> [a] -> [b]
map (\(FilePath
fp, Text
mt, ByteString
bs) -> (FilePath
fp, Text
mt, forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int64
BL.length ByteString
bs)))
(MediaBag -> [(FilePath, Text, ByteString)]
mediaItems MediaBag
mediabag)
mediaItems :: MediaBag -> [(FilePath, MimeType, BL.ByteString)]
mediaItems :: MediaBag -> [(FilePath, Text, ByteString)]
mediaItems (MediaBag Map Text MediaItem
mediamap) =
forall a b. (a -> b) -> [a] -> [b]
map (\MediaItem
item -> (MediaItem -> FilePath
mediaPath MediaItem
item, MediaItem -> Text
mediaMimeType MediaItem
item, MediaItem -> ByteString
mediaContents MediaItem
item))
(forall k a. Map k a -> [a]
M.elems Map Text MediaItem
mediamap)