packages feed

headroom-0.4.3.0: src/Headroom/PostProcess/UpdateCopyright.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE StrictData        #-}
{-# LANGUAGE TypeApplications  #-}

{-|
Module      : Headroom.PostProcess.UpdateCopyright
Description : /Post-processor/ for updating years in copyrights
Copyright   : (c) 2019-2022 Vaclav Svejcar
License     : BSD-3-Clause
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

This module provides functionality for updating years in copyright statements
in already rendered /license headers/.
-}

module Headroom.PostProcess.UpdateCopyright
  ( -- * Data Types
    SelectedAuthors(..)
  , UpdateCopyrightMode(..)
    -- * Header Functions
  , updateCopyright
    -- * Helper Functions
  , updateYears
  )
where

import           Data.String.Interpolate             ( i )
import           Headroom.Data.Has                   ( Has(..) )
import           Headroom.Data.Regex                 ( re
                                                     , replace
                                                     )
import           Headroom.Data.Text                  ( mapLines
                                                     , read
                                                     )
import           Headroom.PostProcess.Types          ( PostProcess(..) )
import           Headroom.Types                      ( CurrentYear(..) )
import           RIO
import qualified RIO.NonEmpty                       as NE
import qualified RIO.Text                           as T


---------------------------------  DATA TYPES  ---------------------------------

-- | Non-empty list of authors for which to update years in their copyrights.
newtype SelectedAuthors = SelectedAuthors (NonEmpty Text) deriving (Eq, Show)


-- | Mode that changes behaviour of the 'updateCopyright' function.
data UpdateCopyrightMode
  = UpdateAllAuthors                      -- ^ updates years in copyrights for all authors
  | UpdateSelectedAuthors SelectedAuthors -- ^ updates years in copyrights only for selected authors
  deriving (Eq, Show)


------------------------------  PUBLIC FUNCTIONS  ------------------------------

-- | /Post-processor/ that updates years and year ranges in any
-- present copyright statements.
--
-- = Reader Environment Parameters
--   ['CurrentYear'] value of the current year
--   ['UpdateCopyrightMode'] mode specifying the behaviour of the updater
updateCopyright :: (Has CurrentYear env, Has UpdateCopyrightMode env)
                => PostProcess env
updateCopyright = PostProcess $ \input -> do
  currentYear <- viewL
  mode        <- viewL
  pure $ mapLines (update mode currentYear) input
 where
  update mode year line | shouldUpdate mode line = updateYears year line
                        | otherwise              = line
  shouldUpdate UpdateAllAuthors _ = True
  shouldUpdate (UpdateSelectedAuthors (SelectedAuthors authors)) input =
    any (`T.isInfixOf` input) (NE.toList authors)


-- | Updates years and years ranges in given text.
--
-- >>> updateYears (CurrentYear 2020) "Copyright (c) 2020"
-- "Copyright (c) 2020"
--
-- >>> updateYears (CurrentYear 2020) "Copyright (c) 2019"
-- "Copyright (c) 2019-2020"
--
-- >>> updateYears (CurrentYear 2020) "Copyright (c) 2018-2020"
-- "Copyright (c) 2018-2020"
--
-- >>> updateYears (CurrentYear 2020) "Copyright (c) 2018-2019"
-- "Copyright (c) 2018-2020"
updateYears :: CurrentYear -- ^ current year
            -> Text        -- ^ text to update
            -> Text        -- ^ text with updated years
updateYears cy = replace [re|(\d{4})(?:-)?(\d{4})?|] go
 where
  go _ [r1] | (Just y1) <- read r1 = bumpYear cy y1
  go _ rs@[_, _] | [Just y1, Just y2] <- read <$> rs = bumpRange cy y1 y2
  go other _                       = other


------------------------------  PRIVATE FUNCTIONS  -----------------------------

bumpYear :: CurrentYear -> Integer -> Text
bumpYear (CurrentYear cy) y | y >= cy   = tshow y
                            | otherwise = [i|#{y}-#{cy}|]


bumpRange :: CurrentYear -> Integer -> Integer -> Text
bumpRange (CurrentYear cy) y1 y2 | y2 >= cy  = [i|#{y1}-#{y2}|]
                                 | otherwise = [i|#{y1}-#{cy}|]