packages feed

casa-reoc-0.1.0.0: src/ReOC/Amendment.hs

{-# OPTIONS_GHC -Wall #-}

{- | The amendment record of an operations manual. The first entry gives its
version and date; each revision follows the entry before it, so the versions
increase and the dates do not go backwards.
-}
module ReOC.Amendment (
  AmendmentEntry,
  amendmentRecord,
  manualVersion,
  manualDate,
  manualReviewDate,
  addMonths,
) where

import Control.Lens (cloneLens, view)
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Time.Calendar.Month (Month)
import qualified Data.Time.Calendar.Month as Month
import Numeric.Natural (Natural)
import ReOC.Model

-- | An entry of the amendment record: version, date, effective date, parts and details.
type AmendmentEntry = (Version, Month, Text1, Text1, Text1)

{- | A month a number of months later.

>>> addMonths 12 (Month.YearMonth 2026 10)
2027-10
-}
addMonths :: Natural -> Month -> Month
addMonths n (Month.YearMonth y m) =
  let total = y * 12 + fromIntegral (m - 1) + fromIntegral n
   in Month.YearMonth (total `div` 12) (fromIntegral (total `mod` 12) + 1)

-- | The entries of the amendment record, in order.
amendmentRecord :: OperationsManual -> NonEmpty AmendmentEntry
amendmentRecord m =
  let f = view (cloneLens firstIssue) m
      first = (view (cloneLens version) f, view (cloneLens issueDate) f, view (cloneLens effective) f, view (cloneLens parts) f, view (cloneLens details) f)
      next :: AmendmentEntry -> Revision -> AmendmentEntry
      next (v, d, _, _, _) r =
        ( case view (cloneLens versionIncrement) r of
            MajorRevision -> Version' (view (cloneLens majorVersion) v + 1) 0
            MinorRevision -> Version' (view (cloneLens majorVersion) v) (view (cloneLens minorVersion) v + 1)
        , addMonths (view (cloneLens monthsAfter) r) d
        , view (cloneLens effective) r
        , view (cloneLens parts) r
        , view (cloneLens details) r
        )
   in NonEmpty.scanl next first (view (cloneLens revisions) m)

-- | The version of the manual: the version of its last amendment.
manualVersion :: OperationsManual -> Version
manualVersion m =
  let (v, _, _, _, _) = NonEmpty.last (amendmentRecord m) in v

-- | The date of the manual: the date of its last amendment.
manualDate :: OperationsManual -> Month
manualDate m =
  let (_, d, _, _, _) = NonEmpty.last (amendmentRecord m) in d

-- | The date by which the manual is next reviewed: 12 months after its date.
manualReviewDate :: OperationsManual -> Month
manualReviewDate =
  addMonths 12 . manualDate