{-# LANGUAGE DataKinds #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module App.Commands.ExtractFiles where import App.Commands.Options.Type (ExtractFilesOptions (ExtractFilesOptions)) import Arbor.File.Format.Asif.IO import Arbor.File.Format.Asif.Segment import Control.Lens import Control.Monad import Control.Monad.IO.Class import Control.Monad.Trans.Resource (MonadResource, runResourceT) import Data.Generics.Product.Any import Data.List import Data.Maybe import Data.Monoid ((<>)) import Options.Applicative import System.Directory import qualified Data.Attoparsec.ByteString as AP import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.Map as M import qualified Data.Text as T import qualified System.Directory as IO import qualified System.IO as IO parseExtractFilesOptions :: Parser ExtractFilesOptions parseExtractFilesOptions = ExtractFilesOptions <$> strOption ( long "source" <> metavar "FILE" <> value "-" <> help "Input file" ) <*> strOption ( long "target" <> metavar "PATH" <> help "Output directory" ) commandExtractFiles :: Parser (IO ()) commandExtractFiles = runResourceT . runExtractFiles <$> parseExtractFilesOptions runExtractFiles :: MonadResource m => ExtractFilesOptions -> m () runExtractFiles opt = do (_, hIn) <- openFileOrStd (opt ^. the @"source") IO.ReadMode contents <- liftIO $ LBS.hGetContents hIn case extractSegments magic contents of Left errorMessage -> do liftIO $ IO.hPutStrLn IO.stderr $ "Error occured: " <> errorMessage return () Right segments -> do let filenames = fromMaybe "" . (^. the @"meta" . the @"filename") <$> segments let namedSegments = M.fromList $ mfilter ((/= "") . fst) (zip filenames segments) let targetPath = opt ^. the @"target" liftIO $ IO.hPutStrLn IO.stderr $ "Writing to: " <> targetPath liftIO $ createDirectoryIfMissing True targetPath forM_ (zip [0..] filenames) $ \(i :: Int, filename) -> case M.lookup filename namedSegments of Just segment -> do let outFilename = T.pack targetPath <> "/" <> filename let basename = mconcat (intersperse "/" (init (T.splitOn "/" outFilename))) liftIO $ IO.createDirectoryIfMissing True (T.unpack basename) liftIO $ LBS.writeFile (T.unpack outFilename) (segment ^. the @"payload") Nothing -> liftIO $ IO.hPutStrLn IO.stderr $ "Segment " <> show i <> " has no filename. Skipping" where magic = AP.string "seg:" *> (BS.pack <$> many AP.anyWord8) AP. "\"seg:????\""