packages feed

policeman-0.0.0.0: src/Policeman/Core/Version.hs

{- | Module for 'Version' data type and helper functions to work with the
PVP-agreed versioning.

More information about PVP: <https://pvp.haskell.org/>.
-}

module Policeman.Core.Version
    ( Version (..)
      -- ** Parse/Display helpers
    , versionToText
    , versionFromText
    , versionToIntList
    , versionFromIntList
      -- ** Bumping versions
    , bumpMarketingMajorVersion
    , bumpMajorVersion
    , bumpMinorVersion
    , bumpTinyMinorVersion

    ) where

import qualified Data.Text as Text


{- | Package version that should satisfy the PVP.
-}
data Version = Version
    { versionA    :: !Int   -- ^ Marketing major.
    , versionB    :: !Int   -- ^ Major.
    , versionC    :: !Int   -- ^ Minor.
    , versionD    :: !Int   -- ^ Tiny minor.
    , versionText :: !Text  -- ^ Original text version.
    } deriving stock (Eq, Show)

-- | Initial minimal PVP-satisfactory version: @0.0.0.0@.
nullVersion :: Version
nullVersion = Version
     { versionA = 0
     , versionB = 0
     , versionC = 0
     , versionD = 0
     , versionText = ""
     }

{- | Shows 'Version' in the standard format:

@
A.B.C.D
1.0.2.1
@
-}
versionToText :: Version -> Text
versionToText Version{..} = Text.intercalate "." $ map show
    [ versionA
    , versionB
    , versionC
    , versionD
    ]

versionFromText :: Text -> Maybe Version
versionFromText (Text.strip -> txt) = maybeInts >>= \ints ->
    if length ints > 4
    then Nothing
    else (\ver -> ver{versionText = txt}) <$> versionFromIntList ints
  where
    maybeInts :: Maybe [Int]
    maybeInts = mapM (readMaybe @Int . toString) $ Text.split (== '.') txt

versionToIntList :: Version -> [Int]
versionToIntList Version{..} =
    [ versionA
    , versionB
    , versionC
    , versionD
    ]

-- TODO: check on negative numbers
versionFromIntList :: [Int] -> Maybe Version
versionFromIntList [] = Nothing
versionFromIntList l@(x:xs) = Just $ mkVer x (toTriple xs)
  where
    mkVer :: Int -> (Int, Int, Int) -> Version
    mkVer versionA (versionB, versionC, versionD) = Version
        { versionText = Text.intercalate "." $ map show l
        , ..
        }

    toTriple :: [Int] -> (Int, Int, Int)
    toTriple (a:b:c:_rest) = (a, b, c)
    toTriple [a, b]        = (a, b, 0)
    toTriple [a]           = (a, 0, 0)
    toTriple []            = (0, 0, 0)

bumpMarketingMajorVersion :: Version -> Version
bumpMarketingMajorVersion Version{..} = nullVersion
    { versionA = versionA + 1 }

bumpMajorVersion :: Version -> Version
bumpMajorVersion Version{..} = nullVersion
    { versionA = versionA
    , versionB = versionB + 1
    }

bumpMinorVersion :: Version -> Version
bumpMinorVersion Version{..} = nullVersion
    { versionA = versionA
    , versionB = versionB
    , versionC = versionC + 1
    }

bumpTinyMinorVersion :: Version -> Version
bumpTinyMinorVersion ver@Version{..} = ver
    { versionD =  versionD + 1 }