document-build-0.2.0.0: src/Document/Build/Error.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall #-}
{- | Errors that occur while building documents.
A 'BuildError' is a sum: each constructor has a name, a prism. A constructor
with two or more fields holds a product type of its own, such as
'ProcessFailed', whose own name is the constructor's name. 'BuildError'
inherits the names of every type it holds: those it reaches through two or
more constructors, such as 'commandLine' and 'file', are affine traversals.
Code that fails with a 'BuildError', in an error type @e@ that it builds with
@'review' 'buildError'@, has the constraint @'ReviewBuildError' e@.
-}
module Document.Build.Error (
-- * Type
BuildError (..),
ReviewBuildError,
-- * Errors
buildErrorExitCode,
displayBuildError,
-- * Optics
BuildErrorLike (..),
lawNamesBuildError,
InheritBuildError,
inheritBuildError,
inheritsBuildError,
) where
import Control.DeepSeq (NFData)
import Control.Lens (Choice, Optic, prism', review)
import Data.Data (Data)
import Data.Foldable (toList)
import Data.Functor.Const (Const)
import Data.Functor.Identity (Identity)
import Data.Proxy (Proxy (..))
import Data.Tagged (Tagged)
import Document.Build.CommandLine (CommandLineLike (..), displayCommandLine)
import Document.Build.Error.FileTypeUnsupported (FileTypeUnsupported (..), FileTypeUnsupportedLike (..), inheritFileTypeUnsupported)
import Document.Build.Error.InputFilesTooFew (InputFilesTooFew (..), InputFilesTooFewLike (..), inheritInputFilesTooFew)
import Document.Build.Error.OutputFileMissing (OutputFileMissing (..), OutputFileMissingLike (..), inheritOutputFileMissing)
import Document.Build.Error.OutputNotInDirectory (OutputNotInDirectory (..), OutputNotInDirectoryLike (..), inheritOutputNotInDirectory)
import Document.Build.Error.ProcessFailed (ProcessFailed (..), ProcessFailedLike (..), inheritProcessFailed)
import Document.Build.Error.ProcessOutputUnrecognised (ProcessOutputUnrecognised (..), ProcessOutputUnrecognisedLike (..), inheritProcessOutputUnrecognised)
import Document.Build.Error.StringsMissing (StringsMissing (..), StringsMissingLike (..), inheritStringsMissing)
import Document.Build.Name.CommandLineArguments (CommandLineArgumentsLike (..))
import Document.Build.Name.CommandLineProgram (CommandLineProgramLike (..))
import Document.Build.Name.Directory (DirectoryLike (..))
import Document.Build.Name.ExpectedOutput (ExpectedOutputLike (..))
import Document.Build.Name.FileNotInGit (FileNotInGitLike (..))
import Document.Build.Name.FileNotUtf8 (FileNotUtf8Like (..))
import Document.Build.Name.InputFileCount (InputFileCountLike (..))
import Document.Build.Name.InputFileMissing (InputFileMissingLike (..))
import Document.Build.Name.InputFilesNeeded (InputFilesNeededLike (..))
import Document.Build.Name.MissingStrings (MissingStringsLike (..))
import Document.Build.Name.OutputDirectory (OutputDirectoryLike (..))
import Document.Build.Name.ShallowRepository (ShallowRepositoryLike (..))
import Document.Build.Name.StandardError (StandardErrorLike (..))
import Document.Build.Name.StandardOutput (StandardOutputLike (..))
import Document.Build.Name.SupportedExtensions (SupportedExtensionsLike (..))
import GHC.Generics (Generic)
import Natural (NotZero, notZeroOr)
import System.FilePather.Cycle (Cycle (..), CycleLike (..), displayCycle, inheritCycle)
import System.FilePather.Name.File (FileLike (..))
import System.FilePather.Name.StepInput (StepInputLike (..))
import System.FilePather.Name.StepName (StepNameLike (..))
import System.FilePather.Name.Steps (StepsLike (..))
import System.FilePather.RepeatsStep (RepeatsStepLike (..))
import System.FilePather.ReproducesInput (ReproducesInputLike (..))
import System.FilePather.Step (StepLike (..))
import System.FilePather.Trail (TrailLike (..))
import Prelude hiding (cycle)
{- $setup
>>> import Control.Lens (preview, review)
>>> import Data.List.NonEmpty (NonEmpty (..))
-}
{- | An error that occurs while building documents.
>>> preview inputFileMissing (InputFileMissing "report.fodt")
Just "report.fodt"
>>> review fileNotUtf8 "report.odt" :: BuildError
FileNotUtf8 "report.odt"
>>> preview file (BuildErrorStringsMissing (StringsMissing "report.fodt" ("${UPDATED}" :| [])))
Just "report.fodt"
@since 0.1.0.0
-}
data BuildError
= -- | A process exited with a non-zero exit code, with its standard output
-- and standard error.
-- Its name is 'processFailed'.
BuildErrorProcessFailed ProcessFailed
| -- | The output of a process was not recognised, with a description of what
-- was expected.
-- Its name is 'processOutputUnrecognised'.
BuildErrorProcessOutputUnrecognised ProcessOutputUnrecognised
| -- | An input file does not exist.
-- Its name is 'inputFileMissing'.
InputFileMissing FilePath
| -- | A process succeeded, but did not produce an output file it should
-- have.
-- Its name is 'outputFileMissing'.
BuildErrorOutputFileMissing OutputFileMissing
| -- | A file does not contain strings it is required to contain.
-- Its name is 'stringsMissing'.
BuildErrorStringsMissing StringsMissing
| -- | A file does not have one of the supported extensions.
-- Its name is 'fileTypeUnsupported'.
BuildErrorFileTypeUnsupported FileTypeUnsupported
| -- | A file has no history in git.
-- Its name is 'fileNotInGit'.
FileNotInGit FilePath
| -- | A file is not valid UTF-8 text. ('fileNotUtf8')
FileNotUtf8 FilePath
| -- | A chain of build steps was stopped, because it would go round in a
-- cycle.
-- Its name is 'cycle'.
BuildCycle Cycle
| -- | A directory has fewer input files than a command needs.
-- Its name is 'inputFilesTooFew'.
BuildErrorInputFilesTooFew InputFilesTooFew
| -- | A build step made a file outside the output directory.
-- Its name is 'outputNotInDirectory'.
BuildErrorOutputNotInDirectory OutputNotInDirectory
| -- | The git history of a directory is shallow, so the last commit to
-- change a file in it is not known.
-- Its name is 'shallowRepository'.
ShallowRepository FilePath
deriving stock (Eq, Ord, Show, Generic, Data)
deriving anyclass (NFData)
{- | The constraint of code that fails with a 'BuildError' in the error type
@e@: a 'BuildError' is made into an @e@ with @'review' 'buildError'@.
@since 0.2.0.0
-}
type ReviewBuildError e =
BuildErrorLike Tagged Identity e e
{- | The class of 'BuildError'.
@since 0.2.0.0
-}
class BuildErrorLike p f x x' where
buildError ::
Optic p f x x' BuildError BuildError
{- | Identity
@since 0.2.0.0
-}
instance (x' ~ BuildError) => BuildErrorLike p f BuildError x' where
buildError =
id
{-# INLINE buildError #-}
{- | Prism: 'processFailed' is the default name of 'BuildErrorProcessFailed'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => ProcessFailedLike p f BuildError x' where
processFailed =
prism'
BuildErrorProcessFailed
( \case
BuildErrorProcessFailed x -> Just x
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE processFailed #-}
{- | Prism: 'processOutputUnrecognised' is the default name of 'BuildErrorProcessOutputUnrecognised'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => ProcessOutputUnrecognisedLike p f BuildError x' where
processOutputUnrecognised =
prism'
BuildErrorProcessOutputUnrecognised
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised x -> Just x
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE processOutputUnrecognised #-}
{- | Prism
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => InputFileMissingLike p f BuildError x' where
inputFileMissing =
prism'
InputFileMissing
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing x -> Just x
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE inputFileMissing #-}
{- | Prism: 'outputFileMissing' is the default name of 'BuildErrorOutputFileMissing'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => OutputFileMissingLike p f BuildError x' where
outputFileMissing =
prism'
BuildErrorOutputFileMissing
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing x -> Just x
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE outputFileMissing #-}
{- | Prism: 'stringsMissing' is the default name of 'BuildErrorStringsMissing'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => StringsMissingLike p f BuildError x' where
stringsMissing =
prism'
BuildErrorStringsMissing
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing x -> Just x
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE stringsMissing #-}
{- | Prism: 'fileTypeUnsupported' is the default name of 'BuildErrorFileTypeUnsupported'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => FileTypeUnsupportedLike p f BuildError x' where
fileTypeUnsupported =
prism'
BuildErrorFileTypeUnsupported
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported x -> Just x
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE fileTypeUnsupported #-}
{- | Prism
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => FileNotInGitLike p f BuildError x' where
fileNotInGit =
prism'
FileNotInGit
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit x -> Just x
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE fileNotInGit #-}
{- | Prism
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => FileNotUtf8Like p f BuildError x' where
fileNotUtf8 =
prism'
FileNotUtf8
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 x -> Just x
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE fileNotUtf8 #-}
{- | Prism: 'cycle' is the default name of 'BuildCycle'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => CycleLike p f BuildError x' where
cycle =
prism'
BuildCycle
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle x -> Just x
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE cycle #-}
{- | Prism: 'inputFilesTooFew' is the default name of 'BuildErrorInputFilesTooFew'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => InputFilesTooFewLike p f BuildError x' where
inputFilesTooFew =
prism'
BuildErrorInputFilesTooFew
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew x -> Just x
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository _ -> Nothing
)
{-# INLINE inputFilesTooFew #-}
{- | Prism: 'outputNotInDirectory' is the default name of 'BuildErrorOutputNotInDirectory'.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => OutputNotInDirectoryLike p f BuildError x' where
outputNotInDirectory =
prism'
BuildErrorOutputNotInDirectory
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory x -> Just x
ShallowRepository _ -> Nothing
)
{-# INLINE outputNotInDirectory #-}
{- | Prism
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => ShallowRepositoryLike p f BuildError x' where
shallowRepository =
prism'
ShallowRepository
( \case
BuildErrorProcessFailed _ -> Nothing
BuildErrorProcessOutputUnrecognised _ -> Nothing
InputFileMissing _ -> Nothing
BuildErrorOutputFileMissing _ -> Nothing
BuildErrorStringsMissing _ -> Nothing
BuildErrorFileTypeUnsupported _ -> Nothing
FileNotInGit _ -> Nothing
FileNotUtf8 _ -> Nothing
BuildCycle _ -> Nothing
BuildErrorInputFilesTooFew _ -> Nothing
BuildErrorOutputNotInDirectory _ -> Nothing
ShallowRepository x -> Just x
)
{-# INLINE shallowRepository #-}
{- | Affine traversal: 'commandLine' is reached through 3 constructors, once each: 'BuildErrorProcessFailed', 'BuildErrorProcessOutputUnrecognised', 'BuildErrorOutputFileMissing'.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => CommandLineLike p f BuildError x' where
commandLine f = \case
BuildErrorProcessFailed x -> BuildErrorProcessFailed <$> commandLine f x
BuildErrorProcessOutputUnrecognised x -> BuildErrorProcessOutputUnrecognised <$> commandLine f x
InputFileMissing x -> pure (InputFileMissing x)
BuildErrorOutputFileMissing x -> BuildErrorOutputFileMissing <$> commandLine f x
BuildErrorStringsMissing x -> pure (BuildErrorStringsMissing x)
BuildErrorFileTypeUnsupported x -> pure (BuildErrorFileTypeUnsupported x)
FileNotInGit x -> pure (FileNotInGit x)
FileNotUtf8 x -> pure (FileNotUtf8 x)
BuildCycle x -> pure (BuildCycle x)
BuildErrorInputFilesTooFew x -> pure (BuildErrorInputFilesTooFew x)
BuildErrorOutputNotInDirectory x -> pure (BuildErrorOutputNotInDirectory x)
ShallowRepository x -> pure (ShallowRepository x)
{-# INLINE commandLine #-}
{- | Affine traversal: through 'BuildErrorProcessFailed', a prism, then 'ProcessFailed''s 'standardOutput', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StandardOutputLike p f BuildError x' where
standardOutput =
processFailed . standardOutput
{-# INLINE standardOutput #-}
{- | Affine traversal: through 'BuildErrorProcessFailed', a prism, then 'ProcessFailed''s 'standardError', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StandardErrorLike p f BuildError x' where
standardError =
processFailed . standardError
{-# INLINE standardError #-}
{- | Affine traversal: 'commandLineProgram' is reached through 3 constructors, once each: 'BuildErrorProcessFailed', 'BuildErrorProcessOutputUnrecognised', 'BuildErrorOutputFileMissing'.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => CommandLineProgramLike p f BuildError x' where
commandLineProgram f = \case
BuildErrorProcessFailed x -> BuildErrorProcessFailed <$> commandLineProgram f x
BuildErrorProcessOutputUnrecognised x -> BuildErrorProcessOutputUnrecognised <$> commandLineProgram f x
InputFileMissing x -> pure (InputFileMissing x)
BuildErrorOutputFileMissing x -> BuildErrorOutputFileMissing <$> commandLineProgram f x
BuildErrorStringsMissing x -> pure (BuildErrorStringsMissing x)
BuildErrorFileTypeUnsupported x -> pure (BuildErrorFileTypeUnsupported x)
FileNotInGit x -> pure (FileNotInGit x)
FileNotUtf8 x -> pure (FileNotUtf8 x)
BuildCycle x -> pure (BuildCycle x)
BuildErrorInputFilesTooFew x -> pure (BuildErrorInputFilesTooFew x)
BuildErrorOutputNotInDirectory x -> pure (BuildErrorOutputNotInDirectory x)
ShallowRepository x -> pure (ShallowRepository x)
{-# INLINE commandLineProgram #-}
{- | Affine traversal: 'commandLineArguments' is reached through 3 constructors, once each: 'BuildErrorProcessFailed', 'BuildErrorProcessOutputUnrecognised', 'BuildErrorOutputFileMissing'.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => CommandLineArgumentsLike p f BuildError x' where
commandLineArguments f = \case
BuildErrorProcessFailed x -> BuildErrorProcessFailed <$> commandLineArguments f x
BuildErrorProcessOutputUnrecognised x -> BuildErrorProcessOutputUnrecognised <$> commandLineArguments f x
InputFileMissing x -> pure (InputFileMissing x)
BuildErrorOutputFileMissing x -> BuildErrorOutputFileMissing <$> commandLineArguments f x
BuildErrorStringsMissing x -> pure (BuildErrorStringsMissing x)
BuildErrorFileTypeUnsupported x -> pure (BuildErrorFileTypeUnsupported x)
FileNotInGit x -> pure (FileNotInGit x)
FileNotUtf8 x -> pure (FileNotUtf8 x)
BuildCycle x -> pure (BuildCycle x)
BuildErrorInputFilesTooFew x -> pure (BuildErrorInputFilesTooFew x)
BuildErrorOutputNotInDirectory x -> pure (BuildErrorOutputNotInDirectory x)
ShallowRepository x -> pure (ShallowRepository x)
{-# INLINE commandLineArguments #-}
{- | Affine traversal: through 'BuildErrorProcessOutputUnrecognised', a prism, then 'ProcessOutputUnrecognised''s 'expectedOutput', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => ExpectedOutputLike p f BuildError x' where
expectedOutput =
processOutputUnrecognised . expectedOutput
{-# INLINE expectedOutput #-}
{- | Affine traversal: 'file' is reached through 5 constructors, once each: 'BuildErrorOutputFileMissing', 'BuildErrorStringsMissing', 'BuildErrorFileTypeUnsupported', 'BuildCycle', 'BuildErrorOutputNotInDirectory'.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => FileLike p f BuildError x' where
file f = \case
BuildErrorProcessFailed x -> pure (BuildErrorProcessFailed x)
BuildErrorProcessOutputUnrecognised x -> pure (BuildErrorProcessOutputUnrecognised x)
InputFileMissing x -> pure (InputFileMissing x)
BuildErrorOutputFileMissing x -> BuildErrorOutputFileMissing <$> file f x
BuildErrorStringsMissing x -> BuildErrorStringsMissing <$> file f x
BuildErrorFileTypeUnsupported x -> BuildErrorFileTypeUnsupported <$> file f x
FileNotInGit x -> pure (FileNotInGit x)
FileNotUtf8 x -> pure (FileNotUtf8 x)
BuildCycle x -> BuildCycle <$> file f x
BuildErrorInputFilesTooFew x -> pure (BuildErrorInputFilesTooFew x)
BuildErrorOutputNotInDirectory x -> BuildErrorOutputNotInDirectory <$> file f x
ShallowRepository x -> pure (ShallowRepository x)
{-# INLINE file #-}
{- | Affine traversal: through 'BuildErrorStringsMissing', a prism, then 'StringsMissing''s 'missingStrings', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => MissingStringsLike p f BuildError x' where
missingStrings =
stringsMissing . missingStrings
{-# INLINE missingStrings #-}
{- | Affine traversal: through 'BuildErrorFileTypeUnsupported', a prism, then 'FileTypeUnsupported''s 'supportedExtensions', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => SupportedExtensionsLike p f BuildError x' where
supportedExtensions =
fileTypeUnsupported . supportedExtensions
{-# INLINE supportedExtensions #-}
{- | Prism: through 'BuildCycle', a prism, then 'Cycle''s 'reproducesInput', a prism.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => ReproducesInputLike p f BuildError x' where
reproducesInput =
cycle . reproducesInput
{-# INLINE reproducesInput #-}
{- | Prism: through 'BuildCycle', a prism, then 'Cycle''s 'repeatsStep', a prism.
@since 0.2.0.0
-}
instance (Choice p, Applicative f, x' ~ BuildError) => RepeatsStepLike p f BuildError x' where
repeatsStep =
cycle . repeatsStep
{-# INLINE repeatsStep #-}
{- | Affine traversal: through 'BuildCycle', a prism, then 'Cycle''s 'step', an affine traversal.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StepLike p f BuildError x' where
step =
cycle . step
{-# INLINE step #-}
{- | Affine traversal: through 'BuildCycle', a prism, then 'Cycle''s 'trail', an affine traversal.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => TrailLike p f BuildError x' where
trail =
cycle . trail
{-# INLINE trail #-}
{- | Affine traversal: through 'BuildCycle', a prism, then 'Cycle''s 'stepName', an affine traversal.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StepNameLike p f BuildError x' where
stepName =
cycle . stepName
{-# INLINE stepName #-}
{- | Affine traversal: through 'BuildCycle', a prism, then 'Cycle''s 'stepInput', an affine traversal.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StepInputLike p f BuildError x' where
stepInput =
cycle . stepInput
{-# INLINE stepInput #-}
{- | Affine traversal: through 'BuildCycle', a prism, then 'Cycle''s 'steps', an affine traversal.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => StepsLike p f BuildError x' where
steps =
cycle . steps
{-# INLINE steps #-}
{- | Affine traversal: through 'BuildErrorInputFilesTooFew', a prism, then 'InputFilesTooFew''s 'directory', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => DirectoryLike p f BuildError x' where
directory =
inputFilesTooFew . directory
{-# INLINE directory #-}
{- | Affine traversal: through 'BuildErrorInputFilesTooFew', a prism, then 'InputFilesTooFew''s 'inputFileCount', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => InputFileCountLike p f BuildError x' where
inputFileCount =
inputFilesTooFew . inputFileCount
{-# INLINE inputFileCount #-}
{- | Affine traversal: through 'BuildErrorInputFilesTooFew', a prism, then 'InputFilesTooFew''s 'inputFilesNeeded', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => InputFilesNeededLike p f BuildError x' where
inputFilesNeeded =
inputFilesTooFew . inputFilesNeeded
{-# INLINE inputFilesNeeded #-}
{- | Affine traversal: through 'BuildErrorOutputNotInDirectory', a prism, then 'OutputNotInDirectory''s 'outputDirectory', a lens.
@since 0.2.0.0
-}
instance (p ~ (->), Applicative f, x' ~ BuildError) => OutputDirectoryLike p f BuildError x' where
outputDirectory =
outputNotInDirectory . outputDirectory
{-# INLINE outputDirectory #-}
{- | Rebuild a value using only the name of each constructor.
Laws:
> lawNamesBuildError == id
@since 0.2.0.0
-}
lawNamesBuildError :: BuildError -> BuildError
lawNamesBuildError = \case
BuildErrorProcessFailed x -> review processFailed x
BuildErrorProcessOutputUnrecognised x -> review processOutputUnrecognised x
InputFileMissing x -> review inputFileMissing x
BuildErrorOutputFileMissing x -> review outputFileMissing x
BuildErrorStringsMissing x -> review stringsMissing x
BuildErrorFileTypeUnsupported x -> review fileTypeUnsupported x
FileNotInGit x -> review fileNotInGit x
FileNotUtf8 x -> review fileNotUtf8 x
BuildCycle x -> review cycle x
BuildErrorInputFilesTooFew x -> review inputFilesTooFew x
BuildErrorOutputNotInDirectory x -> review outputNotInDirectory x
ShallowRepository x -> review shallowRepository x
{- | The classes that a data type holding 'BuildError' inherits.
@since 0.2.0.0
-}
type InheritBuildError s =
( BuildErrorLike (->) (Const ()) s s
, ProcessFailedLike (->) (Const ()) s s
, ProcessOutputUnrecognisedLike (->) (Const ()) s s
, InputFileMissingLike (->) (Const ()) s s
, OutputFileMissingLike (->) (Const ()) s s
, StringsMissingLike (->) (Const ()) s s
, FileTypeUnsupportedLike (->) (Const ()) s s
, FileNotInGitLike (->) (Const ()) s s
, FileNotUtf8Like (->) (Const ()) s s
, CycleLike (->) (Const ()) s s
, InputFilesTooFewLike (->) (Const ()) s s
, OutputNotInDirectoryLike (->) (Const ()) s s
, ShallowRepositoryLike (->) (Const ()) s s
, CommandLineLike (->) (Const ()) s s
, StandardOutputLike (->) (Const ()) s s
, StandardErrorLike (->) (Const ()) s s
, CommandLineProgramLike (->) (Const ()) s s
, CommandLineArgumentsLike (->) (Const ()) s s
, ExpectedOutputLike (->) (Const ()) s s
, FileLike (->) (Const ()) s s
, MissingStringsLike (->) (Const ()) s s
, SupportedExtensionsLike (->) (Const ()) s s
, ReproducesInputLike (->) (Const ()) s s
, RepeatsStepLike (->) (Const ()) s s
, StepLike (->) (Const ()) s s
, TrailLike (->) (Const ()) s s
, StepNameLike (->) (Const ()) s s
, StepInputLike (->) (Const ()) s s
, StepsLike (->) (Const ()) s s
, DirectoryLike (->) (Const ()) s s
, InputFileCountLike (->) (Const ()) s s
, InputFilesNeededLike (->) (Const ()) s s
, OutputDirectoryLike (->) (Const ()) s s
)
{- | Check, where it is applied, that @s@ inherits every name of 'BuildError'.
@since 0.2.0.0
-}
inheritBuildError :: (InheritBuildError s) => proxy s -> ()
inheritBuildError _ =
()
{- | 'BuildError' inherits every name of each data type that it holds in a
constructor.
@since 0.2.0.0
-}
inheritsBuildError :: [()]
inheritsBuildError =
let p = Proxy :: Proxy BuildError
in [ inheritProcessFailed p
, inheritProcessOutputUnrecognised p
, inheritOutputFileMissing p
, inheritStringsMissing p
, inheritFileTypeUnsupported p
, inheritCycle p
, inheritInputFilesTooFew p
, inheritOutputNotInDirectory p
]
{- | The exit code for an error that is not the failure of a process. A
failed process keeps its own exit code, and this function gives @1@ for it.
>>> buildErrorExitCode (InputFileMissing "report.fodt")
NotZero True (Positive 22)
@since 0.1.0.0
-}
buildErrorExitCode :: BuildError -> NotZero
buildErrorExitCode e =
notZeroOr $
case e of
BuildErrorProcessFailed _ -> 1 :: Int
FileNotInGit _ -> 12
BuildErrorProcessOutputUnrecognised _ -> 14
InputFileMissing _ -> 22
BuildErrorStringsMissing _ -> 26
BuildErrorOutputFileMissing _ -> 28
BuildErrorFileTypeUnsupported _ -> 30
FileNotUtf8 _ -> 32
BuildCycle _ -> 34
BuildErrorInputFilesTooFew _ -> 36
BuildErrorOutputNotInDirectory _ -> 38
ShallowRepository _ -> 40
{- | A description of an error, for people to read.
>>> putStr (displayBuildError (BuildErrorStringsMissing (StringsMissing "report.fodt" ("${UPDATED}" :| []))))
report.fodt does not contain:
${UPDATED}
@since 0.1.0.0
-}
displayBuildError :: BuildError -> String
displayBuildError e =
case e of
BuildErrorProcessFailed (ProcessFailed c o r) ->
unlines (("command failed: " <> displayCommandLine c) : section "stdout" o <> section "stderr" r)
BuildErrorProcessOutputUnrecognised (ProcessOutputUnrecognised c x) ->
unlines ["output not recognised: " <> displayCommandLine c, " expected " <> x]
InputFileMissing p ->
unlines ["input file does not exist: " <> p]
BuildErrorOutputFileMissing (OutputFileMissing c p) ->
unlines ["command did not produce " <> p <> ": " <> displayCommandLine c]
BuildErrorStringsMissing (StringsMissing p xs) ->
unlines ((p <> " does not contain:") : fmap (" " <>) (toList xs))
BuildErrorFileTypeUnsupported (FileTypeUnsupported p xs) ->
unlines [p <> " is not one of the supported file types: " <> unwords xs]
FileNotInGit p ->
unlines ["file has no git history: " <> p]
FileNotUtf8 p ->
unlines ["file is not UTF-8 text: " <> p]
BuildCycle c ->
unlines ["build step stopped, because it would go round in a cycle:", " " <> displayCycle c]
BuildErrorInputFilesTooFew (InputFilesTooFew d n m) ->
unlines ["directory has " <> show n <> " input files, and needs at least " <> show m <> ": " <> d]
BuildErrorOutputNotInDirectory (OutputNotInDirectory f d) ->
unlines ["build step made a file outside the output directory " <> d <> ": " <> f]
ShallowRepository d ->
unlines ["git history is shallow, so the last commit to change each file is not known: " <> d, " fetch the whole history, such as with GIT_DEPTH: 0 in GitLab CI"]
section :: String -> String -> [String]
section name x =
if null x
then []
else (" " <> name <> ":") : fmap (" " <>) (lines x)