module Main where import qualified Data.Aeson as Aeson import qualified Data.ByteString.Lazy as BL import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Text.IO as TIO import Data.Version (showVersion) import LCJF (prettyLeadingComma) import Options.Applicative import qualified Paths_lcjf as Paths import System.Exit (exitFailure) import System.IO (hPutStrLn, stderr) data Options = Options { optInputFile :: Maybe FilePath , optOutputFile :: Maybe FilePath , optInplace :: Maybe FilePath , optInputString :: Maybe String } deriving (Show) optionsParser :: Parser Options optionsParser = Options <$> optional ( strOption ( long "file" <> short 'f' <> metavar "FILE" <> help "Read JSON from FILE instead of stdin" ) ) <*> optional ( strOption ( long "output" <> short 'o' <> metavar "FILE" <> help "Write output to FILE instead of stdout" ) ) <*> optional ( strOption ( long "inplace" <> short 'i' <> metavar "FILE" <> help "Format FILE in place (equivalent to -f FILE -o FILE)" ) ) <*> optional ( strArgument ( metavar "JSON" <> help "JSON string to format (if not provided, reads from stdin or file)" ) ) versionOption :: Parser (a -> a) versionOption = infoOption (showVersion Paths.version) ( long "version" <> short 'v' <> help "Show version number" ) optsInfo :: ParserInfo Options optsInfo = info (optionsParser <**> versionOption <**> helper) ( fullDesc <> progDesc "Format JSON with leading commas in the style of Haskell/Elm" <> header "lcjf - Leading-Comma JSON Formatter" ) main :: IO () main = do opts <- execParser optsInfo let (inputSource, outputDest) = resolveIO opts input <- readInput inputSource (optInputString opts) case Aeson.decode input of Nothing -> do hPutStrLn stderr "Error: Invalid JSON input" exitFailure Just val -> writeOutput outputDest (prettyLeadingComma val) resolveIO :: Options -> (Maybe FilePath, Maybe FilePath) resolveIO opts = case optInplace opts of Just fp -> (Just fp, Just fp) Nothing -> (optInputFile opts, optOutputFile opts) readInput :: Maybe FilePath -> Maybe String -> IO BL.ByteString readInput _ (Just jsonStr) = pure (BL.fromStrict (TE.encodeUtf8 (T.pack jsonStr))) readInput Nothing _ = BL.getContents readInput (Just fp) _ = BL.readFile fp writeOutput :: Maybe FilePath -> T.Text -> IO () writeOutput Nothing txt = TIO.putStrLn txt writeOutput (Just fp) txt = TIO.writeFile fp (txt <> T.singleton '\n')