packages feed

swarm-0.7.0.0: src/swarm-scenario/Swarm/Game/Scenario/Objective/Validation.hs

{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Validity checking for 'Objective' prerequisites
module Swarm.Game.Scenario.Objective.Validation where

import Control.Lens (view, (^.))
import Control.Monad (forM_, unless)
import Data.Foldable (for_, toList)
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Set qualified as Set
import Data.Text qualified as T
import Swarm.Game.Scenario.Objective
import Swarm.Game.Scenario.Objective.Graph
import Swarm.Util (failT, quote)
import Swarm.Util.Graph

-- | Performs monadic validation before returning
-- the "pure" construction of a wrapper record.
-- This validation entails:
--
-- 1. Ensuring that all goal references utilized in prerequisites
--    actually exist
-- 2. Ensuring that the graph of dependencies is acyclic.
validateObjectives ::
  MonadFail m =>
  [Objective] ->
  m [Objective]
validateObjectives objectives = do
  for_ objectives $ \x -> forM_ (x ^. objectivePrerequisite) $ \p ->
    let refs = Set.fromList $ toList $ logic p
        remaining = Set.difference refs allIds
     in unless (null remaining) $
          failT
            [ "Reference to undefined objective(s)"
            , T.intercalate ", " (map quote $ Set.toList remaining) <> "."
            , "Defined are:"
            , T.intercalate ", " (map quote $ Set.toList allIds)
            ]

  either (fail . T.unpack) return $
    failOnCyclicGraph "Prerequisites" (fromMaybe "N/A" . view objectiveId) edges

  return objectives
 where
  edges = makeGraphEdges objectives
  allIds = Set.fromList $ mapMaybe (view objectiveId) objectives