polyglot-0.1.1.0: app/CommandLine/FileIO.hs
module CommandLine.FileIO where
import CommandLine.Options (Language (..), Opts (..))
import Lexers.Haskell.Layout (adaptLayout)
import Control.Monad ((<=<))
import Control.Parallel.Strategies (parMap, rseq)
import Data.List (isPrefixOf)
import Data.Text (Text, pack)
import Data.Text.Encoding (decodeUtf8, encodeUtf8)
import Utils.Foldable (andPred, orPred, wrapMaybe)
import Utils.Functor ((<<$>>))
import Utils.String (wrapBlock, wrapContext)
import System.Directory (canonicalizePath)
import System.Directory.Tree (AnchoredDirTree (..), DirTree (..))
import System.FilePath (joinPath, normalise, splitDirectories, splitPath,
takeDirectory, takeExtensions, takeFileName,
(-<.>), (</>))
import System.FSNotify (ActionPredicate, Event (..))
import qualified Data.ByteString as B (readFile, writeFile)
import qualified Conversions.ToPurescript.ModuleDef as ToPurescript
import qualified Conversions.ToScala.ModuleDef as ToScala
import qualified Parsers.Haskell.ModuleDef as Parser
import Bookhound.Parser (ParseError, runParser)
import Utils.String (wrapNewLines)
toTargetLanguage :: Language -> Text -> Either [ParseError] String
toTargetLanguage language = convert <=< adaptLayout
where
convert = syntaxConverter <<$>> runParser Parser.moduleDef
syntaxConverter = case language of
Purescript -> show . ToPurescript.moduleDef
Scala -> show . ToScala.moduleDef
convertDirTree :: Language -> DirTree Text -> DirTree Text
convertDirTree language (File x y)
| isHaskellFile x = applyTransform y
where
applyTransform = either (Failed x . userError . wrapNewLines
. wrapContext . ("Parse Errors: " ++)
. wrapBlock)
(File $ pathToLanguage language x)
. (pack <$>) . toTargetLanguage language
convertDirTree language (Dir x y) = Dir x (parMap rseq (convertDirTree language) y)
convertDirTree _ x = x
moveTree :: FilePath -> AnchoredDirTree a -> AnchoredDirTree a
moveTree fp2 (_ :/ x@(Dir _ _)) = "." :/ newDirTree
where
newDirTree = foldr (\curr acc -> Dir curr [acc]) prunedDirTree
(init outputDirs)
prunedDirTree = Dir (last outputDirs)
(getDirTreeContents 1 x)
outputDirs = splitDirectories $ takeDirectory $ normalise fp2
moveTree _ x = x
reportFailure :: DirTree a -> IO ()
reportFailure (Failed x y) = putStrLn $
"\nFailure when converting file " ++ x ++ ": " ++ show y
reportFailure _ = pure ()
watchPred :: Foldable t => t FilePath -> ActionPredicate
watchPred x (Added fp _ _) = filePathPred fp x
watchPred x (Modified fp _ _) = filePathPred fp x
watchPred x (Removed fp _ _) = filePathPred fp x
watchPred _ _ = False
filePathPred :: Foldable t => FilePath -> t FilePath -> Bool
filePathPred fp x = isHaskellFile fp
&& all (== fileName) x
where
fileName = takeFileName fp
getWatchPath :: FilePath -> Opts -> IO FilePath
getWatchPath fp Opts{sourcePath, targetPath} =
maybe targetPath (targetPath </>) . wrapMaybe <$> prunedPath
where
prunedPath = diffPath <$> traverse canonicalizePath (fp, sourcePath)
diffPath (fp1, fp2) = joinPath $ drop (length $ splitPath fp2)
(splitPath fp1)
getDirTreeContents :: Int -> DirTree a -> [DirTree a]
getDirTreeContents n (Dir _ x)
| n > 0 = x >>= getDirTreeContents (n - 1)
getDirTreeContents _ x = [x]
isDir :: FilePath -> Bool
isDir = null . takeFileName
isHaskellFile :: FilePath -> Bool
isHaskellFile = (`elem` [".hs", ".lhs"]) . takeExtensions
isDotFile :: String -> Bool
isDotFile = andPred [isPrefixOf ".", (/= "."), (/= "..")]
pathToLanguage :: Language -> FilePath -> FilePath
pathToLanguage language = (-<.> extension)
where
extension = case language of
Purescript -> "purs"
Scala -> "scala"
formatterExec :: Language -> FilePath
formatterExec Purescript = "purs-tidy"
formatterExec Scala = "scalafmt"
emitError :: [ParseError] -> IO ()
emitError = fail . ("\n\nParse Errors: " ++) . wrapBlock
dirNamePred :: String -> Bool
dirNamePred = not . orPred [isDotFile, (== "bin")]
dirPred :: DirTree a -> Bool
dirPred (Dir x _) = dirNamePred x
dirPred _ = True
readFileUtf8 :: FilePath -> IO Text
readFileUtf8 fp | all dirNamePred (splitDirectories fp)
= decodeUtf8 <$> B.readFile fp
readFileUtf8 _ = pure mempty
writeFileUtf8 :: FilePath -> Text -> IO ()
writeFileUtf8 fp = B.writeFile fp . encodeUtf8