packages feed

dhall-check (empty) → 1.0.0.0

raw patch · 4 files changed

+248/−0 lines, 4 filesdep +basedep +containersdep +dhallsetup-changed

Dependencies added: base, containers, dhall, directory, filepath, fsnotify, text, trifecta

Files

+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Anton Felix Lorenzen (c) 2017++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++    * Neither the name of Anton Felix Lorenzen nor the names of other+      contributors may be used to endorse or promote products derived+      from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ dhall-check.cabal view
@@ -0,0 +1,36 @@+-- This file has been generated from package.yaml by hpack version 0.17.0.+--+-- see: https://github.com/sol/hpack++name:           dhall-check+version:        1.0.0.0+synopsis:       Check all dhall files in a project+category:       Development+homepage:       https://github.com/anfelor/dhall-check#readme+bug-reports:    https://github.com/anfelor/dhall-check/issues+author:         Anton Felix Lorenzen+maintainer:     anfelor@posteo.de+copyright:      BSD3+license:        BSD3+license-file:   LICENSE+build-type:     Simple+cabal-version:  >= 1.10++source-repository head+  type: git+  location: https://github.com/anfelor/dhall-check++executable dhall-check+  main-is: Main.hs+  hs-source-dirs:+      src+  build-depends:+      base >= 4.7 && < 5+    , containers+    , directory+    , dhall+    , filepath+    , fsnotify+    , trifecta+    , text+  default-language: Haskell2010
+ src/Main.hs view
@@ -0,0 +1,180 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-}++module Main where++import Control.Concurrent (threadDelay)+import Control.Exception hiding (TypeError)+import Control.Monad+import Data.Char+import Data.Map (Map)+import Data.Monoid+import Dhall+import Dhall.Core+import Dhall.Parser+import Dhall.Import+import Dhall.TypeCheck+import qualified Data.Map as Map+import qualified Data.Text.Lazy as TL+import qualified Data.Text.Lazy.IO as TLIO+import System.Directory+import System.FilePath+import System.FSNotify+import Text.Trifecta.Delta (Delta(..))++import Debug.Trace+++-- | A dhall type, e.g. "entry" for "file.entry.dh"+-- Should only contain lower-case strings.+newtype DhallType = DhallType String+  deriving (Eq, Ord, Show)++-- | Turn a string into a 'DhallType'.+-- >>> dhallType "Entry"+-- DhallType "entry"+dhallType :: String -> DhallType+dhallType = DhallType . map toLower++-- | Get the 'DhallType' for a file.+-- >>> dhallTypeOf "file.entry.dh"+-- DhallType "entry"+dhallTypeOf :: FilePath -> DhallType+dhallTypeOf = DhallType . tail . map toLower . takeExtension . dropExtension+++data CheckException+  = CouldntFindTypeDefs+  | ParseException FilePath ParseError+  | UnknownType FilePath DhallType+  | TypeChecking FilePath (TypeError Src)+  deriving (Show)++instance Exception CheckException where+  displayException CouldntFindTypeDefs =+    "Error: Expected a .dhall file with type definitions, but couldn't find it."+  displayException (UnknownType f (DhallType t)) =+    "Error: The file " ++ f ++ "has an unknown type: " ++ t+  displayException (ParseException f pe) =+    "While parsing " ++ f ++ ":" ++ displayException pe+  displayException (TypeChecking f te) =+    "While type checking " ++ f ++ ":" ++ displayException te+++main :: IO ()+main = do+  here <- getCurrentDirectory+  printExceptions $ do+    typedefs <- allTypeDefs here+    dhallfiles <- allDhallFiles here+    -- Check all files once+    printExceptions $ do+      checkAll typedefs dhallfiles+      putStrLn "No errors."+    -- and then watch for changes+    withManager $ \mgr -> do+      watchTree mgr here isDhallFile $ \event -> do+        case event of+          Added f utc -> do+            putStrLn $ "\nAdded: " ++ f+            printExceptions $ do+              expr <- loadFile f+              checkFile typedefs (f, expr)+              putStrLn "No errors."+          Modified f utc -> do+            putStrLn $ "\nModified: " ++ f+            printExceptions $ do+              expr <- loadFile f+              checkFile typedefs (f, expr)+              putStrLn "No errors."+          Removed f utc -> pure ()+      forever $ threadDelay 1000000+  where+    -- | Print 'CheckException's and recover.+    printExceptions comp = do+      result <- try comp+      case result of+        Left e -> putStrLn $ displayException (e :: CheckException)+        Right v -> pure v++    isDhallFile f = (flip elem [".dhall", ".dh"] . takeExtension . eventPath) f+                 && (not . (==) '.' . head . takeFileName . eventPath) f+++-- | A list of all files in a directory with the one of the given extensions,+-- using absolute paths.+--+-- >>> allFiles "/home/user" [".hs"]+-- ["/home/user/project/Main.hs", ...]+allFiles :: Foldable t => FilePath -> t String -> IO [FilePath]+allFiles dir exts = do+  setCurrentDirectory dir+  files <- mapM makeAbsolute =<< listDirectory dir+  inThisDir <- flip filterM files $ \f -> do+    exists <- doesFileExist f+    let valid = takeExtension f `elem` exts+    pure $ exists && valid+  subdirs <- filterM doesDirectoryExist files+  inSubDirs <- forM subdirs $ \d -> do+    allFiles (dir </> d) exts+  pure $ inThisDir ++ concat inSubDirs+++-- | Get all type definitions in the ".dhall" directory of a given directory+-- and return them in a map of filename without ".dht" -> expression in file.+allTypeDefs :: FilePath -> IO (Map DhallType (Expr Src X))+allTypeDefs dir = do+  isThere <- doesDirectoryExist (dir </> ".dhall")+  when (not isThere) $ do+    throwIO CouldntFindTypeDefs+  files <- allFiles (dir </> ".dhall") [".dht"]+  exprs <- forM files $ \f -> do+    let name = dhallType $ takeFileName $ dropExtension f+    sequence (name, loadFile f)+  pure $ Map.fromList exprs+++-- | Get all a list of all dhall files in the given directory using absolute paths.+allDhallFiles :: FilePath -> IO [(FilePath, Expr Src X)]+allDhallFiles dir = do+  files <- allFiles dir [".dh", ".dhall"]+  forM files $ \f -> do+    sequence (f, loadFile f)+++-- | Compile a dhall file.+loadFile :: FilePath -> IO (Expr Src X)+loadFile f = do+  let delta = Directed "(input)" 0 0 0 0+  content <- TLIO.readFile f+  case exprFromText delta content of+    Left e -> throwIO $ ParseException f e+    Right e -> load e+++-- | Typecheck a single file against one of the given types.+-- Throws an exception on failure to do so.+--+-- >>> checkFile (Map.fromList [("conf", .. )]) ("file.conf.dh", ..)+-- /throws/ TypeChecking "file.conf.dh" (TypeError ..)+checkFile :: Map DhallType (Expr Src X) -> (FilePath, Expr Src X) -> IO ()+checkFile types (f, expr) = do+  let name = dhallTypeOf f+  case Map.lookup name types of+    Nothing -> do+      throwIO $ UnknownType f name+    Just t -> do+      let annot = case (expr, t) of+            (Note (Src begin1 end1 bytes1) _, Note (Src begin2 end2 bytes2) _) ->+              Note (Src begin1 end1 bytes') (Annot expr t)+              where+                bytes' = bytes1 <> "\n\n : \n\n" <> bytes2+            _ -> Annot expr t+      case typeOf annot of+        Left err -> throwIO $ TypeChecking f err+        Right _ -> pure ()+++-- | Perform 'checkFile' on every file in the Foldable.+checkAll :: Foldable t => Map DhallType (Expr Src X) -> t (FilePath, Expr Src X) -> IO ()+checkAll types = mapM_ (checkFile types)