module Nbparts.Pack.Sources.Markdown where

import Control.Applicative (Alternative ((<|>)))
import Control.Arrow (left)
import Control.Monad qualified as Monad
import Data.Aeson qualified as Aeson
import Data.Map qualified as Map
import Data.Maybe qualified as Maybe
import Data.Text (Text)
import Data.Text qualified as Text
import Data.Text.Encoding qualified as Text
import Nbparts.Types
  ( CellMarker (CellMarker),
    CellSource (CellSource),
    CellType (Code, Markdown, Raw),
    PackError (PackParseMarkdownSourcesError),
    ParseMarkdownSourcesError (ParseMarkdownSourcesJsonError, ParseMarkdownSourcesMarkdownError),
    UnembeddedMimeAttachments (UnembeddedMimeAttachments),
    UnembeddedMimeBundle (UnembeddedMimeBundle),
    UnembeddedMimeData (BinaryData),
  )
import Nbparts.Util.Markdown qualified as MarkdownUtil
import Nbparts.Util.Text qualified as TextUtil
import Text.Megaparsec (Parsec)
import Text.Megaparsec qualified as P
import Text.Megaparsec.Char qualified as P

type Parser = Parsec ParseMarkdownSourcesError Text

markdownToSources :: String -> Text -> Either PackError [CellSource]
markdownToSources :: FilePath -> Text -> Either PackError [CellSource]
markdownToSources FilePath
filename Text
mdText = (ParseErrorBundle Text ParseMarkdownSourcesError -> PackError)
-> Either
     (ParseErrorBundle Text ParseMarkdownSourcesError) [CellSource]
-> Either PackError [CellSource]
forall b c d. (b -> c) -> Either b d -> Either c d
forall (a :: * -> * -> *) b c d.
ArrowChoice a =>
a b c -> a (Either b d) (Either c d)
left ParseErrorBundle Text ParseMarkdownSourcesError -> PackError
PackParseMarkdownSourcesError (Either
   (ParseErrorBundle Text ParseMarkdownSourcesError) [CellSource]
 -> Either PackError [CellSource])
-> Either
     (ParseErrorBundle Text ParseMarkdownSourcesError) [CellSource]
-> Either PackError [CellSource]
forall a b. (a -> b) -> a -> b
$ Parsec ParseMarkdownSourcesError Text [CellSource]
-> FilePath
-> Text
-> Either
     (ParseErrorBundle Text ParseMarkdownSourcesError) [CellSource]
forall e s a.
Parsec e s a -> FilePath -> s -> Either (ParseErrorBundle s e) a
P.runParser Parsec ParseMarkdownSourcesError Text [CellSource]
parseSources FilePath
filename Text
mdText

parseSources :: Parser [CellSource]
parseSources :: Parsec ParseMarkdownSourcesError Text [CellSource]
parseSources = ParsecT ParseMarkdownSourcesError Text Identity CellSource
-> Parsec ParseMarkdownSourcesError Text [CellSource]
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
P.many ParsecT ParseMarkdownSourcesError Text Identity CellSource
parseSource

parseSource :: Parser CellSource
parseSource :: ParsecT ParseMarkdownSourcesError Text Identity CellSource
parseSource = do
  (CellMarker Text
cellId CellType
cellType Maybe UnembeddedMimeAttachments
maybeAttachments) <- Parser CellMarker
parseCellInfo

  -- Try removing the newline added after the <!-- nbparts:cell ... --> comment.
  ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
P.optional ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m (Token s)
P.newline

  Text
srcText <- case CellType
cellType of
    CellType
Code -> ParsecT ParseMarkdownSourcesError Text Identity Text
parseCodeOrRawCell
    CellType
Raw -> ParsecT ParseMarkdownSourcesError Text Identity Text
parseCodeOrRawCell
    CellType
Markdown -> do
      Text
mdText <- ParsecT ParseMarkdownSourcesError Text Identity Text
parseOtherCell

      Blocks
mdAst <- case Text -> Either ParseError Blocks
MarkdownUtil.parseMarkdown Text
mdText of
        Right Blocks
ast -> Blocks -> ParsecT ParseMarkdownSourcesError Text Identity Blocks
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Blocks
ast
        Left ParseError
