-----------------------------------------------------------------------------

-- |
-- Module      :  Distribution.Version
-- Copyright   :  Isaac Jones, Simon Marlow 2003-2004
--                Duncan Coutts 2008
-- License     :  BSD3
--
-- Maintainer  :  cabal-devel@haskell.org
-- Portability :  portable
--
-- Exports the 'Version' type along with a parser and pretty printer. A version
-- is something like @\"1.3.3\"@. It also defines the 'VersionRange' data
-- types. Version ranges are like @\">= 1.2 && < 2\"@.
module Distribution.Version
  ( -- * Package versions
    Version
  , version0
  , mkVersion
  , mkVersion'
  , versionNumbers
  , nullVersion
  , alterVersion

    -- * Version ranges
  , VersionRange

    -- ** Constructing
  , anyVersion
  , noVersion
  , thisVersion
  , notThisVersion
  , laterVersion
  , earlierVersion
  , orLaterVersion
  , orEarlierVersion
  , unionVersionRanges
  , intersectVersionRanges
  , withinVersion
  , majorBoundVersion

    -- ** Inspection
  , withinRange
  , isAnyVersion
  , isNoVersion
  , isSpecificVersion
  , simplifyVersionRange
  , foldVersionRange
  , normaliseVersionRange
  , stripParensVersionRange
  , hasUpperBound
  , hasLowerBound

    -- ** Cata & ana
  , VersionRangeF (..)
  , cataVersionRange
  , anaVersionRange
  , hyloVersionRange
  , projectVersionRange
  , embedVersionRange

    -- ** Utilities
  , wildcardUpperBound
  , majorUpperBound

    -- ** Modification
  , removeUpperBound
  , removeLowerBound
  , transformCaret
  , transformCaretUpper
  , transformCaretLower

    -- * Version intervals view
  , asVersionIntervals
  , VersionInterval (..)
  , LowerBound (..)
  , UpperBound (..)
  , Bound (..)

    -- ** 'VersionIntervals' abstract type

    -- | The 'VersionIntervals' type and the accompanying functions are exposed
    -- primarily for completeness and testing purposes. In practice
    -- 'asVersionIntervals' is the main function to use to
    -- view a 'VersionRange' as a bunch of 'VersionInterval's.
  , VersionIntervals
  , toVersionIntervals
  , fromVersionIntervals
  , unVersionIntervals
  ) where

import Distribution.Types.Version
import Distribution.Types.VersionInterval
import Distribution.Types.VersionRange

-------------------------------------------------------------------------------
-- Utilities on VersionRange requiring VersionInterval
-------------------------------------------------------------------------------

-- | This is the converse of 'isAnyVersion'. It check if the version range is
-- empty, if there is no possible version that satisfies the version range.
--
-- For example this is @True@ (for all @v@):
--
-- > isNoVersion (EarlierVersion v `IntersectVersionRanges` LaterVersion v)
isNoVersion :: VersionRange -> Bool
isNoVersion :: VersionRange -> Bool
isNoVersion VersionRange
vr = case VersionRange -> [VersionInterval]
asVersionIntervals VersionRange
vr of
  [] -> Bool
True
  [VersionInterval]
_ -> Bool
False

-- | Is this version range in fact just a specific version?
--
-- For example the version range @\">= 3 && <= 3\"@ contains only the version
-- @3@.
isSpecificVersion :: VersionRange -> Maybe Version
isSpecificVersion :: VersionRange -> Maybe Version
isSpecificVersion VersionRange
vr = case VersionRange -> [VersionInterval]
asVersionIntervals VersionRange
vr of
  [VersionInterval (LowerBound Version
v Bound
InclusiveBound) (UpperBound Version
v' Bound
InclusiveBound)]
    | Version
v Version -> Version -> Bool
forall a. Eq a => a -> a -> Bool
== Version
v' -> Version -> Maybe Version
forall a. a -> Maybe a
Just Version
v
  [VersionInterval]
_ -> Maybe Version
forall a. Maybe a
Nothing

-------------------------------------------------------------------------------
-- Transformations
-------------------------------------------------------------------------------

-- | Simplify a 'VersionRange' expression. For non-empty version ranges
-- this produces a canonical form. Empty or inconsistent version ranges
-- are left as-is because that provides more information.
--
-- If you need a canonical form use
-- @fromVersionIntervals . toVersionIntervals@
--
-- It satisfies the following properties:
--
-- > withinRange v (simplifyVersionRange r) = withinRange v r
--
-- >     withinRange v r = withinRange v r'
-- > ==> simplifyVersionRange r = simplifyVersionRange r'
-- >  || isNoVersion r
-- >  || isNoVersion r'
simplifyVersionRange :: VersionRange -> VersionRange
simplifyVersionRange :: VersionRange -> VersionRange
simplifyVersionRange VersionRange
vr
  -- If the version range is inconsistent then we just return the
  -- original since that has more information than ">1 && < 1", which
  -- is the canonical inconsistent version range.
  | [VersionInterval] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null (VersionIntervals -> [VersionInterval]
unVersionIntervals VersionIntervals
vi) = VersionRange
vr
  | Bool
