snipcheck (empty) → 0.1.0.0
raw patch · 5 files changed
+147/−0 lines, 5 filesdep +basedep +pandocdep +processsetup-changed
Dependencies added: base, pandoc, process
Files
- LICENSE +19/−0
- README.md +28/−0
- Setup.hs +2/−0
- snipcheck.cabal +28/−0
- src/Snipcheck.hs +70/−0
+ LICENSE view
@@ -0,0 +1,19 @@+Copyright (c) 2017 Nicolas Mattia++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ README.md view
@@ -0,0 +1,28 @@+# Snipcheck++Snipcheck makes sure that the code snippets in markdown files are up-to-date.++This is very much a work in progress. The only function currently available is++``` haskell+checkMarkdownFile :: FilePath -> IO ()+```++that will run shell snippets and errored out if the output doesn't match the+snippet. You can skip some of the output with `...`.++## Example:++ # Some title++ some markdown content++ ``` shell+ $ echo foo; echo bar; echo baz; echo qux+ foo+ ...+ qux+ ```++ some more content+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ snipcheck.cabal view
@@ -0,0 +1,28 @@+name: snipcheck+version: 0.1.0.0+cabal-version: >=1.10+build-type: Simple+license: MIT+license-file: LICENSE+copyright: 2017 Nicolas Mattia+maintainer: nicolas@nmattia.com+homepage: https://github.com/nmattia/snipcheck#readme+synopsis: Markdown tester+description:+ Markdown snippet runner and checker+category: Development+author: Nicolas Mattia+extra-source-files:+ README.md++library+ exposed-modules:+ Snipcheck+ build-depends:+ base >=4.7 && <5,+ pandoc >=1.19.2.1 && <1.20,+ process >=1.4.3.0 && <1.5+ default-language: Haskell2010+ hs-source-dirs: src+ ghc-options: -Wall+
+ src/Snipcheck.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE DeriveFunctor #-}++module Snipcheck where++import Control.Monad+import Data.Maybe+import Data.Monoid+import System.Process(readCreateProcess, shell)+import Text.Pandoc (Block(..))++import qualified Text.Pandoc as Pandoc++data Sloppy a = Skip | Must a deriving (Show, Functor)++sloppyString :: String -> Sloppy String+sloppyString "..." = Skip+sloppyString str = Must str++checkSloppy :: Eq a => [a] -> [Sloppy a] -> Bool+checkSloppy (a:as) ((Must a'):as')+ | a == a' = checkSloppy as as'+ | otherwise = False+checkSloppy (a:as) as'@(Skip:(Must a'):as'')+ | a == a' = checkSloppy as as''+ | otherwise = checkSloppy as as'+checkSloppy as (Skip:Skip:as') = checkSloppy as (Skip:as')+checkSloppy [] ((Must _):_) = False+checkSloppy [] (Skip:as') = checkSloppy [] as'+checkSloppy [] [] = True+checkSloppy (_:_) [] = False+checkSloppy _ [Skip] = True++checkMarkdownFile :: FilePath -> IO ()+checkMarkdownFile fp = do+ content <- readFile fp+ let (Right (Pandoc.Pandoc _meta blocks)) = Pandoc.readMarkdown Pandoc.def content+ forM_ blocks check++check :: Pandoc.Block -> IO ()+check (CodeBlock (typ, classes, kvs) content)+ | "shell" `elem` classes = do+ let Right cmds = extractCommands content+ forM_ cmds $ \(cmd, expected) -> do+ actual <- lines <$> readCreateProcess (shell cmd) ""+ let expected' = sloppyString <$> expected+ unless (checkSloppy actual expected') $ error $ mconcat+ [ "Couldnt match expected ", show expected'+ , " with " <> show actual+ ]+ | otherwise = print (typ, classes, kvs)+check _ = return ()++extractCommands :: String -> Either String [(String, [String])]+extractCommands str = go (lines str)+ where+ go :: [String] -> Either String [(String, [String])]+ go (l:ls) | Just cmd <- toCommand l =+ let (output, rest) = span (not . isCommand) ls+ in ((cmd,output):) <$> (go rest)+ | otherwise = Left $ "Expected a command, got " <> l+ go [] = Right []+ toCommand :: String -> Maybe String+ toCommand ('$':cmd) = Just cmd+ toCommand _ = Nothing+ isCommand :: String -> Bool+ isCommand = isJust . toCommand+++someFunc :: IO ()+someFunc = putStrLn "someFunc"