mdErr -> ParseMarkdownSourcesError
-> ParsecT ParseMarkdownSourcesError Text Identity Blocks
forall e s (m :: * -> *) a. MonadParsec e s m => e -> m a
P.customFailure (ParseError -> ParseMarkdownSourcesError
ParseMarkdownSourcesMarkdownError ParseError
mdErr)
      let mdLines :: [Text]
mdLines = Text -> [Text]
Text.lines Text
mdText

      let escapesReplacements :: [((Int, Int), Text)]
escapesReplacements = (Text -> Text) -> [Text] -> Blocks -> [((Int, Int), Text)]
MarkdownUtil.commentChangesWith Text -> Text
unescapeComments [Text]
mdLines Blocks
mdAst
      let attachmentReplacements :: [((Int, Int), Text)]
attachmentReplacements = case Maybe UnembeddedMimeAttachments
maybeAttachments of
            Just UnembeddedMimeAttachments
attachments ->
              (Text -> Maybe Text) -> [Text] -> Blocks -> [((Int, Int), Text)]
MarkdownUtil.attachmentChangesWith
                ((Text -> Text) -> Maybe Text -> Maybe Text
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> Text -> Text
forall a. Monoid a => a -> a -> a
mappend Text
"attachment:") (Maybe Text -> Maybe Text)
-> (Text -> Maybe Text) -> Text -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UnembeddedMimeAttachments -> FilePath -> Maybe Text
findAttachmentNameByFilePath UnembeddedMimeAttachments
attachments (FilePath -> Maybe Text)
-> (Text -> FilePath) -> Text -> Maybe Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> FilePath
Text.unpack)
                [Text]
mdLines
                Blocks
mdAst
            Maybe UnembeddedMimeAttachments
Nothing -> []
      let textReplacements :: [((Int, Int), Text)]
textReplacements = [((Int, Int), Text)]
escapesReplacements [((Int, Int), Text)]
-> [((Int, Int), Text)] -> [((Int, Int), Text)]
forall a. Semigroup a => a -> a -> a
<> [((Int, Int), Text)]
attachmentReplacements

      -- Safety: The replacements do not overlap.
      Text -> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> ParsecT ParseMarkdownSourcesError Text Identity Text)
-> (Maybe Text -> Text)
-> Maybe Text
-> ParsecT ParseMarkdownSourcesError Text Identity Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Text -> Text
forall a. HasCallStack => Maybe a -> a
Maybe.fromJust (Maybe Text
 -> ParsecT ParseMarkdownSourcesError Text Identity Text)
-> Maybe Text
-> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a b. (a -> b) -> a -> b
$ Text -> [((Int, Int), Text)] -> Maybe Text
TextUtil.replaceSlices Text
mdText [((Int, Int), Text)]
textReplacements

  let src :: [Text]
src = Text -> [Text]
TextUtil.splitKeepNewlines Text
srcText

  CellSource
-> ParsecT ParseMarkdownSourcesError Text Identity CellSource
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CellSource
 -> ParsecT ParseMarkdownSourcesError Text Identity CellSource)
-> CellSource
-> ParsecT ParseMarkdownSourcesError Text Identity CellSource
forall a b. (a -> b) -> a -> b
$ Text
-> CellType
-> [Text]
-> Maybe UnembeddedMimeAttachments
-> CellSource
CellSource Text
cellId CellType
cellType [Text]
src Maybe UnembeddedMimeAttachments
maybeAttachments

parseCodeOrRawCell :: Parser Text
parseCodeOrRawCell :: ParsecT ParseMarkdownSourcesError Text Identity Text
parseCodeOrRawCell = do
  Text
code <- ParsecT ParseMarkdownSourcesError Text Identity Text
parseCodeBlock
  -- During unpacking, we appended two newlines to the end of the cell content for prettier output,
  -- so, now, we remove the newlines.
  ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity (Maybe Char)
forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
P.optional (ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m (Token s)
P.newline ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity Char
forall a b.
ParsecT ParseMarkdownSourcesError Text Identity a
-> ParsecT ParseMarkdownSourcesError Text Identity b
-> ParsecT ParseMarkdownSourcesError Text Identity b
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m (Token s)
P.newline)
  Text -> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
code

