diff --git a/README.md b/README.md index 9c67beb..369b0e2 100644 --- a/README.md +++ b/README.md @@ -3,17 +3,36 @@ Hakyll compilers for passing Hakyll items to external processes. This is useful for tools that do not have Haskell bindings or may not make sense to have Haskell bindings, like Typescript or LaTex compilers. -## Example Usage: +## Common Usage + +There are a few main entry points for this library: + +- `execCompilerWith` is the most common. This allows the caller to declaratively run an external process + and includes support for passing arguments. +- `execCompiler` is an aliased version of `execCompilerWith` that provides no arguments to the external executable/process. +- `unsafeExecCompiler` is the less type safe and more manual method to calling external executables. This provides + no helper functionality but may be useful if you are already manually building out a compiler in your site generator. + +### Example This example shows how this library can be used to include latex files in your site source and include the output pdf in your target site. ```haskell +import qualified Data.ByteString.Lazy.Char8 as B import Hakyll.Process main = do hakyll $ do match "resume/*.tex" $ do route $ setExtension "pdf" - compile $ execCompilerWith (execName "xelatex") [ProcArg "-output-directory=resume/", HakFilePath] (newExtOutFilePath ("pdf")) + compile $ execCompilerWith (execName "xelatex") [ProcArg "-output-directory=resume/", HakFilePath] (newExtOutFilePath "pdf") + + -- alternative, manual method, using unsafeExecCompiler + match "resume/*.tex" $ do + route $ setExtension "pdf" + compile $ do + input <- getResourceFilePath + let outputReader _ = B.readFile (newExtension "pdf" input) + unsafeExecCompiler (execName "xelatex") ["-output-directory=resume/", input] outputReader ``` diff --git a/hakyll-process.cabal b/hakyll-process.cabal index aab8cd3..98b1891 100644 --- a/hakyll-process.cabal +++ b/hakyll-process.cabal @@ -1,5 +1,5 @@ name: hakyll-process -version: 0.0.1.0 +version: 0.0.2.0 synopsis: Hakyll compiler for arbitrary external processes. description: Exposes Hakyll compilers for passing file paths to external processes. Transformed results are made available as Hakyll `Items`. diff --git a/src/Hakyll/Process.hs b/src/Hakyll/Process.hs index 1dd4dea..fb9aa33 100644 --- a/src/Hakyll/Process.hs +++ b/src/Hakyll/Process.hs @@ -1,3 +1,8 @@ +{-| +Module : Hakyll.Process +Description : Common compilers and helpers for external executables. +Stability : experimental +-} module Hakyll.Process ( newExtension @@ -5,6 +10,7 @@ module Hakyll.Process , execName , execCompiler , execCompilerWith + , unsafeExecCompiler , CompilerOut(..) , ExecutableArg(..) , ExecutableArgs @@ -46,7 +52,10 @@ type ExecutableArgs = [ExecutableArg] -- | Helper function to indicate that the output file name is the same as the input file name with a new extension -- Note: like hakyll, assumes that no "." is present in the extension -newExtension :: String -> FilePath -> FilePath +newExtension :: + String -- ^ New file extension, excluding the leading "." + -> FilePath -- ^ Original FilePath + -> FilePath newExtension ext f = (reverse . dropWhile (/= '.') . reverse $ f) <> ext -- | Helper function to indicate that the output file name is the same as the input file name with a new extension @@ -54,11 +63,12 @@ newExtension ext f = (reverse . dropWhile (/= '.') . reverse $ f) <> ext newExtOutFilePath :: String -> CompilerOut newExtOutFilePath ext = COutFile $ RelativePath (newExtension ext) -execName :: String -> ExecutableName +execName :: String -> ExecutableName execName = ExecutableName -- | Calls the external compiler with no arguments. Returns the output contents as a 'B.ByteString'. -- If an error occurs this raises an exception. +-- May be useful if you already have build scripts for artifacts in your repository. execCompiler :: ExecutableName -> CompilerOut -> Compiler (Item B.ByteString) execCompiler name out = execCompilerWith name [] out @@ -68,22 +78,36 @@ execCompilerWith :: ExecutableName -> ExecutableArgs -> CompilerOut -> Compiler execCompilerWith name exArgs out = do input <- getResourceFilePath let args = fmap (hargToArg input) exArgs - results <- unsafeCompiler $ runExecutable name args out input - -- just using this to get at the item - oldBody <- getResourceString - pure $ itemSetBody results oldBody + let outputReader = cOutToFileContents input out + unsafeExecCompiler name args outputReader -runExecutable :: ExecutableName -> [String] -> CompilerOut -> FilePath -> IO B.ByteString -runExecutable (ExecutableName exName) args compilerOut inputFile = withProcessWait procConf waitOutput where +-- | Primarily for internal use, occasionally useful when already building a compiler imperatively. +-- Allows the caller to opt out of the declarative components of 'execCompiler' and 'execCompilerWith'. +unsafeExecCompiler :: + ExecutableName -- ^ Name or filepath of the executable + -> [String] -- ^ Arguments to pass to the executable + -> (B.ByteString -> IO B.ByteString) -- ^ Action to read the output of the compiler. Input is the stdout of the process. + -> Compiler (Item B.ByteString) +unsafeExecCompiler (ExecutableName exName) args outputReader = + do + results <- unsafeCompiler $ procResults + -- just using this to get at the item + oldBody <- getResourceString + pure $ itemSetBody results oldBody + where + procResults = withProcessWait procConf waitOutput procConf = setStdout byteStringOutput . proc exName $ args waitOutput process = do let stmProc = getStdout process out <- atomically stmProc checkExitCode process - case compilerOut of - CStdOut -> pure out - COutFile (SpecificPath f) -> B.readFile f - COutFile (RelativePath f) -> B.readFile (f inputFile) + outputReader out + +-- input fpath stdout contents +cOutToFileContents :: FilePath -> CompilerOut -> B.ByteString -> IO B.ByteString +cOutToFileContents _ CStdOut out = pure out +cOutToFileContents _ (COutFile (SpecificPath f)) _ = B.readFile f +cOutToFileContents input (COutFile (RelativePath f)) _ = B.readFile (f input) hargToArg :: FilePath -> ExecutableArg -> String hargToArg _ (ProcArg s) = s