otherwise = VersionIntervals -> VersionRange
fromVersionIntervals VersionIntervals
vi
  where
    vi :: VersionIntervals
vi = VersionRange -> VersionIntervals
toVersionIntervals VersionRange
vr

-- | Given a version range, remove the highest upper bound. Example: @(>= 1 && <
-- 3) || (>= 4 && < 5)@ is converted to @(>= 1 && < 3) || (>= 4)@.
removeUpperBound :: VersionRange -> VersionRange
removeUpperBound :: VersionRange -> VersionRange
removeUpperBound = VersionIntervals -> VersionRange
fromVersionIntervals (VersionIntervals -> VersionRange)
-> (VersionRange -> VersionIntervals)
-> VersionRange
-> VersionRange
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VersionIntervals -> VersionIntervals
relaxLastInterval (VersionIntervals -> VersionIntervals)
-> (VersionRange -> VersionIntervals)
-> VersionRange
-> VersionIntervals
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VersionRange -> VersionIntervals
toVersionIntervals

-- | Given a version range, remove the lowest lower bound.
-- Example: @(>= 1 && < 3) || (>= 4 && < 5)@ is converted to
-- @(>= 0 && < 3) || (>= 4 && < 5)@.
removeLowerBound :: VersionRange -> VersionRange
removeLowerBound :: VersionRange -> VersionRange
removeLowerBound = VersionIntervals -> VersionRange
fromVersionIntervals (VersionIntervals -> VersionRange)
-> (VersionRange -> VersionIntervals)
-> VersionRange
-> VersionRange
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VersionIntervals -> VersionIntervals
relaxHeadInterval (VersionIntervals -> VersionIntervals)
-> (VersionRange -> VersionIntervals)
-> VersionRange
-> VersionIntervals
forall b c a. (b -> c) -> (a -> b) -> a -> c
. VersionRange -> VersionIntervals
toVersionIntervals

-- | Rewrite @^>= x.y.z@ into @>= x.y.z && < x.(y+1)@
--
-- @since 3.6.0.0
transformCaret :: VersionRange -> VersionRange
transformCaret :: VersionRange -> VersionRange
transformCaret = (VersionRangeF VersionRange -> VersionRange)
-> (VersionRange -> VersionRangeF VersionRange)
-> VersionRange
-> VersionRange
hyloVersionRange VersionRangeF VersionRange -> VersionRange
embed VersionRange -> VersionRangeF VersionRange
projectVersionRange
  where
    embed :: VersionRangeF VersionRange -> VersionRange
embed (MajorBoundVersionF Version
v) = Version -> VersionRange
orLaterVersion Version
v VersionRange -> VersionRange -> VersionRange
`intersectVersionRanges` Version -> VersionRange
earlierVersion (Version -> Version
majorUpperBound Version
v)
    embed VersionRangeF VersionRange
vr = VersionRangeF VersionRange -> VersionRange
embedVersionRange VersionRangeF VersionRange
vr

-- | Rewrite @^>= x.y.z@ into @>= x.y.z@
--
-- @since 3.6.0.0
transformCaretUpper :: VersionRange -> VersionRange
transformCaretUpper :: VersionRange -> VersionRange
transformCaretUpper = (VersionRangeF VersionRange -> VersionRange)
-> (VersionRange -> VersionRangeF VersionRange)
-> VersionRange
-> VersionRange
hyloVersionRange VersionRangeF VersionRange -> VersionRange
embed VersionRange -> VersionRangeF VersionRange
projectVersionRange
  where
    embed :: VersionRangeF VersionRange -> VersionRange
embed (MajorBoundVersionF Version
v) = Version -> VersionRange
orLaterVersion Version
v
    embed VersionRangeF VersionRange
vr = VersionRangeF VersionRange -> VersionRange
embedVersionRange VersionRangeF VersionRange
vr

-- | Rewrite @^>= x.y.z@ into @<x.(y+1)@
--
-- @since 3.6.0.0
transformCaretLower :: VersionRange -> VersionRange
transformCaretLower :: VersionRange -> VersionRange
transformCaretLower = (VersionRangeF VersionRange -> VersionRange)
-> (VersionRange -> VersionRangeF VersionRange)
-> VersionRange
-> VersionRange
hyloVersionRange VersionRangeF VersionRange -> VersionRange
embed VersionRange -> VersionRangeF VersionRange
projectVersionRange
  where
    embed :: VersionRangeF VersionRange -> VersionRange
embed (MajorBoundVersionF Version
v) = Version -> VersionRange
earlierVersion (Version -> Version
majorUpperBound Version
v)
    embed VersionRangeF VersionRange
vr = VersionRangeF VersionRange -> VersionRange
embedVersionRange VersionRangeF VersionRange
vr