packages feed

seihou-core-0.6.0.0: src/Seihou/Engine/Baseline.hs

module Seihou.Engine.Baseline
  ( recordGeneratedBaselines,
    manifestBaselineRefs,
  )
where

import Control.Monad (foldM)
import Data.Generics.Labels ()
import Data.Map.Strict qualified as Map
import Data.Maybe (mapMaybe)
import Data.Set qualified as Set
import Data.Text qualified as T
import Seihou.Core.Types (BaselineRef, FileRecord (..), Manifest (..))
import Seihou.Effect.BaselineStore (BaselineError (..), BaselineStore, putBaseline)
import Seihou.Effect.Filesystem (Filesystem, doesFileExist, readFileText)
import Seihou.Manifest.Hash (hashContent)
import Seihou.Prelude

-- | Capture the exact post-execution bytes for every returned file record.
-- The same content supplies both the generated baseline and the applied disk
-- hash, which is essential for patch operations whose final content only
-- exists after execution.
recordGeneratedBaselines ::
  (Filesystem :> es, BaselineStore :> es) =>
  FilePath ->
  Map FilePath FileRecord ->
  Eff es (Either BaselineError (Map FilePath FileRecord))
recordGeneratedBaselines targetDir records =
  foldM capture (Right Map.empty) (Map.toAscList records)
  where
    capture (Left err) _ = pure (Left err)
    capture (Right captured) (path, record) = do
      let fullPath = targetDir </> path
      exists <- doesFileExist fullPath
      if not exists
        then
          pure $
            Left $
              BaselineStoreFailure
                ("generated file disappeared before baseline capture: " <> T.pack fullPath)
        else do
          content <- readFileText fullPath
          ref <- putBaseline content
          let enriched =
                ( record
                    & #hash
                    .~ hashContent content
                    & #baseline
                    ?~ ref
                )
          pure (Right (Map.insert path enriched captured))

-- | Every blob protected by the currently durable manifest. Callers pass this
-- set to 'pruneBaselines' only after publishing that manifest.
manifestBaselineRefs :: Manifest -> Set BaselineRef
manifestBaselineRefs manifest =
  Set.fromList (mapMaybe (^. #baseline) (Map.elems (manifest ^. #files)))