parseCodeBlock :: Parser Text
parseCodeBlock :: ParsecT ParseMarkdownSourcesError Text Identity Text
parseCodeBlock = do
  ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ Tokens Text
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
P.string Tokens Text
"```"
  ParsecT ParseMarkdownSourcesError Text Identity [Token Text]
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity [Token Text]
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity [Token Text]
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
-> ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity [Token Text]
forall (m :: * -> *) a end. MonadPlus m => m a -> m end -> m [a]
P.manyTill ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
P.anySingle ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m (Token s)
P.newline -- Ignore the language identifier.
  Text
code <- FilePath -> Text
Text.pack (FilePath -> Text)
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
-> ParsecT ParseMarkdownSourcesError Text Identity Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
forall (m :: * -> *) a end. MonadPlus m => m a -> m end -> m [a]
P.manyTill ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
P.anySingle (Tokens Text
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
P.string Tokens Text
"```")

  -- We appended a newline when unpacking, so remove it.
  let stripped :: Text
stripped = Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
Maybe.fromMaybe Text
code (Maybe Text -> Text) -> Maybe Text -> Text
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Maybe Text
Text.stripSuffix Text
"\n" Text
code
  Text -> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Text
stripped

parseOtherCell :: Parser Text
parseOtherCell :: ParsecT ParseMarkdownSourcesError Text Identity Text
parseOtherCell = do
  Text
body <- FilePath -> Text
Text.pack (FilePath -> Text)
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
-> ParsecT ParseMarkdownSourcesError Text Identity Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity ()
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
forall (m :: * -> *) a end. MonadPlus m => m a -> m end -> m [a]
P.manyTill ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
P.anySingle (ParsecT ParseMarkdownSourcesError Text Identity ()
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a.
ParsecT ParseMarkdownSourcesError Text Identity a
-> ParsecT ParseMarkdownSourcesError Text Identity a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
P.lookAhead (Parser CellMarker
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (Parser CellMarker -> Parser CellMarker
forall a.
ParsecT ParseMarkdownSourcesError Text Identity a
-> ParsecT ParseMarkdownSourcesError Text Identity a
forall e s (m :: * -> *) a. MonadParsec e s m => m a -> m a
P.try Parser CellMarker
parseCellInfo) ParsecT ParseMarkdownSourcesError Text Identity ()
-> ParsecT ParseMarkdownSourcesError Text Identity ()
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a.
ParsecT ParseMarkdownSourcesError Text Identity a
-> ParsecT ParseMarkdownSourcesError Text Identity a
-> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ParsecT ParseMarkdownSourcesError Text Identity ()
forall e s (m :: * -> *). MonadParsec e s m => m ()
P.eof))
  -- Again, removing the trailing two newlines. Since parsing non-code blocks only stops
  -- when encountering the <!-- nbparts:cell ... --> comment, the parsed text contains the
  -- newlines, so we have to remove them using plain text functions instead of Megaparsec.
  Text -> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> ParsecT ParseMarkdownSourcesError Text Identity Text)
-> Text -> ParsecT ParseMarkdownSourcesError Text Identity Text
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
Maybe.fromMaybe Text
body (Maybe Text -> Text) -> Maybe Text -> Text
forall a b. (a -> b) -> a -> b
$ Text -> Text -> Maybe Text
Text.stripSuffix Text
"\n\n" Text
body

parseCellInfo :: Parser CellMarker
parseCellInfo :: Parser CellMarker
parseCellInfo = do
  ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ Tokens Text
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
P.string Tokens Text
"<!--"
  ParsecT ParseMarkdownSourcesError Text Identity ()
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m ()
P.space
  ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall (f :: * -> *) a. Functor f => f a -> f ()
Monad.void (ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
 -> ParsecT ParseMarkdownSourcesError Text Identity ())
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity ()
forall a b. (a -> b) -> a -> b
$ Tokens Text
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
P.string Tokens Text
"nbparts:cell"
  ParsecT ParseMarkdownSourcesError Text Identity ()
forall e s (m :: * -> *).
(MonadParsec e s m, Token s ~ Char) =>
m ()
P.space1
  Text
json <- FilePath -> Text
Text.pack (FilePath -> Text)
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
-> ParsecT ParseMarkdownSourcesError Text Identity Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ParsecT ParseMarkdownSourcesError Text Identity Char
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
-> ParsecT ParseMarkdownSourcesError Text Identity FilePath
forall (m :: * -> *) a end. MonadPlus m => m a -> m end -> m [a]
P.manyTill ParsecT ParseMarkdownSourcesError Text Identity Char
ParsecT ParseMarkdownSourcesError Text Identity (Token Text)
forall e s (m :: * -> *). MonadParsec e s m => m (Token s)
P.anySingle (Tokens Text
-> ParsecT ParseMarkdownSourcesError Text Identity (Tokens Text)
forall e s (m :: * -> *).
MonadParsec e s m =>
Tokens s -> m (Tokens s)
P.string Tokens Text
"-->")
  let jsonUnescaped :: Text
