{-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} module App.Commands.Ls ( commandLs ) where import App.Commands.Options.Type (LsOptions (LsOptions)) import Arbor.File.Format.Asif.IO import Arbor.File.Format.Asif.Segment hiding (meta) import Control.Lens import Control.Monad import Control.Monad.IO.Class (liftIO) import Control.Monad.Trans.Resource (MonadResource, runResourceT) import Data.Generics.Product.Any import Data.Maybe import Data.Monoid ((<>)) import Data.Thyme.Clock.POSIX (POSIXTime) import Data.Thyme.Time.Core import Options.Applicative import qualified Data.Attoparsec.ByteString as AP import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as LBS import qualified Data.Text as T import qualified System.IO as IO {-# ANN module ("HLint: ignore Reduce duplication" :: String) #-} {-# ANN module ("HLint: ignore Redundant do" :: String) #-} parseLsOptions :: Parser LsOptions parseLsOptions = LsOptions <$> strOption ( long "source" <> metavar "FILE" <> value "-" <> help "Input file" ) <*> strOption ( long "target" <> metavar "FILE" <> value "-" <> help "Output file" ) commandLs :: Parser (IO ()) commandLs = runResourceT . runDump <$> parseLsOptions runDump :: MonadResource m => LsOptions -> m () runDump opt = do (_, hIn) <- openFileOrStd (opt ^. the @"source") IO.ReadMode (_, hOut) <- openFileOrStd (opt ^. the @"target") IO.WriteMode 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 = zip filenames segments forM_ (zip [0..] namedSegments) $ \(i :: Int, (filename, segment)) -> do let meta = segment ^. the @"meta" liftIO $ IO.hPutStrLn hOut $ mempty <> (show i & trimPadLeft 4) <> " " <> (meta ^. the @"createTime" <&> showPosixSeconds & fromMaybe "" & trimPadLeft 19) <> " " <> (meta ^. the @"format" <&> show & fromMaybe "" & trimPadRight 20) <> " " <> (segment ^. the @"payload" & showLbsLength & trimPadLeft 12) <> " " <> T.unpack filename where magic = AP.string "seg:" *> (BS.pack <$> many AP.anyWord8) AP. "\"seg:????\"" showLbsLength :: LBS.ByteString -> String showLbsLength = show . LBS.length showPosixSeconds :: POSIXTime -> String showPosixSeconds t = take 19 (show (posixSecondsToUTCTime t)) trimPadLeft :: Int -> String -> String trimPadLeft n s | len < n = replicate (n - len) ' ' <> s | len == n = s | otherwise = '<':reverse (take (n - 1) (reverse s)) where len = length s trimPadRight :: Int -> String -> String trimPadRight n s | len < n = s <> replicate (n - len) ' ' | len == n = s | otherwise = take (n - 1) s <> ">" where len = length s