Distribution/Version.hs

    -----------------------------------------------------------------------------
    -- |
    -- 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,
      differenceVersionRanges,
      invertVersionRange,
      withinVersion,
      majorBoundVersion,
      betweenVersionsInclusive,

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

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

      -- ** Utilities
      wildcardUpperBound,
      majorUpperBound,

      -- ** Modification
      removeUpperBound,
      removeLowerBound,

      -- * 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,
      withinIntervals,
      versionIntervals,
      mkVersionIntervals,
      unionVersionIntervals,
      intersectVersionIntervals,
      invertVersionIntervals

     ) where

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

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

    -- | Does this 'VersionRange' place any restriction on the 'Version' or is it
    -- in fact equivalent to 'AnyVersion'.
    --
    -- Note this is a semantic check, not simply a syntactic check. So for example
    -- the following is @True@ (for all @v@).
    --
    -- > isAnyVersion (EarlierVersion v `UnionVersionRanges` orLaterVersion v)
    --
    isAnyVersion :: VersionRange -> Bool
    isAnyVersion vr = case asVersionIntervals vr of
      [(LowerBound v InclusiveBound, NoUpperBound)] | isVersion0 v -> True
      _                                                            -> False
      where
        isVersion0 :: Version -> Bool
        isVersion0 = (== mkVersion [0])

    -- | 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 vr = case asVersionIntervals vr of
      [] -> True
      _  -> 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 vr = case asVersionIntervals vr of
      [(LowerBound v  InclusiveBound
       ,UpperBound v' InclusiveBound)]
        | v == v' -> Just v
      _           -> Nothing

    -- | 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 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.
        | null (versionIntervals vi) = vr
        | otherwise                  = fromVersionIntervals vi
      where
        vi = toVersionIntervals vr

    -- | The difference of two version ranges
    --
    -- >   withinRange v' (differenceVersionRanges vr1 vr2)
    -- > = withinRange v' vr1 && not (withinRange v' vr2)
    --
    -- @since 1.24.1.0
    differenceVersionRanges :: VersionRange -> VersionRange -> VersionRange
    differenceVersionRanges vr1 vr2 =
        intersectVersionRanges vr1 (invertVersionRange vr2)

    -- | The inverse of a version range
    --
    -- >   withinRange v' (invertVersionRange vr)
    -- > = not (withinRange v' vr)
    --
    invertVersionRange :: VersionRange -> VersionRange
    invertVersionRange =
        fromVersionIntervals . invertVersionIntervals . toVersionIntervals

    -- | Given a version range, remove the highest upper bound. Example: @(>= 1 && <
    -- 3) || (>= 4 && < 5)@ is converted to @(>= 1 && < 3) || (>= 4)@.
    removeUpperBound :: VersionRange -> VersionRange
    removeUpperBound = fromVersionIntervals . relaxLastInterval . 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 = fromVersionIntervals . relaxHeadInterval . toVersionIntervals

    -------------------------------------------------------------------------------
    -- Deprecated
    -------------------------------------------------------------------------------

    -- In practice this is not very useful because we normally use inclusive lower
    -- bounds and exclusive upper bounds.
    --
    -- > withinRange v' (laterVersion v) = v' > v
    --
    betweenVersionsInclusive :: Version -> Version -> VersionRange
    betweenVersionsInclusive v1 v2 =
      intersectVersionRanges (orLaterVersion v1) (orEarlierVersion v2)

    {-# DEPRECATED betweenVersionsInclusive
        "In practice this is not very useful because we normally use inclusive lower bounds and exclusive upper bounds" #-}

    -- | An extended variant of 'foldVersionRange' that also provides a view of the
    -- expression in which the syntactic sugar @\">= v\"@, @\"<= v\"@ and @\"==
    -- v.*\"@ is presented explicitly rather than in terms of the other basic
    -- syntax.
    --
    foldVersionRange' :: a                         -- ^ @\"-any\"@ version
                      -> (Version -> a)            -- ^ @\"== v\"@
                      -> (Version -> a)            -- ^ @\"> v\"@
                      -> (Version -> a)            -- ^ @\"< v\"@
                      -> (Version -> a)            -- ^ @\">= v\"@
                      -> (Version -> a)            -- ^ @\"<= v\"@
                      -> (Version -> Version -> a) -- ^ @\"== v.*\"@ wildcard. The
                                                   -- function is passed the
                                                   -- inclusive lower bound and the
                                                   -- exclusive upper bounds of the
                                                   -- range defined by the wildcard.
                      -> (Version -> Version -> a) -- ^ @\"^>= v\"@ major upper bound
                                                   -- The function is passed the
                                                   -- inclusive lower bound and the
                                                   -- exclusive major upper bounds
                                                   -- of the range defined by this
                                                   -- operator.
                      -> (a -> a -> a)             -- ^ @\"_ || _\"@ union
                      -> (a -> a -> a)             -- ^ @\"_ && _\"@ intersection
                      -> (a -> a)                  -- ^ @\"(_)\"@ parentheses
                      -> VersionRange -> a
    foldVersionRange' anyv this later earlier orLater orEarlier
                      wildcard major union intersect parens =
        cataVersionRange alg . normaliseVersionRange
      where
        alg AnyVersionF                     = anyv
        alg (ThisVersionF v)                = this v
        alg (LaterVersionF v)               = later v
        alg (EarlierVersionF v)             = earlier v
        alg (OrLaterVersionF v)             = orLater v
        alg (OrEarlierVersionF v)           = orEarlier v
        alg (WildcardVersionF v)            = wildcard v (wildcardUpperBound v)
        alg (MajorBoundVersionF v)          = major v (majorUpperBound v)
        alg (UnionVersionRangesF v1 v2)     = union v1 v2
        alg (IntersectVersionRangesF v1 v2) = intersect v1 v2
        alg (VersionRangeParensF v)         = parens v
    {-# DEPRECATED foldVersionRange' "Use cataVersionRange & normaliseVersionRange for more principled folding" #-}