@@ -113,12 +113,12 @@ module Dhall.Import (
113
113
, PrettyHttpException (.. )
114
114
, MissingFile (.. )
115
115
, MissingEnvironmentVariable (.. )
116
+ , MissingImports (.. )
116
117
) where
117
118
118
119
import Control.Applicative (empty )
119
- import Control.Exception (Exception , SomeException , throwIO )
120
- import Control.Monad (join )
121
- import Control.Monad.Catch (throwM , MonadCatch (catch ))
120
+ import Control.Exception (Exception , SomeException , throwIO , toException )
121
+ import Control.Monad.Catch (throwM , MonadCatch (catch ), catches , Handler (.. ))
122
122
import Control.Monad.IO.Class (MonadIO (.. ))
123
123
import Control.Monad.Trans.State.Strict (StateT )
124
124
import Crypto.Hash (SHA256 )
@@ -269,6 +269,28 @@ instance Show MissingEnvironmentVariable where
269
269
<> " \n "
270
270
<> " ↳ " <> Text. unpack name
271
271
272
+ -- | List of Exceptions we encounter while resolving Import Alternatives
273
+ newtype MissingImports = MissingImports [SomeException ]
274
+
275
+ instance Exception MissingImports
276
+
277
+ instance Show MissingImports where
278
+ show (MissingImports [] ) =
279
+ " \n "
280
+ <> " \ESC [1;31mError\ESC [0m: No valid imports"
281
+ <> " \n "
282
+ show (MissingImports [e]) = show e
283
+ show (MissingImports es) =
284
+ " \n "
285
+ <> " \ESC [1;31mError\ESC [0m: Failed to resolve imports. Error list:"
286
+ <> " \n "
287
+ <> concatMap (\ e -> " \n " <> show e <> " \n " ) es
288
+ <> " \n "
289
+
290
+ throwMissingImport :: (MonadCatch m , Exception e ) => e -> m a
291
+ throwMissingImport e = throwM (MissingImports [(toException e)])
292
+
293
+
272
294
-- | Exception thrown when a HTTP url is imported but dhall was built without
273
295
-- the @with-http@ Cabal flag.
274
296
data CannotImportHTTPURL =
@@ -336,6 +358,9 @@ instance Canonicalize ImportType where
336
358
canonicalize (Env name) =
337
359
Env name
338
360
361
+ canonicalize Missing =
362
+ Missing
363
+
339
364
instance Canonicalize ImportHashed where
340
365
canonicalize (ImportHashed hash importType) =
341
366
ImportHashed hash (canonicalize importType)
@@ -429,7 +454,7 @@ exprFromImport (Import {..}) = do
429
454
430
455
if exists
431
456
then return ()
432
- else throwIO (MissingFile path)
457
+ else throwMissingImport (MissingFile path)
433
458
434
459
text <- Data.Text.IO. readFile path
435
460
@@ -487,7 +512,10 @@ exprFromImport (Import {..}) = do
487
512
x <- System.Environment. lookupEnv (Text. unpack env)
488
513
case x of
489
514
Just string -> return (Text. unpack env, Text. pack string)
490
- Nothing -> throwIO (MissingEnvironmentVariable env)
515
+ Nothing -> throwMissingImport (MissingEnvironmentVariable env)
516
+
517
+ Missing -> liftIO $ do
518
+ throwM (MissingImports [] )
491
519
492
520
case importMode of
493
521
Code -> do
@@ -537,39 +565,61 @@ loadStaticWith
537
565
-> Dhall.Core. Normalizer X
538
566
-> Expr Src Import
539
567
-> StateT Status m (Expr Src X )
540
- loadStaticWith from_import ctx n (Embed import_) = do
568
+ loadStaticWith from_import ctx n expr₀ = case expr₀ of
569
+ Embed import_ -> do
541
570
imports <- zoom stack State. get
542
571
543
- let local (Import (ImportHashed _ (URL {})) _) = False
544
- local (Import (ImportHashed _ (Local {})) _) = True
545
- local (Import (ImportHashed _ (Env {})) _) = True
572
+ let local (Import (ImportHashed _ (URL {})) _) = False
573
+ local (Import (ImportHashed _ (Local {})) _) = True
574
+ local (Import (ImportHashed _ (Env {})) _) = True
575
+ local (Import (ImportHashed _ (Missing {})) _) = True
546
576
547
577
let parent = canonicalizeImport imports
548
578
let here = canonicalizeImport (import_: imports)
549
579
550
580
if local here && not (local parent)
551
- then throwM (Imported imports (ReferentiallyOpaque import_))
581
+ then throwMissingImport (Imported imports (ReferentiallyOpaque import_))
552
582
else return ()
553
583
554
584
expr <- if here `elem` canonicalizeAll imports
555
- then throwM (Imported imports (Cycle import_))
585
+ then throwMissingImport (Imported imports (Cycle import_))
556
586
else do
557
587
m <- zoom cache State. get
558
588
case Map. lookup here m of
559
589
Just expr -> return expr
560
590
Nothing -> do
561
- let handler
562
- :: MonadCatch m
591
+ -- Here we have to match and unwrap the @MissingImports@
592
+ -- in a separate handler, otherwise we'd have it wrapped
593
+ -- in another @Imported@ when parsing a @missing@, because
594
+ -- we are representing it with an empty exception list
595
+ -- (which would not be empty if this would happen).
596
+ -- TODO: restructure the Exception hierarchy to prevent
597
+ -- this nesting from happening in the first place.
598
+ let handler₀
599
+ :: (MonadCatch m )
600
+ => MissingImports
601
+ -> StateT Status m (Expr Src Import )
602
+ handler₀ e@ (MissingImports [] ) = throwM e
603
+ handler₀ (MissingImports [e]) =
604
+ throwMissingImport (Imported (import_: imports) e)
605
+ handler₀ (MissingImports es) = throwM
606
+ (MissingImports
607
+ (fmap
608
+ (\ e -> (toException (Imported (import_: imports) e)))
609
+ es))
610
+ handler₁
611
+ :: (MonadCatch m )
563
612
=> SomeException
564
613
-> StateT Status m (Expr Src Import )
565
- handler e = throwM (Imported (import_: imports) e)
614
+ handler₁ e =
615
+ throwMissingImport (Imported (import_: imports) e)
566
616
567
617
-- This loads a \"dynamic\" expression (i.e. an expression
568
618
-- that might still contain imports)
569
619
let loadDynamic =
570
620
from_import (canonicalizeImport (import_: imports))
571
621
572
- expr' <- loadDynamic `catch` handler
622
+ expr' <- loadDynamic `catches` [ Handler handler₀, Handler handler₁ ]
573
623
574
624
let imports' = import_: imports
575
625
zoom stack (State. put imports')
@@ -599,12 +649,80 @@ loadStaticWith from_import ctx n (Embed import_) = do
599
649
let actualHash = hashExpression expr
600
650
if expectedHash == actualHash
601
651
then return ()
602
- else throwM (Imported (import_: imports) (HashMismatch {.. }))
652
+ else throwMissingImport (Imported (import_: imports) (HashMismatch {.. }))
603
653
604
654
return expr
605
- loadStaticWith from_import ctx n expr = fmap join (traverse process expr)
655
+ ImportAlt a b -> loop a `catch` handler₀
656
+ where
657
+ handler₀ (MissingImports es₀) =
658
+ loop b `catch` handler₁
659
+ where
660
+ handler₁ (MissingImports es₁) =
661
+ throwM (MissingImports (es₀ ++ es₁))
662
+ Const a -> pure (Const a)
663
+ Var a -> pure (Var a)
664
+ Lam a b c -> Lam <$> pure a <*> loop b <*> loop c
665
+ Pi a b c -> Pi <$> pure a <*> loop b <*> loop c
666
+ App a b -> App <$> loop a <*> loop b
667
+ Let a b c d -> Let <$> pure a <*> mapM loop b <*> loop c <*> loop d
668
+ Annot a b -> Annot <$> loop a <*> loop b
669
+ Bool -> pure Bool
670
+ BoolLit a -> pure (BoolLit a)
671
+ BoolAnd a b -> BoolAnd <$> loop a <*> loop b
672
+ BoolOr a b -> BoolOr <$> loop a <*> loop b
673
+ BoolEQ a b -> BoolEQ <$> loop a <*> loop b
674
+ BoolNE a b -> BoolNE <$> loop a <*> loop b
675
+ BoolIf a b c -> BoolIf <$> loop a <*> loop b <*> loop c
676
+ Natural -> pure Natural
677
+ NaturalLit a -> pure (NaturalLit a)
678
+ NaturalFold -> pure NaturalFold
679
+ NaturalBuild -> pure NaturalBuild
680
+ NaturalIsZero -> pure NaturalIsZero
681
+ NaturalEven -> pure NaturalEven
682
+ NaturalOdd -> pure NaturalOdd
683
+ NaturalToInteger -> pure NaturalToInteger
684
+ NaturalShow -> pure NaturalShow
685
+ NaturalPlus a b -> NaturalPlus <$> loop a <*> loop b
686
+ NaturalTimes a b -> NaturalTimes <$> loop a <*> loop b
687
+ Integer -> pure Integer
688
+ IntegerLit a -> pure (IntegerLit a)
689
+ IntegerShow -> pure IntegerShow
690
+ IntegerToDouble -> pure IntegerToDouble
691
+ Double -> pure Double
692
+ DoubleLit a -> pure (DoubleLit a)
693
+ DoubleShow -> pure DoubleShow
694
+ Text -> pure Text
695
+ TextLit (Chunks a b) -> fmap TextLit (Chunks <$> mapM (mapM loop) a <*> pure b)
696
+ TextAppend a b -> TextAppend <$> loop a <*> loop b
697
+ List -> pure List
698
+ ListLit a b -> ListLit <$> mapM loop a <*> mapM loop b
699
+ ListAppend a b -> ListAppend <$> loop a <*> loop b
700
+ ListBuild -> pure ListBuild
701
+ ListFold -> pure ListFold
702
+ ListLength -> pure ListLength
703
+ ListHead -> pure ListHead
704
+ ListLast -> pure ListLast
705
+ ListIndexed -> pure ListIndexed
706
+ ListReverse -> pure ListReverse
707
+ Optional -> pure Optional
708
+ OptionalLit a b -> OptionalLit <$> loop a <*> mapM loop b
709
+ OptionalFold -> pure OptionalFold
710
+ OptionalBuild -> pure OptionalBuild
711
+ Record a -> Record <$> mapM loop a
712
+ RecordLit a -> RecordLit <$> mapM loop a
713
+ Union a -> Union <$> mapM loop a
714
+ UnionLit a b c -> UnionLit <$> pure a <*> loop b <*> mapM loop c
715
+ Combine a b -> Combine <$> loop a <*> loop b
716
+ CombineTypes a b -> CombineTypes <$> loop a <*> loop b
717
+ Prefer a b -> Prefer <$> loop a <*> loop b
718
+ Merge a b c -> Merge <$> loop a <*> loop b <*> mapM loop c
719
+ Constructors a -> Constructors <$> loop a
720
+ Field a b -> Field <$> loop a <*> pure b
721
+ Project a b -> Project <$> loop a <*> pure b
722
+ Note a b -> Note <$> pure a <*> loop b
606
723
where
607
- process import_ = loadStaticWith from_import ctx n (Embed import_)
724
+ loop = loadStaticWith from_import ctx n
725
+
608
726
609
727
-- | Resolve all imports within an expression
610
728
load :: Expr Src Import -> IO (Expr Src X )
0 commit comments