jsonUnescaped = Text -> Text
unescapeCellMarkerContent Text
json
  case ByteString -> Either FilePath CellMarker
forall a. FromJSON a => ByteString -> Either FilePath a
Aeson.eitherDecodeStrict (Text -> ByteString
Text.encodeUtf8 Text
jsonUnescaped) of
    Right CellMarker
cellInfo -> CellMarker -> Parser CellMarker
forall a. a -> ParsecT ParseMarkdownSourcesError Text Identity a
forall (f :: * -> *) a. Applicative f => a -> f a
pure CellMarker
cellInfo
    Left FilePath
err -> ParseMarkdownSourcesError -> Parser CellMarker
forall e s (m :: * -> *) a. MonadParsec e s m => e -> m a
P.customFailure (Text -> ParseMarkdownSourcesError
ParseMarkdownSourcesJsonError (Text -> ParseMarkdownSourcesError)
-> Text -> ParseMarkdownSourcesError
forall a b. (a -> b) -> a -> b
$ FilePath -> Text
Text.pack FilePath
err)

unescapeComments :: Text -> Text
unescapeComments :: Text -> Text
unescapeComments = HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
Text.replace Text
"\\\\" Text
"\\" (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
Text.replace Text
"\\nbparts:cell" Text
"nbparts:cell"

findAttachmentNameByFilePath :: UnembeddedMimeAttachments -> FilePath -> Maybe Text
findAttachmentNameByFilePath :: UnembeddedMimeAttachments -> FilePath -> Maybe Text
findAttachmentNameByFilePath (UnembeddedMimeAttachments Map Text UnembeddedMimeBundle
attachments) FilePath
targetFp =
  (Text -> UnembeddedMimeBundle -> Maybe Text -> Maybe Text)
-> Maybe Text -> Map Text UnembeddedMimeBundle -> Maybe Text
forall k a b. (k -> a -> b -> b) -> b -> Map k a -> b
Map.foldrWithKey Text -> UnembeddedMimeBundle -> Maybe Text -> Maybe Text
go Maybe Text
forall a. Maybe a
Nothing Map Text UnembeddedMimeBundle
attachments
  where
    go :: Text -> UnembeddedMimeBundle -> Maybe Text -> Maybe Text
    go :: Text -> UnembeddedMimeBundle -> Maybe Text -> Maybe Text
go Text
attachmentName (UnembeddedMimeBundle Map Text UnembeddedMimeData
bundle) Maybe Text
acc =
      Maybe Text
acc
        Maybe Text -> Maybe Text -> Maybe Text
forall a. Maybe a -> Maybe a -> Maybe a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> if (UnembeddedMimeData -> Bool) -> [UnembeddedMimeData] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (FilePath -> UnembeddedMimeData -> Bool
matches FilePath
targetFp) (Map Text UnembeddedMimeData -> [UnembeddedMimeData]
forall k a. Map k a -> [a]
Map.elems Map Text UnembeddedMimeData
bundle)
          then Text -> Maybe Text
forall a. a -> Maybe a
Just Text
attachmentName
          else Maybe Text
forall a. Maybe a
Nothing

    matches :: FilePath -> UnembeddedMimeData -> Bool
    matches :: FilePath -> UnembeddedMimeData -> Bool
matches FilePath
targetFp' (BinaryData FilePath
fp) = FilePath
fp FilePath -> FilePath -> Bool
forall a. Eq a => a -> a -> Bool
== FilePath
targetFp'
    matches FilePath
_ UnembeddedMimeData
_ = Bool
False

unescapeCellMarkerContent :: Text -> Text
unescapeCellMarkerContent :: Text -> Text
unescapeCellMarkerContent = HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
Text.replace Text
"\\\\" Text
"\\" (Text -> Text) -> (Text -> Text) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HasCallStack => Text -> Text -> Text -> Text
Text -> Text -> Text -> Text
Text.replace Text
"-\\->" Text
"-->"