salve 1.0.11 → 2.0.0.10
raw patch · 12 files changed
Files
- CHANGELOG.md +4/−0
- LICENSE.markdown +0/−21
- LICENSE.txt +21/−0
- README.markdown +0/−10
- README.md +9/−0
- salve.cabal +41/−23
- source/library/Salve.hs +621/−0
- source/library/Salve/Internal.hs +1012/−0
- source/test-suite/Main.hs +360/−0
- src/lib/Salve.hs +0/−611
- src/lib/Salve/Internal.hs +0/−1006
- src/test/Main.hs +0/−102
+ CHANGELOG.md view
@@ -0,0 +1,4 @@+# Change log++Salve follows the [Package Versioning Policy](https://pvp.haskell.org).+You can find release notes [on GitHub](https://github.com/tfausak/salve/releases).
− LICENSE.markdown
@@ -1,21 +0,0 @@-MIT License--Copyright (c) 2021 Taylor Fausak--Permission is hereby granted, free of charge, to any person obtaining a copy-of this software and associated documentation files (the "Software"), to deal-in the Software without restriction, including without limitation the rights-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell-copies of the Software, and to permit persons to whom the Software is-furnished to do so, subject to the following conditions:--The above copyright notice and this permission notice shall be included in all-copies or substantial portions of the Software.--THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-SOFTWARE.
+ LICENSE.txt view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2026 Taylor Fausak++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
− README.markdown
@@ -1,10 +0,0 @@-# Salve--[](https://github.com/tfausak/salve/actions/new)-[](https://hackage.haskell.org/package/salve)-[](https://www.stackage.org/package/salve)--Salve provides semantic version numbers and constraints for Haskell.--Salve has extensive documentation available on Hackage:-<https://hackage.haskell.org/package/salve>.
+ README.md view
@@ -0,0 +1,9 @@+# Salve++[](https://github.com/tfausak/salve/actions/workflows/ci.yml)+[](https://hackage.haskell.org/package/salve)++Salve provides semantic version numbers and constraints for Haskell.++Salve has extensive documentation available on Hackage:+<https://hackage.haskell.org/package/salve>.
salve.cabal view
@@ -1,16 +1,17 @@-cabal-version: >= 1.10-+cabal-version: 2.2 name: salve-version: 1.0.11-+version: 2.0.0.10 synopsis: Semantic version numbers and constraints. description: Salve provides semantic version (SemVer) numbers and constraints (ranges). build-type: Simple category: Distribution-extra-source-files: README.markdown-license-file: LICENSE.markdown+extra-doc-files:+ CHANGELOG.md+ README.md++license-file: LICENSE.txt license: MIT maintainer: Taylor Fausak @@ -18,32 +19,49 @@ location: https://github.com/tfausak/salve type: git -library- build-depends:- base >= 4.13.0 && < 4.16+flag pedantic+ default: False+ manual: True++common library+ build-depends: base ^>=4.20.0.0 || ^>=4.21.0.0 || ^>=4.22.0.0 default-language: Haskell2010- exposed-modules:- Salve- Salve.Internal ghc-options: -Weverything+ -Wno-all-missed-specialisations -Wno-implicit-prelude -Wno-missing-deriving-strategies -Wno-missing-exported-signatures+ -Wno-missing-kind-signatures+ -Wno-missing-safe-haskell-mode+ -Wno-prepositive-qualified-module -Wno-safe- hs-source-dirs: src/lib - if impl(ghc >= 8.10)- ghc-options:- -Wno-missing-safe-haskell-mode- -Wno-prepositive-qualified-module+ if flag(pedantic)+ ghc-options: -Werror -test-suite test+common executable+ import: library+ build-depends: salve+ ghc-options:+ -rtsopts+ -threaded+ -Wno-unused-packages++library+ import: library+ -- cabal-gild: discover source/library+ exposed-modules:+ Salve+ Salve.Internal++ hs-source-dirs: source/library++test-suite salve-test-suite+ import: executable build-depends:- base -any- , HUnit >= 1.6.1 && < 1.7- , salve -any- default-language: Haskell2010- hs-source-dirs: src/test+ HUnit ^>=1.6.2.0++ hs-source-dirs: source/test-suite main-is: Main.hs type: exitcode-stdio-1.0
+ source/library/Salve.hs view
@@ -0,0 +1,621 @@+-- | This module defines types and functions for working with versions as+-- defined by [Semantic Versioning](http://semver.org/spec/v2.0.0.html). It+-- also provides types and functions for working with version constraints as+-- described by [npm](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#ranges).+module Salve+ ( -- | This module doesn't export anything that conflicts with the "Prelude", so+ -- you can import it unqualified.+ --+ -- >>> import Salve+ --+ -- This module provides lenses for modifying versions. If you want to modify+ -- versions, consider importing a lens library like+ -- [microlens](https://www.stackage.org/lts-9.3/package/microlens-0.4.8.1).+ --+ -- The 'Version' data type is the core of this module. Use 'parseVersion' to+ -- make versions and 'renderVersion' to convert them into strings.+ --+ -- >>> renderVersion <$> parseVersion "1.2.3"+ -- Just "1.2.3"+ --+ -- The 'Constraint' data type allows you to specify version constraints. Use+ -- 'parseConstraint' to make constraints and 'renderConstraint' to convert them+ -- into strings.+ --+ -- >>> renderConstraint <$> parseConstraint ">1.2.0"+ -- Just ">1.2.0"+ --+ -- Use 'satisfiesConstraint' to see if a version satisfiesConstraint a+ -- constraint.+ --+ -- >>> satisfiesConstraint <$> parseConstraint ">1.2.0" <*> parseVersion "1.2.3"+ -- Just True++ -- * Cheat sheet++ -- | If you're coming from Cabal, you might not be familiar with npm's version+ -- range syntax. This table shows you how npm version ranges map to Cabal's+ -- version constraints.+ --+ -- > Salve | Cabal | Notes+ -- > ----- | ----- | -----+ -- > <1.2.3 | <1.2.3 | -+ -- > <=1.2.3 | <=1.2.3 | -+ -- > =1.2.3 | ==1.2.3 | equals sign is optional+ -- > >=1.2.3 | >=1.2.3 | -+ -- > >1.2.3 | >1.2.3 | -+ -- > 1.2.3 || >1.2.3 | ==1.2.3 || >1.2.3 | lower precedence than and+ -- > >=1.2.3 <2.0.0 | >=1.2.3 && <2.0.0 | higher precedence than or+ -- > 1.2.3 - 2.3.4 | >=1.2.3 && <=2.3.4 | inclusive ranges+ -- > 1.2.x | ==1.2.* | can use X or * instead of x+ -- > 1.x.x | ==1.* | -+ -- > x.x.x | ==* | same as -any+ -- > ~1.2.3 | ^>=1.2.3 | same as >=1.2.3 && <1.3.0+ -- > ^1.2.3 | >=1.2.3 && <2 | -+ -- > ^0.2.3 | >=0.2.3 && <0.3 | -+ -- > ^0.0.3 | >=0.0.3 && <0.0.4 | -++ -- * Rationale++ -- ** The PVP++ -- | Haskell's+ -- [Package Versioning Policy](https://github.com/haskell/pvp/blob/176bb14/pvp-specification.md)+ -- (PVP) defines three things:+ --+ -- 1. A spec for versioning your package, which includes how version numbers+ -- look and how to encode breaking changes.+ -- 2. A spec for constraining the versions of your dependencies, which+ -- incldues how version ranges look.+ -- 3. A prescription for how to constrain the versions of your dependencies,+ -- which includes how the ranges of your dependencies should be.+ --+ -- By comparison, Semantic Versioning only deals with the first thing. npm's+ -- version ranges only deal with the second thing. This module deals with the+ -- first and second things but leaves the third up to you.+ --+ -- Looking at the first point, why might you want to use SemVer instead of the+ -- PVP? The PVP has many problems, as described by the+ -- [Problematic versioning policy](http://taylor.fausak.me/2016/12/28/problematic-versioning-policy/)+ -- blog post. In short, the PVP is too flexible and it's unique to Haskell,+ -- which causes unnecessary friction with developers from other languages.+ --+ -- Moving on to the second point, why should we use npm's version ranges+ -- instead of the PVP's? This is a less clear cut. The two syntaxes are broadly+ -- compatible. Really the only gains here are compatibility with a widely-used+ -- syntax and convenient shorthand for common constraints (like hyphens+ -- @1.2.3 - 2.3.4@, tildes @~1.2.3@, and carets @^1.2.3@).++ -- ** Other modules++ -- | There are already a few modules that provide version numbers. Why do we+ -- need another one? Let's take a look at the options.+ --+ -- - [Data.Version](https://www.stackage.org/haddock/lts-9.3/base-4.9.1.0/Data-Version.html)+ -- from the @base@ package:+ --+ -- - Exposes constructors, which allows creating versions that cannot be+ -- parsed.+ -- - Allows any number of components, from zero to inifinity.+ -- - Deprecated tags on versions.+ -- - Does not support build metadata on versions.+ -- - Does not support constraints.+ --+ -- - [Distribution.Version](https://www.stackage.org/haddock/lts-9.3/Cabal-1.24.2.0/Distribution-Version.html)+ -- from the @Cabal@ package:+ --+ -- - Has the same problems as Data.Version because it re-uses that+ -- version type.+ -- - Depends on the @array@, @binary@, @bytestring@, @containers@,+ -- @deepseq@, @directory@, @filepath@, @pretty@, @process@, @time@, and+ -- @unix@ packages.+ --+ -- - [Data.SemVer](https://www.stackage.org/haddock/lts-9.3/semver-0.3.3.1/Data-SemVer.html)+ -- from the @semver@ package:+ --+ -- - Depends on the @attoparsec@, @deepseq@, and @text@ packages.+ -- - Does not support version constraints.+ --+ -- - [Data.SemVer](https://hackage.haskell.org/package/semver-range-0.2.2/docs/Data-SemVer.html)+ -- from the @semver-range@ package:+ --+ -- - Depends on the @classy-prelude@, @parsec@, @text@, and+ -- @unordered-containers@ packages.+ -- - Module name collides with the @semver@ package.+ -- - Supports constraints, but does not provide a way to render them.+ --+ -- - [Data.Versions](https://www.stackage.org/haddock/lts-9.3/versions-3.1.1/Data-Versions.html)+ -- from the @versions@ package:+ --+ -- - Depends on the @deepseq@, @hashable@, @megaparsec@, and @text@+ -- packages.+ -- - Intentially allows weird versions.+ -- - Does not support constraints.+ --+ -- By comparison, this module:+ --+ -- - Does not expose constructors. Any version you create can be rendered and+ -- parsed without issue.+ -- - Requires exactly three components. You won't have to wonder if version+ -- @1.2.0@ is greater than @1.2@.+ -- - Allows pre-release identifiers on versions. Go ahead and release version+ -- @1.0.0-alpha@ for early adopters.+ -- - Allows build metadata on versions. Show when a release was made with+ -- versions like @1.0.0+2001-02-03@.+ -- - Supports version constraints. Just like versions, rendering and parsing+ -- constraints is no problem.+ -- - Only depends on the @base@ package. You can use all the functionality+ -- without installing any other packages.+ -- - Has a unique module name. You won't have to use the @PackageImports@+ -- extension simply to deal with version numbers.++ -- * Types+ Salve.Version,+ Salve.PreRelease,+ Salve.Build,+ Salve.Constraint,++ -- * Constructors+ Salve.makeVersion,+ Salve.initialVersion,++ -- * Parsing+ Salve.parseVersion,+ Salve.parsePreRelease,+ Salve.parseBuild,+ Salve.parseConstraint,++ -- ** Unsafe++ -- | These functions can be used to unsafely parse strings. Instead of+ -- returning 'Nothing', they raise an exception. Only use these if you are sure+ -- the string can be successfully parsed!+ Salve.unsafeParseVersion,+ Salve.unsafeParsePreRelease,+ Salve.unsafeParseBuild,+ Salve.unsafeParseConstraint,++ -- * Rendering+ Salve.renderVersion,+ Salve.renderPreRelease,+ Salve.renderBuild,+ Salve.renderConstraint,++ -- * Predicates+ Salve.isUnstable,+ Salve.isStable,++ -- * Conversions+ Salve.fromBaseVersion,+ Salve.toBaseVersion,++ -- * Helpers+ Salve.bumpMajor,+ Salve.bumpMinor,+ Salve.bumpPatch,+ Salve.satisfiesConstraint,++ -- * Lenses++ -- | These lenses can be used to access and modify specific parts of a+ -- 'Version'.+ --+ -- Don't be scared by these type signatures. They are provided in full to avoid+ -- the @RankNTypes@ language extension. The type signature+ -- @'Functor' f => (a -> f a) -> 'Version' -> f 'Version'@ is the same as+ -- @Lens' 'Version' a@, which you may already be familiar with.+ Salve.majorLens,+ Salve.minorLens,+ Salve.patchLens,+ Salve.preReleasesLens,+ Salve.buildsLens,++ -- * Examples++ -- | These examples are provided to showcase functionality and explain weird+ -- behavior. If something isn't clear, please open a pull request adding an+ -- example!++ -- ** Versions++ -- | Leading zeros are not allowed.+ --+ -- >>> parseVersion "01.0.0"+ -- Nothing+ -- >>> parseVersion "0.01.0"+ -- Nothing+ -- >>> parseVersion "0.0.01"+ -- Nothing+ --+ -- Negative numbers are not allowed.+ --+ -- >>> parseVersion "-1.0.0"+ -- Nothing+ -- >>> parseVersion "0.-1.0"+ -- Nothing+ -- >>> parseVersion "0.0.-1"+ -- Nothing+ --+ -- Non-digits are not allowed.+ --+ -- >>> parseVersion "a.0.0"+ -- Nothing+ -- >>> parseVersion "0.a.0"+ -- Nothing+ -- >>> parseVersion "0.0.a"+ -- Nothing+ --+ -- Partial version numbers are not allowed.+ --+ -- >>> parseVersion "0.0"+ -- Nothing+ --+ -- Extra version numbers are not allowed.+ --+ -- >>> parseVersion "0.0.0.0"+ -- Nothing+ --+ -- Spaces are allowed+ --+ -- >>> parseVersion " 0.0.0"+ -- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+ -- >>> parseVersion "0.0.0 "+ -- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+ --+ -- Interior spaces are not allowed.+ --+ -- >>> parseVersion "0 .0.0"+ -- Nothing+ -- >>> parseVersion "0. 0.0"+ -- Nothing+ --+ -- Each version component cannot be larger than a 64-bit unsigned integer.+ --+ -- >>> parseVersion "18446744073709551615.0.0"+ -- Just (Version {versionMajor = 18446744073709551615, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+ -- >>> parseVersion "18446744073709551616.0.0"+ -- Nothing+ --+ -- Numeric pre-releases tags cannot be larger than a 64-bit unsigned integer.+ --+ -- >>> parseVersion "0.0.0-18446744073709551615"+ -- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [PreReleaseNumeric 18446744073709551615], versionBuilds = []})+ -- >>> parseVersion "0.0.0-18446744073709551616"+ -- Nothing+ --+ -- Build metadata is not numeric so it does not have any limit.+ --+ -- >>> parseVersion "0.0.0+18446744073709551615"+ -- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551615"]})+ -- >>> parseVersion "0.0.0+18446744073709551616"+ -- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551616"]})++ -- ** Constraints++ -- | Partial version numbers are not allowed.+ --+ -- >>> parseConstraint "1.2"+ -- Nothing+ --+ -- Wildcards (also known as "x-ranges") are allowed. The exact character used+ -- for the wildcard is not round-tripped.+ --+ -- >>> renderConstraint <$> parseConstraint "1.2.x"+ -- Just "1.2.x"+ -- >>> renderConstraint <$> parseConstraint "1.2.X"+ -- Just "1.2.x"+ -- >>> renderConstraint <$> parseConstraint "1.2.*"+ -- Just "1.2.x"+ --+ -- An optional equals sign can be included with wildcard constraints.+ --+ -- >>> renderConstraint <$> parseConstraint "=1.2.x"+ -- Just "1.2.x"+ --+ -- Wildcards can be combined with other constraints.+ --+ -- >>> renderConstraint <$> parseConstraint "1.2.x 2.3.4"+ -- Just "1.2.x 2.3.4"+ -- >>> renderConstraint <$> parseConstraint "1.2.x || 2.3.4"+ -- Just "1.2.x || 2.3.4"+ --+ -- Wildcards are allowed at any position.+ --+ -- >>> renderConstraint <$> parseConstraint "1.2.x"+ -- Just "1.2.x"+ -- >>> renderConstraint <$> parseConstraint "1.x.x"+ -- Just "1.x.x"+ -- >>> renderConstraint <$> parseConstraint "x.x.x"+ -- Just "x.x.x"+ --+ -- Non-wildcards cannot come after wildcards.+ --+ -- >>> parseConstraint "1.x.3"+ -- Nothing+ -- >>> parseConstraint "x.2.3"+ -- Nothing+ -- >>> parseConstraint "x.x.3"+ -- Nothing+ -- >>> parseConstraint "x.2.x"+ -- Nothing+ --+ -- Wildcards cannot be used with other operators.+ --+ -- >>> parseConstraint "<1.2.x"+ -- Nothing+ -- >>> parseConstraint "<=1.2.x"+ -- Nothing+ -- >>> parseConstraint ">=1.2.x"+ -- Nothing+ -- >>> parseConstraint ">1.2.x"+ -- Nothing+ -- >>> parseConstraint "~1.2.x"+ -- Nothing+ -- >>> parseConstraint "^1.2.x"+ -- Nothing+ -- >>> parseConstraint "1.2.x - 2.3.4"+ -- Nothing+ -- >>> parseConstraint "1.2.3 - 2.3.x"+ -- Nothing+ --+ -- Spaces are allowed in most places. Extra spaces are not round-tripped.+ --+ -- >>> renderConstraint <$> parseConstraint " 1.2.3 "+ -- Just "1.2.3"+ -- >>> renderConstraint <$> parseConstraint "> 1.2.3"+ -- Just ">1.2.3"+ -- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"+ -- Just "1.2.3 - 2.3.4"+ -- >>> renderConstraint <$> parseConstraint "1.2.3 2.3.4"+ -- Just "1.2.3 2.3.4"+ -- >>> renderConstraint <$> parseConstraint "1.2.3 || 2.3.4"+ -- Just "1.2.3 || 2.3.4"+ --+ -- Parentheses are not allowed. Note that combining two constraints with a+ -- space (and) has higher precedence than combining them with pipes (or). In+ -- other words, @"a b || c"@ parses as @"(a b) || c"@, not @"a (b || c)"@.+ --+ -- >>> parseConstraint "(1.2.3)"+ -- Nothing+ -- >>> parseConstraint "(1.2.3 || >1.2.3) <1.3.0"+ -- Nothing+ -- >>> parseConstraint "(>1.2.3 <1.3.0) || 1.2.3"+ -- Nothing+ --+ -- Most constraints can be round-tripped through parsing and rendering.+ --+ -- >>> renderConstraint <$> parseConstraint "<1.2.3"+ -- Just "<1.2.3"+ -- >>> renderConstraint <$> parseConstraint "<=1.2.3"+ -- Just "<=1.2.3"+ -- >>> renderConstraint <$> parseConstraint "1.2.3"+ -- Just "1.2.3"+ -- >>> renderConstraint <$> parseConstraint ">=1.2.3"+ -- Just ">=1.2.3"+ -- >>> renderConstraint <$> parseConstraint ">1.2.3"+ -- Just ">1.2.3"+ -- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"+ -- Just "1.2.3 - 2.3.4"+ -- >>> renderConstraint <$> parseConstraint "~1.2.3"+ -- Just "~1.2.3"+ -- >>> renderConstraint <$> parseConstraint "^1.2.3"+ -- Just "^1.2.3"+ -- >>> renderConstraint <$> parseConstraint ">1.2.3 <2.0.0"+ -- Just ">1.2.3 <2.0.0"+ -- >>> renderConstraint <$> parseConstraint "1.2.3 || >1.2.3"+ -- Just "1.2.3 || >1.2.3"+ --+ -- Explicit equal signs do not get round-tripped.+ --+ -- >>> renderConstraint <$> parseConstraint "=1.2.3"+ -- Just "1.2.3"+ --+ -- Pre-releases and builds are allowed on any constraints except wildcards.+ --+ -- >>> renderConstraint <$> parseConstraint "1.2.3-p+b"+ -- Just "1.2.3-p+b"+ -- >>> renderConstraint <$> parseConstraint ">1.2.3-p+b"+ -- Just ">1.2.3-p+b"+ -- >>> renderConstraint <$> parseConstraint "1.2.3-p+b - 2.3.4-p+b"+ -- Just "1.2.3-p+b - 2.3.4-p+b"+ -- >>> renderConstraint <$> parseConstraint "~1.2.3-p+b"+ -- Just "~1.2.3-p+b"+ -- >>> renderConstraint <$> parseConstraint "^1.2.3-p+b"+ -- Just "^1.2.3-p+b"+ --+ -- >>> parseConstraint "1.2.x-p+b"+ -- Nothing+ --+ -- These examples show every type of constraint in a single expression.+ --+ -- >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 =1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9 1.2.x"+ -- Just "<1.2.0 <=1.2.1 1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9 1.2.x"+ -- >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 || =1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9 1.2.x"+ -- Just "<1.2.0 <=1.2.1 || 1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9 1.2.x"+ -- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 =1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9 || 1.2.x"+ -- Just "<1.2.0 || <=1.2.1 1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9 || 1.2.x"+ -- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 || =1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9 || 1.2.x"+ -- Just "<1.2.0 || <=1.2.1 || 1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9 || 1.2.x"++ -- ** Satisfying constraints++ -- | Although in general you should use 'satisfiesConstraint', 'parseVersion',+ -- and 'parseConstraint', doing that here makes it hard to tell what the+ -- examples are doing. An operator makes things clearer.+ --+ -- >>> satisfiesConstraint <$> parseConstraint "=1.2.3" <*> parseVersion "1.2.3"+ -- Just True+ -- >>> let version ? constraint = satisfiesConstraint (unsafeParseConstraint constraint) (unsafeParseVersion version)+ -- >>> "1.2.3" ? "=1.2.3"+ -- True+ --+ -- - Less than:+ --+ -- >>> "1.2.2" ? "<1.2.3"+ -- True+ -- >>> "1.2.3" ? "<1.2.3"+ -- False+ -- >>> "1.2.4" ? "<1.2.3"+ -- False+ -- >>> "1.2.3-pre" ? "<1.2.3"+ -- True+ --+ -- - Less than or equal to:+ --+ -- >>> "1.2.2" ? "<=1.2.3"+ -- True+ -- >>> "1.2.3" ? "<=1.2.3"+ -- True+ -- >>> "1.2.4" ? "<=1.2.3"+ -- False+ --+ -- - Equal to:+ --+ -- >>> "1.2.2" ? "=1.2.3"+ -- False+ -- >>> "1.2.3" ? "=1.2.3"+ -- True+ -- >>> "1.2.4" ? "=1.2.3"+ -- False+ -- >>> "1.2.3-pre" ? "=1.2.3"+ -- False+ -- >>> "1.2.3+build" ? "=1.2.3"+ -- True+ --+ -- - Greater than or equal to:+ --+ -- >>> "1.2.2" ? ">=1.2.3"+ -- False+ -- >>> "1.2.3" ? ">=1.2.3"+ -- True+ -- >>> "1.2.4" ? ">=1.2.3"+ -- True+ --+ -- - Greater than:+ --+ -- >>> "1.2.2" ? ">1.2.3"+ -- False+ -- >>> "1.2.3" ? ">1.2.3"+ -- False+ -- >>> "1.2.4" ? ">1.2.3"+ -- True+ -- >>> "1.2.4-pre" ? ">1.2.3"+ -- True+ --+ -- >>> "1.2.4" ? ">1.2.3-pre"+ -- True+ --+ -- - And:+ --+ -- >>> "1.2.3" ? ">1.2.3 <1.2.5"+ -- False+ -- >>> "1.2.4" ? ">1.2.3 <1.2.5"+ -- True+ -- >>> "1.2.5" ? ">1.2.3 <1.2.5"+ -- False+ --+ -- - Or:+ --+ -- >>> "1.2.2" ? "1.2.3 || 1.2.4"+ -- False+ -- >>> "1.2.3" ? "1.2.3 || 1.2.4"+ -- True+ -- >>> "1.2.4" ? "1.2.3 || 1.2.4"+ -- True+ -- >>> "1.2.5" ? "1.2.3 || 1.2.4"+ -- False+ --+ -- - And & or:+ --+ -- >>> "1.2.2" ? "1.2.2 || >1.2.3 <1.3.0"+ -- True+ -- >>> "1.2.3" ? "1.2.2 || >1.2.3 <1.3.0"+ -- False+ -- >>> "1.2.4" ? "1.2.2 || >1.2.3 <1.3.0"+ -- True+ -- >>> "1.3.0" ? "1.2.2 || >1.2.3 <1.3.0"+ -- False+ --+ -- - Hyphen:+ --+ -- >>> "1.2.2" ? "1.2.3 - 1.2.4"+ -- False+ -- >>> "1.2.3" ? "1.2.3 - 1.2.4"+ -- True+ -- >>> "1.2.4" ? "1.2.3 - 1.2.4"+ -- True+ -- >>> "1.2.5" ? "1.2.3 - 1.2.4"+ -- False+ --+ -- - Tilde:+ --+ -- >>> "1.2.2" ? "~1.2.3"+ -- False+ -- >>> "1.2.3" ? "~1.2.3"+ -- True+ -- >>> "1.2.4" ? "~1.2.3"+ -- True+ -- >>> "1.3.0" ? "~1.2.3"+ -- False+ --+ -- - Caret:+ --+ -- >>> "1.2.2" ? "^1.2.3"+ -- False+ -- >>> "1.2.3" ? "^1.2.3"+ -- True+ -- >>> "1.2.4" ? "^1.2.3"+ -- True+ -- >>> "1.3.0" ? "^1.2.3"+ -- True+ -- >>> "2.0.0" ? "^1.2.3"+ -- False+ --+ -- >>> "0.2.2" ? "^0.2.3"+ -- False+ -- >>> "0.2.3" ? "^0.2.3"+ -- True+ -- >>> "0.2.4" ? "^0.2.3"+ -- True+ -- >>> "0.3.0" ? "^0.2.3"+ -- False+ --+ -- >>> "0.0.2" ? "^0.0.3"+ -- False+ -- >>> "0.0.3" ? "^0.0.3"+ -- True+ -- >>> "0.0.4" ? "^0.0.3"+ -- False+ --+ -- - Wildcard:+ --+ -- >>> "1.1.0" ? "1.2.x"+ -- False+ -- >>> "1.2.3" ? "1.2.x"+ -- True+ -- >>> "1.3.0" ? "1.2.x"+ -- False+ --+ -- >>> "0.1.0" ? "1.x.x"+ -- False+ -- >>> "1.0.0" ? "1.x.x"+ -- True+ -- >>> "1.2.3" ? "1.x.x"+ -- True+ -- >>> "2.0.0" ? "1.x.x"+ -- False+ --+ -- >>> "0.0.0" ? "x.x.x"+ -- True+ -- >>> "1.2.3" ? "x.x.x"+ -- True+ -- >>> "2.0.0" ? "x.x.x"+ -- True+ )+where++import qualified Salve.Internal as Salve++-- $setup+-- >>> import Control.Applicative+-- >>> import Salve
+ source/library/Salve/Internal.hs view
@@ -0,0 +1,1012 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE DeriveGeneric #-}++-- | WARNING: This module should be considered private! If you find yourself+-- wanting to import something from this module, please open an issue to get+-- that thing exported from "Salve".+module Salve.Internal+ ( Version (..),+ PreRelease (..),+ Build (..),+ Constraint (..),+ makeVersion,+ initialVersion,+ parseVersion,+ parsePreRelease,+ parseBuild,+ parseConstraint,+ unsafeParseVersion,+ unsafeParsePreRelease,+ unsafeParseBuild,+ unsafeParseConstraint,+ renderVersion,+ renderPreRelease,+ renderBuild,+ renderConstraint,+ isUnstable,+ isStable,+ fromBaseVersion,+ toBaseVersion,+ bumpMajor,+ bumpMinor,+ bumpPatch,+ satisfiesConstraint,+ majorLens,+ minorLens,+ patchLens,+ preReleasesLens,+ buildsLens,+ Operator (..),+ Wildcard (..),+ constraintLT,+ constraintLE,+ constraintEQ,+ constraintGE,+ constraintGT,+ constraintAnd,+ constraintOr,+ constraintHyphen,+ constraintTilde,+ constraintCaret,+ )+where++import qualified Control.Monad as Monad+import qualified Data.Char as Char+import qualified Data.Data as Data+import qualified Data.List as List+import qualified Data.Maybe as Maybe+import qualified Data.Monoid as Monoid+import qualified Data.Ord as Ord+import qualified Data.Version as Version+import qualified Data.Word as Word+import qualified GHC.Generics as Generics+import qualified Text.ParserCombinators.ReadP as ReadP++-- $setup+--+-- >>> import Control.Applicative (Const(..))+-- >>> let view lens record = getConst (lens Const record)+--+-- >>> import Data.Functor.Identity (Identity(..))+-- >>> let set lens field record = runIdentity (lens (const (Identity field)) record)+--+-- >>> import Control.Applicative ((<$>), (<*>))++-- * Public++-- | A semantic version number. Versions have five parts:+--+-- 1. 'majorLens': The major version number.+-- 2. 'minorLens': The minor version number.+-- 3. 'patchLens': The patch version number.+-- 4. 'preReleasesLens': A list of pre-release identifiers.+-- 5. 'buildsLens': A list of build metadata.+--+-- Use 'parseVersion' to create versions.+data Version = Version+ { versionMajor :: Word.Word64,+ versionMinor :: Word.Word64,+ versionPatch :: Word.Word64,+ versionPreReleases :: [PreRelease],+ versionBuilds :: [Build]+ }+ deriving (Data.Data, Eq, Generics.Generic, Read, Show)++-- | In general, versions compare in the way that you would expect. First the+-- major version numbers are compared, then the minors, then the patches.+--+-- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "2.0.0"+-- Just LT+-- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "1.3.0"+-- Just LT+-- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "1.2.4"+-- Just LT+--+-- Numbers are compared numerically, not alphabetically.+--+-- >>> compare <$> parseVersion "0.0.9" <*> parseVersion "0.0.10"+-- Just LT+--+-- If all the numbers are the same, the pre-releases are compared.+--+-- >>> compare <$> parseVersion "1.2.3-a" <*> parseVersion "1.2.3-b"+-- Just LT+--+-- A version with a pre-release is always less than a version without one as+-- long as the other parts are the same.+--+-- >>> compare <$> parseVersion "1.2.3-pre" <*> parseVersion "1.2.3"+-- Just LT+-- >>> compare <$> parseVersion "1.2.4-pre" <*> parseVersion "1.2.3"+-- Just GT+--+-- Builds are not considered when comparing versions.+--+-- >>> compare <$> parseVersion "1.2.3+a" <*> parseVersion "1.2.3+b"+-- Just EQ+-- >>> (==) <$> parseVersion "1.2.3+a" <*> parseVersion "1.2.3+b"+-- Just False+instance Ord Version where+ compare x y =+ Monoid.mconcat+ [ Ord.comparing versionMajor x y,+ Ord.comparing versionMinor x y,+ Ord.comparing versionPatch x y,+ case both versionPreReleases (x, y) of+ ([], []) -> EQ+ ([], _) -> GT+ (_, []) -> LT+ (p, q) -> compare p q+ ]++-- | Pre-release information attached to a version. These can either be numeric+-- or textual. They must not be empty.+--+-- - Numeric: Can be any non-negative integer. Cannot have leading zeros.+--+-- - Textual: Can be any string of ASCII digits, letters, or hyphens. Cannot be+-- all digits, as that would be numeric.+--+-- In general, pre-releases must match the regular expression+-- @\/^[-0-9A-Za-z]+$\/@.+--+-- Use 'parsePreRelease' to create pre-releases.+data PreRelease+ = PreReleaseNumeric Word.Word64+ | PreReleaseTextual String+ deriving (Data.Data, Eq, Generics.Generic, Read, Show)++-- | Numeric pre-releases are always less than textual pre-releases.+--+-- >>> compare <$> parsePreRelease "1" <*> parsePreRelease "a"+-- Just LT+--+-- Numeric pre-releases are compared numerically.+--+-- >>> compare <$> parsePreRelease "9" <*> parsePreRelease "10"+-- Just LT+--+-- Textual pre-releases are compared alphabetically.+--+-- >>> compare <$> parsePreRelease "p10" <*> parsePreRelease "p9"+-- Just LT+instance Ord PreRelease where+ compare x y = case (x, y) of+ (PreReleaseNumeric n, PreReleaseNumeric m) -> compare n m+ (PreReleaseNumeric _, PreReleaseTextual _) -> LT+ (PreReleaseTextual _, PreReleaseNumeric _) -> GT+ (PreReleaseTextual s, PreReleaseTextual t) -> compare s t++-- | Build metadata attached to a version. These are similar to+-- 'PreRelease's with some key differences:+--+-- 1. There is no such thing as numeric builds. Even though builds can look+-- like numbers, all builds are textual.+-- 2. As a result, builds that look numeric are allowed to have leading zeros.+-- 3. Builds cannot be compared. That is, they do not have an 'Ord' instance.+--+-- Use 'parseBuild' to create builds.+newtype Build = Build String+ deriving (Data.Data, Eq, Generics.Generic, Read, Show)++-- | Constrains allowable version numbers.+--+-- Use 'parseConstraint' to create constraints and 'satisfiesConstraint' to see+-- if a version number satisfies a constraint.+data Constraint+ = ConstraintOperator Operator Version+ | ConstraintHyphen Version Version+ | ConstraintWildcard Wildcard+ | ConstraintAnd Constraint Constraint+ | ConstraintOr Constraint Constraint+ deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)++-- | Makes a new version number.+--+-- >>> makeVersion 1 2 3 [unsafeParsePreRelease "pre"] [unsafeParseBuild "build"]+-- Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "pre"], versionBuilds = [Build "build"]}+--+-- This can be a useful alternative to 'parseVersion' if you want a total way+-- to create a version.+makeVersion ::+ Word.Word64 ->+ Word.Word64 ->+ Word.Word64 ->+ [PreRelease] ->+ [Build] ->+ Version+makeVersion major minor patch preReleases builds =+ Version+ { versionMajor = major,+ versionMinor = minor,+ versionPatch = patch,+ versionPreReleases = preReleases,+ versionBuilds = builds+ }++-- | The initial version number for development.+--+-- >>> initialVersion+-- Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []}+initialVersion :: Version+initialVersion = makeVersion 0 0 0 [] []++-- | Attempts to parse a version. This parser follows+-- [SemVer's BNF](https://github.com/mojombo/semver/blob/eb9aac5/semver.md#backusnaur-form-grammar-for-valid-semver-versions).+--+-- >>> parseVersion "1.2.3-p.4+b.5"+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "p",PreReleaseNumeric 4], versionBuilds = [Build "b",Build "5"]})+--+-- Returns 'Nothing' if the parse fails.+--+-- >>> parseVersion "wrong"+-- Nothing+--+-- Whitespace is allowed.+--+-- >>> parseVersion " 1.2.3 "+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+parseVersion :: String -> Maybe Version+parseVersion =+ parse+ ( do+ ReadP.skipSpaces+ version <- versionP+ ReadP.skipSpaces+ pure version+ )++-- | Attempts to parse a pre-release.+--+-- >>> parsePreRelease "pre"+-- Just (PreReleaseTextual "pre")+-- >>> parsePreRelease "1"+-- Just (PreReleaseNumeric 1)+--+-- Returns 'Nothing' if the parse fails.+--+-- >>> parsePreRelease "wrong!"+-- Nothing+--+-- Numeric pre-releases cannot contain leading zeros.+--+-- >>> parsePreRelease "01"+-- Nothing+parsePreRelease :: String -> Maybe PreRelease+parsePreRelease = parse preReleaseP++-- | Attempts to parse a build.+--+-- >>> parseBuild "build"+-- Just (Build "build")+-- >>> parseBuild "1"+-- Just (Build "1")+--+-- Returns 'Nothing' if the parse fails.+--+-- >>> parseBuild "wrong!"+-- Nothing+--+-- Unlike pre-releases, numeric builds can have leading zeros.+--+-- >>> parseBuild "01"+-- Just (Build "01")+parseBuild :: String -> Maybe Build+parseBuild = parse buildP++-- | Attempts to parse a constraint. This parser mostly follows+-- [npm's BNF](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#range-grammar).+--+-- >>> parseConstraint ">1.2.3"+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+--+-- Returns 'Nothing' if the parse fails.+--+-- >>> parseConstraint "wrong"+-- Nothing+--+-- The two departures from npm's BNF are that x-ranges cannot be used with+-- other operators and partial version numbers are not allowed.+--+-- >>> parseConstraint "1.2.x"+-- Just (ConstraintWildcard (WildcardPatch 1 2))+-- >>> parseConstraint ">=1.2.x"+-- Nothing+--+-- >>> parseConstraint "1.2"+-- Nothing+-- >>> parseConstraint ">=1.2"+-- Nothing+parseConstraint :: String -> Maybe Constraint+parseConstraint = parse constraintsP++-- | Parses a version.+--+-- >>> unsafeParseVersion "1.2.3-p.4+b.5"+-- Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "p",PreReleaseNumeric 4], versionBuilds = [Build "b",Build "5"]}+--+-- Raises an exception if the parse fails.+--+-- >>> unsafeParseVersion "wrong"+-- *** Exception: unsafeParseVersion: invalid version: "wrong"+-- ...+--+-- See 'parseVersion' for a safe version of this function.+unsafeParseVersion :: String -> Version+unsafeParseVersion s = case parseVersion s of+ Nothing -> error ("unsafeParseVersion: invalid version: " <> show s)+ Just v -> v++-- | Parses a pre-release.+--+-- >>> unsafeParsePreRelease "pre"+-- PreReleaseTextual "pre"+--+-- Raises an exception if the parse fails.+--+-- >>> unsafeParsePreRelease "wrong!"+-- *** Exception: unsafeParsePreRelease: invalid pre-release: "wrong!"+-- ...+--+-- See 'parsePreRelease' for a safe version of this function.+unsafeParsePreRelease :: String -> PreRelease+unsafeParsePreRelease s = case parsePreRelease s of+ Nothing -> error ("unsafeParsePreRelease: invalid pre-release: " <> show s)+ Just p -> p++-- | Parses a build.+--+-- >>> unsafeParseBuild "build"+-- Build "build"+--+-- Raises an exception if the parse fails.+--+-- >>> unsafeParseBuild "wrong!"+-- Build "*** Exception: unsafeParseBuild: invalid build: "wrong!"+-- ...+--+-- See 'parseBuild' for a safe version of this function.+unsafeParseBuild :: String -> Build+unsafeParseBuild s = case parseBuild s of+ Nothing -> error ("unsafeParseBuild: invalid build: " <> show s)+ Just b -> b++-- | Parses a constraint.+--+-- >>> unsafeParseConstraint ">1.2.3"+-- ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+--+-- Raises an exception if the parse fails.+--+-- >>> unsafeParseConstraint "wrong"+-- *** Exception: unsafeParseConstraint: invalid constraint: "wrong"+-- ...+--+-- See 'parseConstraint' for a safe version of this function.+unsafeParseConstraint :: String -> Constraint+unsafeParseConstraint s = case parseConstraint s of+ Nothing -> error ("unsafeParseConstraint: invalid constraint: " <> show s)+ Just c -> c++-- | Renders a version.+--+-- >>> renderVersion <$> parseVersion "1.2.3-p.4+b.5"+-- Just "1.2.3-p.4+b.5"+renderVersion :: Version -> String+renderVersion v =+ concat+ [ show (versionMajor v),+ ".",+ show (versionMinor v),+ ".",+ show (versionPatch v),+ renderPreReleases (versionPreReleases v),+ renderBuilds (versionBuilds v)+ ]++-- | Renders a pre-release.+--+-- >>> renderPreRelease <$> parsePreRelease "pre"+-- Just "pre"+-- >>> renderPreRelease <$> parsePreRelease "1"+-- Just "1"+renderPreRelease :: PreRelease -> String+renderPreRelease p = case p of+ PreReleaseNumeric n -> show n+ PreReleaseTextual s -> s++-- | Renders a build.+--+-- >>> renderBuild <$> parseBuild "build"+-- Just "build"+-- >>> renderBuild <$> parseBuild "1"+-- Just "1"+renderBuild :: Build -> String+renderBuild (Build b) = b++-- | Renders a constraint.+--+-- >>> renderConstraint <$> parseConstraint ">1.2.3"+-- Just ">1.2.3"+--+-- Parsing and rendering a constraint doesn't always return what you started+-- with.+--+-- >>> renderConstraint <$> parseConstraint "=1.2.3"+-- Just "1.2.3"+renderConstraint :: Constraint -> String+renderConstraint c = case c of+ ConstraintOperator o v ->+ let s = renderVersion v+ in case o of+ OperatorLT -> '<' : s+ OperatorLE -> '<' : '=' : s+ OperatorEQ -> s+ OperatorGE -> '>' : '=' : s+ OperatorGT -> '>' : s+ OperatorTilde -> '~' : s+ OperatorCaret -> '^' : s+ ConstraintHyphen l r -> unwords [renderVersion l, "-", renderVersion r]+ ConstraintWildcard w -> case w of+ WildcardMajor -> "x.x.x"+ WildcardMinor m -> show m <> ".x.x"+ WildcardPatch m n -> List.intercalate "." [show m, show n, "x"]+ ConstraintAnd l r -> unwords (fmap renderConstraint [l, r])+ ConstraintOr l r -> unwords [renderConstraint l, "||", renderConstraint r]++-- | Returns 'True' if the major version number is zero, 'False' otherwise.+--+-- >>> isUnstable <$> parseVersion "0.1.2"+-- Just True+-- >>> isUnstable <$> parseVersion "1.0.0"+-- Just False+isUnstable :: Version -> Bool+isUnstable v = versionMajor v == 0++-- | Returns 'True' if the major version number is not zero, 'False' otherwise.+--+-- >>> isStable <$> parseVersion "1.0.0"+-- Just True+-- >>> isStable <$> parseVersion "0.1.2"+-- Just False+isStable :: Version -> Bool+isStable v = not (isUnstable v)++-- | Converts from a 'Version.Version' from the @base@ package.+--+-- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2, 3]+-- "1.2.3"+--+-- Missing version components are set to zero.+--+-- >>> renderVersion . fromBaseVersion $ Version.makeVersion []+-- "0.0.0"+-- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1]+-- "1.0.0"+-- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2]+-- "1.2.0"+--+-- Extra version components are ignored.+--+-- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2, 3, 4]+-- "1.2.3"+--+-- Tags are ignored.+--+-- >>> renderVersion . fromBaseVersion $ Version.Version [] ["ignored"]+-- "0.0.0"+fromBaseVersion :: Version.Version -> Version+fromBaseVersion v = case Version.versionBranch v of+ (m : n : p : _) -> mkV (fromIntegral m) (fromIntegral n) (fromIntegral p)+ (m : n : _) -> mkV (fromIntegral m) (fromIntegral n) 0+ (m : _) -> mkV (fromIntegral m) 0 0+ _ -> mkV 0 0 0++-- | Converts to a 'Version.Version' from the @base@ package.+--+-- >>> toBaseVersion <$> parseVersion "1.2.3"+-- Just (Version {versionBranch = [1,2,3], versionTags = []})+--+-- Pre-releases and builds are converted to tags.+--+-- >>> toBaseVersion <$> parseVersion "1.2.3-pre+build"+-- Just (Version {versionBranch = [1,2,3], versionTags = ["pre","build"]})+toBaseVersion :: Version -> Version.Version+toBaseVersion v =+ Version.Version+ (fmap fromIntegral [versionMajor v, versionMinor v, versionPatch v])+ ( mconcat+ [ fmap renderPreRelease (versionPreReleases v),+ fmap renderBuild (versionBuilds v)+ ]+ )++-- | Increments the major version number.+--+-- >>> bumpMajor <$> parseVersion "0.0.0"+-- Just (Version {versionMajor = 1, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- The minor and patch numbers are reset to zero.+--+-- >>> bumpMajor <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- The pre-releases and builds are removed.+--+-- >>> bumpMajor <$> parseVersion "0.0.0-pre+build"+-- Just (Version {versionMajor = 1, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- Consider using 'majorLens' if you want to arbitrarily change the major+-- number, or if you don't want the other parts of the version to change.+bumpMajor :: Version -> Version+bumpMajor v = makeVersion (versionMajor v + 1) 0 0 [] []++-- | Increments the minor version number.+--+-- >>> bumpMinor <$> parseVersion "0.0.0"+-- Just (Version {versionMajor = 0, versionMinor = 1, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- The patch number is reset to zero.+--+-- >>> bumpMinor <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 1, versionMinor = 3, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- The pre-releases and builds are removed.+--+-- >>> bumpMinor <$> parseVersion "0.0.0-pre+build"+-- Just (Version {versionMajor = 0, versionMinor = 1, versionPatch = 0, versionPreReleases = [], versionBuilds = []})+--+-- Consider using 'minorLens' if you want to arbitrarily change the minor+-- number, or if you don't want the other parts of the version to change.+bumpMinor :: Version -> Version+bumpMinor v = makeVersion (versionMajor v) (versionMinor v + 1) 0 [] []++-- | Increments the patch number.+--+-- >>> bumpPatch <$> parseVersion "0.0.0"+-- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 1, versionPreReleases = [], versionBuilds = []})+--+-- The major and minor numbers are not changed.+--+-- >>> bumpPatch <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 4, versionPreReleases = [], versionBuilds = []})+--+-- The pre-releases and builds are removed.+--+-- >>> bumpPatch <$> parseVersion "0.0.0-pre+build"+-- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 1, versionPreReleases = [], versionBuilds = []})+--+-- Consider using 'patchLens' if you want to arbitrarily change the patch+-- number, or if you don't want the other parts of the version to change.+bumpPatch :: Version -> Version+bumpPatch v =+ makeVersion (versionMajor v) (versionMinor v) (versionPatch v + 1) [] []++-- | Returns 'True' if the version satisfies the constraint, 'False' otherwise.+--+-- >>> satisfiesConstraint <$> parseConstraint ">1.2.0" <*> parseVersion "1.2.3"+-- Just True+satisfiesConstraint :: Constraint -> Version -> Bool+satisfiesConstraint c = satisfiesSC (toSC c)++-- | Focuses on the major version number.+--+-- >>> view majorLens <$> parseVersion "1.2.3-pre.4+build.5"+-- Just 1+-- >>> set majorLens 4 <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 4, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+majorLens ::+ (Functor f) => (Word.Word64 -> f Word.Word64) -> Version -> f Version+majorLens f v = fmap (\m -> v {versionMajor = m}) (f (versionMajor v))++-- | Focuses on the minor version number.+--+-- >>> view minorLens <$> parseVersion "1.2.3-pre.4+build.5"+-- Just 2+-- >>> set minorLens 4 <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 1, versionMinor = 4, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+minorLens ::+ (Functor f) => (Word.Word64 -> f Word.Word64) -> Version -> f Version+minorLens f v = fmap (\n -> v {versionMinor = n}) (f (versionMinor v))++-- | Focuses on the patch version number.+--+-- >>> view patchLens <$> parseVersion "1.2.3-pre.4+build.5"+-- Just 3+-- >>> set patchLens 4 <$> parseVersion "1.2.3"+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 4, versionPreReleases = [], versionBuilds = []})+patchLens ::+ (Functor f) => (Word.Word64 -> f Word.Word64) -> Version -> f Version+patchLens f v = fmap (\p -> v {versionPatch = p}) (f (versionPatch v))++-- | Focuses on the pre-release identifiers.+--+-- >>> view preReleasesLens <$> parseVersion "1.2.3-pre.4+build.5"+-- Just [PreReleaseTextual "pre",PreReleaseNumeric 4]+-- >>> set preReleasesLens [] <$> parseVersion "1.2.3-pre"+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+preReleasesLens ::+ (Functor f) => ([PreRelease] -> f [PreRelease]) -> Version -> f Version+preReleasesLens f v =+ fmap (\ps -> v {versionPreReleases = ps}) (f (versionPreReleases v))++-- | Focuses on the build metadata.+--+-- >>> view buildsLens <$> parseVersion "1.2.3-pre.4+build.5"+-- Just [Build "build",Build "5"]+-- >>> set buildsLens [] <$> parseVersion "1.2.3+build"+-- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})+buildsLens :: (Functor f) => ([Build] -> f [Build]) -> Version -> f Version+buildsLens f v = fmap (\bs -> v {versionBuilds = bs}) (f (versionBuilds v))++-- * Private++-- ** Types++data Operator+ = OperatorLT+ | OperatorLE+ | OperatorEQ+ | OperatorGE+ | OperatorGT+ | OperatorTilde+ | OperatorCaret+ deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)++data Wildcard+ = WildcardMajor+ | WildcardMinor Word.Word64+ | WildcardPatch Word.Word64 Word.Word64+ deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)++-- ** Constructors++-- | Makes a new constraint that must be less than the version number.+--+-- >>> constraintLT <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "<1.2.3"+-- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintLT :: Version -> Constraint+constraintLT = ConstraintOperator OperatorLT++-- | Makes a new constraint that must be less than or euqal to the version+-- number.+--+-- >>> constraintLE <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "<=1.2.3"+-- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintLE :: Version -> Constraint+constraintLE = ConstraintOperator OperatorLE++-- | Makes a new constraint that must be equal to the version number.+--+-- >>> constraintEQ <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "=1.2.3"+-- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintEQ :: Version -> Constraint+constraintEQ = ConstraintOperator OperatorEQ++-- | Makes a new constraint that must be greater than or equal to the version+-- number.+--+-- >>> constraintGE <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint ">=1.2.3"+-- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintGE :: Version -> Constraint+constraintGE = ConstraintOperator OperatorGE++-- | Makes a new constraint that must be greater than the version number.+--+-- >>> constraintGT <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint ">1.2.3"+-- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintGT :: Version -> Constraint+constraintGT = ConstraintOperator OperatorGT++-- | Makes a new constraint that must satisfy both constraints.+--+-- >>> constraintAnd <$> (constraintGE <$> parseVersion "1.2.3") <*> (constraintLT <$> parseVersion "2.0.0")+-- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))+-- >>> parseConstraint ">=1.2.3 <2.0.0"+-- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))+constraintAnd :: Constraint -> Constraint -> Constraint+constraintAnd = ConstraintAnd++-- | Makes a new constraint that must satisfy either constraint.+--+-- >>> constraintOr <$> (constraintEQ <$> parseVersion "1.2.3") <*> (constraintGT <$> parseVersion "1.2.3")+-- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))+-- >>> parseConstraint "=1.2.3 || >1.2.3"+-- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))+constraintOr :: Constraint -> Constraint -> Constraint+constraintOr = ConstraintOr++-- | Makes a new constraint that must be between the versions, inclusive.+--+-- >>> constraintHyphen <$> parseVersion "1.2.3" <*> parseVersion "2.3.4"+-- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "1.2.3 - 2.3.4"+-- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))+constraintHyphen :: Version -> Version -> Constraint+constraintHyphen = ConstraintHyphen++-- | Makes a new constraint that allows changes to the patch version number.+--+-- >>> constraintTilde <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "~1.2.3"+-- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintTilde :: Version -> Constraint+constraintTilde = ConstraintOperator OperatorTilde++-- | Makes a new constraint that allows changes that do not modify the+-- left-most non-zero version number.+--+-- >>> constraintCaret <$> parseVersion "1.2.3"+-- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+-- >>> parseConstraint "^1.2.3"+-- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))+constraintCaret :: Version -> Constraint+constraintCaret = ConstraintOperator OperatorCaret++-- ** Parsing++versionP :: ReadP.ReadP Version+versionP = do+ major <- numberP+ Monad.void (ReadP.char '.')+ minor <- numberP+ Monad.void (ReadP.char '.')+ patch <- numberP+ preReleases <- preReleasesP+ makeVersion major minor patch preReleases <$> buildsP++preReleasesP :: ReadP.ReadP [PreRelease]+preReleasesP =+ ReadP.option+ []+ ( do+ Monad.void (ReadP.char '-')+ ReadP.sepBy1 preReleaseP (ReadP.char '.')+ )++preReleaseP :: ReadP.ReadP PreRelease+preReleaseP = preReleaseNumberP ReadP.<++ preReleaseStringP++preReleaseNumberP :: ReadP.ReadP PreRelease+preReleaseNumberP = PreReleaseNumeric <$> numberP++preReleaseStringP :: ReadP.ReadP PreRelease+preReleaseStringP = do+ s <- ReadP.munch1 isIdentifier+ if all Char.isDigit s then ReadP.pfail else pure (PreReleaseTextual s)++buildsP :: ReadP.ReadP [Build]+buildsP =+ ReadP.option+ []+ ( do+ Monad.void (ReadP.char '+')+ ReadP.sepBy1 buildP (ReadP.char '.')+ )++buildP :: ReadP.ReadP Build+buildP = do+ b <- ReadP.munch1 isIdentifier+ pure (Build b)++numberP :: ReadP.ReadP Word.Word64+numberP = zeroP ReadP.<++ nonZeroP++zeroP :: ReadP.ReadP Word.Word64+zeroP = do+ Monad.void (ReadP.char '0')+ pure 0++nonZeroP :: ReadP.ReadP Word.Word64+nonZeroP = do+ x <- ReadP.satisfy isAsciiDigitNonZero+ ys <- ReadP.munch Char.isDigit+ maybe ReadP.pfail pure . toWord64 . stringToIntegral $ x : ys++constraintsP :: ReadP.ReadP Constraint+constraintsP = do+ spacesP+ cs <- ReadP.sepBy1 constraintP orP+ spacesP+ pure (foldr1 constraintOr cs)++constraintP :: ReadP.ReadP Constraint+constraintP = do+ cs <- ReadP.sepBy1 simpleP spaces1P+ pure (foldr1 constraintAnd cs)++hyphenatedP :: ReadP.ReadP Constraint+hyphenatedP = do+ v <- versionP+ hyphenP+ constraintHyphen v <$> versionP++simpleP :: ReadP.ReadP Constraint+simpleP = ReadP.choice [hyphenatedP, wildcardConstraintP, primitiveP]++wildcardConstraintP :: ReadP.ReadP Constraint+wildcardConstraintP = do+ ReadP.optional (ReadP.char '=')+ ConstraintWildcard <$> wildcardP++wildcardP :: ReadP.ReadP Wildcard+wildcardP = ReadP.choice [wildcardPatchP, wildcardMinorP, wildcardMajorP]++wildcardPatchP :: ReadP.ReadP Wildcard+wildcardPatchP = do+ m <- numberP+ Monad.void (ReadP.char '.')+ n <- numberP+ Monad.void (ReadP.char '.')+ Monad.void (ReadP.satisfy isWildcard)+ pure (WildcardPatch m n)++wildcardMinorP :: ReadP.ReadP Wildcard+wildcardMinorP = do+ m <- numberP+ Monad.void (ReadP.char '.')+ Monad.void (ReadP.satisfy isWildcard)+ Monad.void (ReadP.char '.')+ Monad.void (ReadP.satisfy isWildcard)+ pure (WildcardMinor m)++wildcardMajorP :: ReadP.ReadP Wildcard+wildcardMajorP = do+ Monad.void (ReadP.satisfy isWildcard)+ Monad.void (ReadP.char '.')+ Monad.void (ReadP.satisfy isWildcard)+ Monad.void (ReadP.char '.')+ Monad.void (ReadP.satisfy isWildcard)+ pure WildcardMajor++primitiveP :: ReadP.ReadP Constraint+primitiveP = do+ o <- operatorP+ spacesP+ ConstraintOperator o <$> versionP++operatorP :: ReadP.ReadP Operator+operatorP =+ ReadP.choice+ [ ReadP.string "<=" >> pure OperatorLE,+ ReadP.string ">=" >> pure OperatorGE,+ ReadP.char '<' >> pure OperatorLT,+ ReadP.char '>' >> pure OperatorGT,+ ReadP.char '=' >> pure OperatorEQ,+ ReadP.char '^' >> pure OperatorCaret,+ ReadP.char '~' >> pure OperatorTilde,+ pure OperatorEQ+ ]++hyphenP :: ReadP.ReadP ()+hyphenP = do+ spaces1P+ Monad.void (ReadP.char '-')+ spaces1P++orP :: ReadP.ReadP ()+orP = do+ spaces1P+ Monad.void (ReadP.string "||")+ spaces1P++spaces1P :: ReadP.ReadP ()+spaces1P = Monad.void (ReadP.munch1 (== ' '))++spacesP :: ReadP.ReadP ()+spacesP = Monad.void (ReadP.munch (== ' '))++-- ** Rendering++renderPreReleases :: [PreRelease] -> String+renderPreReleases ps =+ if null ps then "" else '-' : List.intercalate "." (fmap renderPreRelease ps)++renderBuilds :: [Build] -> String+renderBuilds bs =+ if null bs then "" else '+' : List.intercalate "." (fmap renderBuild bs)++-- ** Helpers++both :: (a -> b) -> (a, a) -> (b, b)+both f (x, y) = (f x, f y)++isAsciiDigitNonZero :: Char -> Bool+isAsciiDigitNonZero c = Char.isDigit c && (c /= '0')++isIdentifier :: Char -> Bool+isIdentifier c = (Char.isAscii c && Char.isAlphaNum c) || (c == '-')++isWildcard :: Char -> Bool+isWildcard c = (c == 'x') || (c == '*') || (c == 'X')++parse :: ReadP.ReadP a -> String -> Maybe a+parse p s =+ let p' = ReadP.readP_to_S p+ in Maybe.listToMaybe+ ( do+ (x, "") <- p' s+ pure x+ )++stringToIntegral :: (Integral a) => String -> a+stringToIntegral =+ foldl (\n d -> (n * 10) + (fromIntegral (fromEnum d) - 48)) 0++toWord64 :: Integer -> Maybe Word.Word64+toWord64 n+ | n < 0 = Nothing+ | n > fromIntegral (maxBound :: Word.Word64) = Nothing+ | otherwise = Just (fromIntegral n)++-- * Simple constraints++-- | Simple constraints are just as expressive as 'Constraint's, but they are+-- easier to reason about. You can think of them as the desugared version of+-- 'Constraint's.+data SimpleConstraint+ = SCLT Version+ | SCEQ Version+ | SCGT Version+ | SCAnd SimpleConstraint SimpleConstraint+ | SCOr SimpleConstraint SimpleConstraint+ deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)++mkV :: Word.Word64 -> Word.Word64 -> Word.Word64 -> Version+mkV m n p = makeVersion m n p [] []++{- hlint ignore satisfiesSC "Redundant compare" -}+satisfiesSC :: SimpleConstraint -> Version -> Bool+satisfiesSC c v = case c of+ SCLT u -> v < u+ -- This uses `compare` rather than `==` to ignore build metadata.+ SCEQ u -> compare v u == EQ+ SCGT u -> v > u+ SCAnd l r -> satisfiesSC l v && satisfiesSC r v+ SCOr l r -> satisfiesSC l v || satisfiesSC r v++scLE :: Version -> SimpleConstraint+scLE v = SCOr (SCLT v) (SCEQ v)++scGE :: Version -> SimpleConstraint+scGE v = SCOr (SCGT v) (SCEQ v)++toSC :: Constraint -> SimpleConstraint+toSC c = case c of+ ConstraintOperator o v -> case o of+ OperatorLT -> SCLT v+ OperatorLE -> scLE v+ OperatorEQ -> SCEQ v+ OperatorGE -> scGE v+ OperatorGT -> SCGT v+ OperatorTilde ->+ SCAnd (scGE v) (SCLT (mkV (versionMajor v) (versionMinor v + 1) 0))+ OperatorCaret ->+ SCAnd+ (scGE v)+ ( SCLT+ ( case (versionMajor v, versionMinor v, versionPatch v) of+ (0, 0, p) -> mkV 0 0 (p + 1)+ (0, n, _) -> mkV 0 (n + 1) 0+ (m, _, _) -> mkV (m + 1) 0 0+ )+ )+ ConstraintHyphen l h -> SCAnd (scGE l) (scLE h)+ ConstraintWildcard w -> case w of+ WildcardMajor -> scGE initialVersion+ WildcardMinor m -> SCAnd (scGE (mkV m 0 0)) (SCLT (mkV (m + 1) 0 0))+ WildcardPatch m n -> SCAnd (scGE (mkV m n 0)) (SCLT (mkV m (n + 1) 0))+ ConstraintAnd l r -> SCAnd (toSC l) (toSC r)+ ConstraintOr l r -> SCOr (toSC l) (toSC r)
+ source/test-suite/Main.hs view
@@ -0,0 +1,360 @@+import qualified Control.Monad as Monad+import qualified Salve+import qualified System.Exit as Exit+import qualified Test.HUnit as Test++main :: IO ()+main = do+ counts <-+ Test.runTestTT $+ Test.TestList+ [ (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "2.0.0")+ Test.~?= Just LT,+ (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "1.3.0")+ Test.~?= Just LT,+ (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "1.2.4")+ Test.~?= Just LT,+ (compare <$> Salve.parseVersion "0.0.9" <*> Salve.parseVersion "0.0.10")+ Test.~?= Just LT,+ ( compare+ <$> Salve.parseVersion "1.2.3-a"+ <*> Salve.parseVersion+ "1.2.3-b"+ )+ Test.~?= Just LT,+ ( compare+ <$> Salve.parseVersion "1.2.3-pre"+ <*> Salve.parseVersion+ "1.2.3"+ )+ Test.~?= Just LT,+ ( compare+ <$> Salve.parseVersion "1.2.4-pre"+ <*> Salve.parseVersion+ "1.2.3"+ )+ Test.~?= Just GT,+ ( compare+ <$> Salve.parseVersion "1.2.3+a"+ <*> Salve.parseVersion+ "1.2.3+b"+ )+ Test.~?= Just EQ,+ ((==) <$> Salve.parseVersion "1.2.3+a" <*> Salve.parseVersion "1.2.3+b")+ Test.~?= Just False,+ (compare <$> Salve.parsePreRelease "1" <*> Salve.parsePreRelease "a")+ Test.~?= Just LT,+ (compare <$> Salve.parsePreRelease "9" <*> Salve.parsePreRelease "10")+ Test.~?= Just LT,+ (compare <$> Salve.parsePreRelease "p10" <*> Salve.parsePreRelease "p9")+ Test.~?= Just LT,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<1.2.3"+ <*> Salve.parseVersion "1.2.3-pre"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<=1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<=1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "<=1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "=1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "=1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "=1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "=1.2.3"+ <*> Salve.parseVersion "1.2.3-pre"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "=1.2.3"+ <*> Salve.parseVersion "1.2.3+build"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">=1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">=1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">=1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3"+ <*> Salve.parseVersion "1.2.4-pre"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3-pre"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3 <1.2.5"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3 <1.2.5"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint ">1.2.3 <1.2.5"+ <*> Salve.parseVersion "1.2.5"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 || 1.2.4"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 || 1.2.4"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 || 1.2.4"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 || 1.2.4"+ <*> Salve.parseVersion "1.2.5"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0"+ <*> Salve.parseVersion "1.3.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 - 1.2.4"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 - 1.2.4"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 - 1.2.4"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.3 - 1.2.4"+ <*> Salve.parseVersion "1.2.5"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "~1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "~1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "~1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "~1.2.3"+ <*> Salve.parseVersion "1.3.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^1.2.3"+ <*> Salve.parseVersion "1.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^1.2.3"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^1.2.3"+ <*> Salve.parseVersion "1.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^1.2.3"+ <*> Salve.parseVersion "1.3.0"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^1.2.3"+ <*> Salve.parseVersion "2.0.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.2.3"+ <*> Salve.parseVersion "0.2.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.2.3"+ <*> Salve.parseVersion "0.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.2.3"+ <*> Salve.parseVersion "0.2.4"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.2.3"+ <*> Salve.parseVersion "0.3.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.0.3"+ <*> Salve.parseVersion "0.0.2"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.0.3"+ <*> Salve.parseVersion "0.0.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "^0.0.3"+ <*> Salve.parseVersion "0.0.4"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.x"+ <*> Salve.parseVersion "1.1.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.x"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.2.x"+ <*> Salve.parseVersion "1.3.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.x.x"+ <*> Salve.parseVersion "0.1.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.x.x"+ <*> Salve.parseVersion "1.0.0"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.x.x"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "1.x.x"+ <*> Salve.parseVersion "2.0.0"+ )+ Test.~?= Just False,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "x.x.x"+ <*> Salve.parseVersion "0.0.0"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "x.x.x"+ <*> Salve.parseVersion "1.2.3"+ )+ Test.~?= Just True,+ ( Salve.satisfiesConstraint+ <$> Salve.parseConstraint "x.x.x"+ <*> Salve.parseVersion "2.0.0"+ )+ Test.~?= Just True+ ]++ let hasErrors = Test.errors counts /= 0+ hasFailures = Test.failures counts /= 0+ Monad.when (hasErrors || hasFailures) Exit.exitFailure
− src/lib/Salve.hs
@@ -1,611 +0,0 @@--- | This module defines types and functions for working with versions as--- defined by [Semantic Versioning](http://semver.org/spec/v2.0.0.html). It--- also provides types and functions for working with version constraints as--- described by [npm](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#ranges).-module Salve (--- | This module doesn't export anything that conflicts with the "Prelude", so--- you can import it unqualified.------ >>> import Salve------ This module provides lenses for modifying versions. If you want to modify--- versions, consider importing a lens library like--- [microlens](https://www.stackage.org/lts-9.3/package/microlens-0.4.8.1).------ The 'Version' data type is the core of this module. Use 'parseVersion' to--- make versions and 'renderVersion' to convert them into strings.------ >>> renderVersion <$> parseVersion "1.2.3"--- Just "1.2.3"------ The 'Constraint' data type allows you to specify version constraints. Use--- 'parseConstraint' to make constraints and 'renderConstraint' to convert them--- into strings.------ >>> renderConstraint <$> parseConstraint ">1.2.0"--- Just ">1.2.0"------ Use 'satisfiesConstraint' to see if a version satisfiesConstraint a--- constraint.------ >>> satisfiesConstraint <$> parseConstraint ">1.2.0" <*> parseVersion "1.2.3"--- Just True---- * Cheat sheet--- | If you're coming from Cabal, you might not be familiar with npm's version--- range syntax. This table shows you how npm version ranges map to Cabal's--- version constraints.------ > Salve | Cabal | Notes--- > ----- | ----- | -------- > <1.2.3 | <1.2.3 | ---- > <=1.2.3 | <=1.2.3 | ---- > =1.2.3 | ==1.2.3 | equals sign is optional--- > >=1.2.3 | >=1.2.3 | ---- > >1.2.3 | >1.2.3 | ---- > 1.2.3 || >1.2.3 | ==1.2.3 || >1.2.3 | lower precedence than and--- > >=1.2.3 <2.0.0 | >=1.2.3 && <2.0.0 | higher precedence than or--- > 1.2.3 - 2.3.4 | >=1.2.3 && <=2.3.4 | inclusive ranges--- > 1.2.x | ==1.2.* | can use X or * instead of x--- > 1.x.x | ==1.* | ---- > x.x.x | ==* | same as -any--- > ~1.2.3 | ^>=1.2.3 | same as >=1.2.3 && <1.3.0--- > ^1.2.3 | >=1.2.3 && <2 | ---- > ^0.2.3 | >=0.2.3 && <0.3 | ---- > ^0.0.3 | >=0.0.3 && <0.0.4 | ----- * Rationale---- ** The PVP--- | Haskell's--- [Package Versioning Policy](https://github.com/haskell/pvp/blob/176bb14/pvp-specification.md)--- (PVP) defines three things:------ 1. A spec for versioning your package, which includes how version numbers--- look and how to encode breaking changes.--- 2. A spec for constraining the versions of your dependencies, which--- incldues how version ranges look.--- 3. A prescription for how to constrain the versions of your dependencies,--- which includes how the ranges of your dependencies should be.------ By comparison, Semantic Versioning only deals with the first thing. npm's--- version ranges only deal with the second thing. This module deals with the--- first and second things but leaves the third up to you.------ Looking at the first point, why might you want to use SemVer instead of the--- PVP? The PVP has many problems, as described by the--- [Problematic versioning policy](http://taylor.fausak.me/2016/12/28/problematic-versioning-policy/)--- blog post. In short, the PVP is too flexible and it's unique to Haskell,--- which causes unnecessary friction with developers from other languages.------ Moving on to the second point, why should we use npm's version ranges--- instead of the PVP's? This is a less clear cut. The two syntaxes are broadly--- compatible. Really the only gains here are compatibility with a widely-used--- syntax and convenient shorthand for common constraints (like hyphens--- @1.2.3 - 2.3.4@, tildes @~1.2.3@, and carets @^1.2.3@).---- ** Other modules--- | There are already a few modules that provide version numbers. Why do we--- need another one? Let's take a look at the options.------ - [Data.Version](https://www.stackage.org/haddock/lts-9.3/base-4.9.1.0/Data-Version.html)--- from the @base@ package:------ - Exposes constructors, which allows creating versions that cannot be--- parsed.--- - Allows any number of components, from zero to inifinity.--- - Deprecated tags on versions.--- - Does not support build metadata on versions.--- - Does not support constraints.------ - [Distribution.Version](https://www.stackage.org/haddock/lts-9.3/Cabal-1.24.2.0/Distribution-Version.html)--- from the @Cabal@ package:------ - Has the same problems as Data.Version because it re-uses that--- version type.--- - Depends on the @array@, @binary@, @bytestring@, @containers@,--- @deepseq@, @directory@, @filepath@, @pretty@, @process@, @time@, and--- @unix@ packages.------ - [Data.SemVer](https://www.stackage.org/haddock/lts-9.3/semver-0.3.3.1/Data-SemVer.html)--- from the @semver@ package:------ - Depends on the @attoparsec@, @deepseq@, and @text@ packages.--- - Does not support version constraints.------ - [Data.SemVer](https://hackage.haskell.org/package/semver-range-0.2.2/docs/Data-SemVer.html)--- from the @semver-range@ package:------ - Depends on the @classy-prelude@, @parsec@, @text@, and--- @unordered-containers@ packages.--- - Module name collides with the @semver@ package.--- - Supports constraints, but does not provide a way to render them.------ - [Data.Versions](https://www.stackage.org/haddock/lts-9.3/versions-3.1.1/Data-Versions.html)--- from the @versions@ package:------ - Depends on the @deepseq@, @hashable@, @megaparsec@, and @text@--- packages.--- - Intentially allows weird versions.--- - Does not support constraints.------ By comparison, this module:------ - Does not expose constructors. Any version you create can be rendered and--- parsed without issue.--- - Requires exactly three components. You won't have to wonder if version--- @1.2.0@ is greater than @1.2@.--- - Allows pre-release identifiers on versions. Go ahead and release version--- @1.0.0-alpha@ for early adopters.--- - Allows build metadata on versions. Show when a release was made with--- versions like @1.0.0+2001-02-03@.--- - Supports version constraints. Just like versions, rendering and parsing--- constraints is no problem.--- - Only depends on the @base@ package. You can use all the functionality--- without installing any other packages.--- - Has a unique module name. You won't have to use the @PackageImports@--- extension simply to deal with version numbers.---- * Types-Salve.Version,-Salve.PreRelease,-Salve.Build,-Salve.Constraint,---- * Constructors-Salve.makeVersion,-Salve.initialVersion,---- * Parsing-Salve.parseVersion,-Salve.parsePreRelease,-Salve.parseBuild,-Salve.parseConstraint,---- ** Unsafe--- | These functions can be used to unsafely parse strings. Instead of--- returning 'Nothing', they raise an exception. Only use these if you are sure--- the string can be successfully parsed!-Salve.unsafeParseVersion,-Salve.unsafeParsePreRelease,-Salve.unsafeParseBuild,-Salve.unsafeParseConstraint,---- * Rendering-Salve.renderVersion,-Salve.renderPreRelease,-Salve.renderBuild,-Salve.renderConstraint,---- * Predicates-Salve.isUnstable,-Salve.isStable,---- * Conversions-Salve.fromBaseVersion,-Salve.toBaseVersion,---- * Helpers-Salve.bumpMajor,-Salve.bumpMinor,-Salve.bumpPatch,-Salve.satisfiesConstraint,---- * Lenses--- | These lenses can be used to access and modify specific parts of a--- 'Version'.------ Don't be scared by these type signatures. They are provided in full to avoid--- the @RankNTypes@ language extension. The type signature--- @'Functor' f => (a -> f a) -> 'Version' -> f 'Version'@ is the same as--- @Lens' 'Version' a@, which you may already be familiar with.-Salve.majorLens,-Salve.minorLens,-Salve.patchLens,-Salve.preReleasesLens,-Salve.buildsLens,---- * Examples--- | These examples are provided to showcase functionality and explain weird--- behavior. If something isn't clear, please open a pull request adding an--- example!---- ** Versions--- | Leading zeros are not allowed.------ >>> parseVersion "01.0.0"--- Nothing--- >>> parseVersion "0.01.0"--- Nothing--- >>> parseVersion "0.0.01"--- Nothing------ Negative numbers are not allowed.------ >>> parseVersion "-1.0.0"--- Nothing--- >>> parseVersion "0.-1.0"--- Nothing--- >>> parseVersion "0.0.-1"--- Nothing------ Non-digits are not allowed.------ >>> parseVersion "a.0.0"--- Nothing--- >>> parseVersion "0.a.0"--- Nothing--- >>> parseVersion "0.0.a"--- Nothing------ Partial version numbers are not allowed.------ >>> parseVersion "0.0"--- Nothing------ Extra version numbers are not allowed.------ >>> parseVersion "0.0.0.0"--- Nothing------ Spaces are allowed------ >>> parseVersion " 0.0.0"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})--- >>> parseVersion "0.0.0 "--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ Interior spaces are not allowed.------ >>> parseVersion "0 .0.0"--- Nothing--- >>> parseVersion "0. 0.0"--- Nothing------ Each version component cannot be larger than a 64-bit unsigned integer.------ >>> parseVersion "18446744073709551615.0.0"--- Just (Version {versionMajor = 18446744073709551615, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})--- >>> parseVersion "18446744073709551616.0.0"--- Nothing------ Numeric pre-releases tags cannot be larger than a 64-bit unsigned integer.------ >>> parseVersion "0.0.0-18446744073709551615"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [PreReleaseNumeric 18446744073709551615], versionBuilds = []})--- >>> parseVersion "0.0.0-18446744073709551616"--- Nothing------ Build metadata is not numeric so it does not have any limit.------ >>> parseVersion "0.0.0+18446744073709551615"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551615"]})--- >>> parseVersion "0.0.0+18446744073709551616"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = [Build "18446744073709551616"]})---- ** Constraints--- | Partial version numbers are not allowed.------ >>> parseConstraint "1.2"--- Nothing------ Wildcards (also known as "x-ranges") are allowed. The exact character used--- for the wildcard is not round-tripped.------ >>> renderConstraint <$> parseConstraint "1.2.x"--- Just "1.2.x"--- >>> renderConstraint <$> parseConstraint "1.2.X"--- Just "1.2.x"--- >>> renderConstraint <$> parseConstraint "1.2.*"--- Just "1.2.x"------ An optional equals sign can be included with wildcard constraints.------ >>> renderConstraint <$> parseConstraint "=1.2.x"--- Just "1.2.x"------ Wildcards can be combined with other constraints.------ >>> renderConstraint <$> parseConstraint "1.2.x 2.3.4"--- Just "1.2.x 2.3.4"--- >>> renderConstraint <$> parseConstraint "1.2.x || 2.3.4"--- Just "1.2.x || 2.3.4"------ Wildcards are allowed at any position.------ >>> renderConstraint <$> parseConstraint "1.2.x"--- Just "1.2.x"--- >>> renderConstraint <$> parseConstraint "1.x.x"--- Just "1.x.x"--- >>> renderConstraint <$> parseConstraint "x.x.x"--- Just "x.x.x"------ Non-wildcards cannot come after wildcards.------ >>> parseConstraint "1.x.3"--- Nothing--- >>> parseConstraint "x.2.3"--- Nothing--- >>> parseConstraint "x.x.3"--- Nothing--- >>> parseConstraint "x.2.x"--- Nothing------ Wildcards cannot be used with other operators.------ >>> parseConstraint "<1.2.x"--- Nothing--- >>> parseConstraint "<=1.2.x"--- Nothing--- >>> parseConstraint ">=1.2.x"--- Nothing--- >>> parseConstraint ">1.2.x"--- Nothing--- >>> parseConstraint "~1.2.x"--- Nothing--- >>> parseConstraint "^1.2.x"--- Nothing--- >>> parseConstraint "1.2.x - 2.3.4"--- Nothing--- >>> parseConstraint "1.2.3 - 2.3.x"--- Nothing------ Spaces are allowed in most places. Extra spaces are not round-tripped.------ >>> renderConstraint <$> parseConstraint " 1.2.3 "--- Just "1.2.3"--- >>> renderConstraint <$> parseConstraint "> 1.2.3"--- Just ">1.2.3"--- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"--- Just "1.2.3 - 2.3.4"--- >>> renderConstraint <$> parseConstraint "1.2.3 2.3.4"--- Just "1.2.3 2.3.4"--- >>> renderConstraint <$> parseConstraint "1.2.3 || 2.3.4"--- Just "1.2.3 || 2.3.4"------ Parentheses are not allowed. Note that combining two constraints with a--- space (and) has higher precedence than combining them with pipes (or). In--- other words, @"a b || c"@ parses as @"(a b) || c"@, not @"a (b || c)"@.------ >>> parseConstraint "(1.2.3)"--- Nothing--- >>> parseConstraint "(1.2.3 || >1.2.3) <1.3.0"--- Nothing--- >>> parseConstraint "(>1.2.3 <1.3.0) || 1.2.3"--- Nothing------ Most constraints can be round-tripped through parsing and rendering.------ >>> renderConstraint <$> parseConstraint "<1.2.3"--- Just "<1.2.3"--- >>> renderConstraint <$> parseConstraint "<=1.2.3"--- Just "<=1.2.3"--- >>> renderConstraint <$> parseConstraint "1.2.3"--- Just "1.2.3"--- >>> renderConstraint <$> parseConstraint ">=1.2.3"--- Just ">=1.2.3"--- >>> renderConstraint <$> parseConstraint ">1.2.3"--- Just ">1.2.3"--- >>> renderConstraint <$> parseConstraint "1.2.3 - 2.3.4"--- Just "1.2.3 - 2.3.4"--- >>> renderConstraint <$> parseConstraint "~1.2.3"--- Just "~1.2.3"--- >>> renderConstraint <$> parseConstraint "^1.2.3"--- Just "^1.2.3"--- >>> renderConstraint <$> parseConstraint ">1.2.3 <2.0.0"--- Just ">1.2.3 <2.0.0"--- >>> renderConstraint <$> parseConstraint "1.2.3 || >1.2.3"--- Just "1.2.3 || >1.2.3"------ Explicit equal signs do not get round-tripped.------ >>> renderConstraint <$> parseConstraint "=1.2.3"--- Just "1.2.3"------ Pre-releases and builds are allowed on any constraints except wildcards.------ >>> renderConstraint <$> parseConstraint "1.2.3-p+b"--- Just "1.2.3-p+b"--- >>> renderConstraint <$> parseConstraint ">1.2.3-p+b"--- Just ">1.2.3-p+b"--- >>> renderConstraint <$> parseConstraint "1.2.3-p+b - 2.3.4-p+b"--- Just "1.2.3-p+b - 2.3.4-p+b"--- >>> renderConstraint <$> parseConstraint "~1.2.3-p+b"--- Just "~1.2.3-p+b"--- >>> renderConstraint <$> parseConstraint "^1.2.3-p+b"--- Just "^1.2.3-p+b"------ >>> parseConstraint "1.2.x-p+b"--- Nothing------ These examples show every type of constraint in a single expression.------ >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 =1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9 1.2.x"--- Just "<1.2.0 <=1.2.1 1.2.2 >=1.2.3 >1.2.4 1.2.5 1.2.6 - 1.2.7 ~1.2.8 ^1.2.9 1.2.x"--- >>> renderConstraint <$> parseConstraint "<1.2.0 <=1.2.1 || =1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9 1.2.x"--- Just "<1.2.0 <=1.2.1 || 1.2.2 >=1.2.3 || >1.2.4 1.2.5 || 1.2.6 - 1.2.7 ~1.2.8 || ^1.2.9 1.2.x"--- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 =1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9 || 1.2.x"--- Just "<1.2.0 || <=1.2.1 1.2.2 || >=1.2.3 >1.2.4 || 1.2.5 1.2.6 - 1.2.7 || ~1.2.8 ^1.2.9 || 1.2.x"--- >>> renderConstraint <$> parseConstraint "<1.2.0 || <=1.2.1 || =1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9 || 1.2.x"--- Just "<1.2.0 || <=1.2.1 || 1.2.2 || >=1.2.3 || >1.2.4 || 1.2.5 || 1.2.6 - 1.2.7 || ~1.2.8 || ^1.2.9 || 1.2.x"---- ** Satisfying constraints--- | Although in general you should use 'satisfiesConstraint', 'parseVersion',--- and 'parseConstraint', doing that here makes it hard to tell what the--- examples are doing. An operator makes things clearer.------ >>> satisfiesConstraint <$> parseConstraint "=1.2.3" <*> parseVersion "1.2.3"--- Just True--- >>> let version ? constraint = satisfiesConstraint (unsafeParseConstraint constraint) (unsafeParseVersion version)--- >>> "1.2.3" ? "=1.2.3"--- True------ - Less than:------ >>> "1.2.2" ? "<1.2.3"--- True--- >>> "1.2.3" ? "<1.2.3"--- False--- >>> "1.2.4" ? "<1.2.3"--- False--- >>> "1.2.3-pre" ? "<1.2.3"--- True------ - Less than or equal to:------ >>> "1.2.2" ? "<=1.2.3"--- True--- >>> "1.2.3" ? "<=1.2.3"--- True--- >>> "1.2.4" ? "<=1.2.3"--- False------ - Equal to:------ >>> "1.2.2" ? "=1.2.3"--- False--- >>> "1.2.3" ? "=1.2.3"--- True--- >>> "1.2.4" ? "=1.2.3"--- False--- >>> "1.2.3-pre" ? "=1.2.3"--- False--- >>> "1.2.3+build" ? "=1.2.3"--- True------ - Greater than or equal to:------ >>> "1.2.2" ? ">=1.2.3"--- False--- >>> "1.2.3" ? ">=1.2.3"--- True--- >>> "1.2.4" ? ">=1.2.3"--- True------ - Greater than:------ >>> "1.2.2" ? ">1.2.3"--- False--- >>> "1.2.3" ? ">1.2.3"--- False--- >>> "1.2.4" ? ">1.2.3"--- True--- >>> "1.2.4-pre" ? ">1.2.3"--- True------ >>> "1.2.4" ? ">1.2.3-pre"--- True------ - And:------ >>> "1.2.3" ? ">1.2.3 <1.2.5"--- False--- >>> "1.2.4" ? ">1.2.3 <1.2.5"--- True--- >>> "1.2.5" ? ">1.2.3 <1.2.5"--- False------ - Or:------ >>> "1.2.2" ? "1.2.3 || 1.2.4"--- False--- >>> "1.2.3" ? "1.2.3 || 1.2.4"--- True--- >>> "1.2.4" ? "1.2.3 || 1.2.4"--- True--- >>> "1.2.5" ? "1.2.3 || 1.2.4"--- False------ - And & or:------ >>> "1.2.2" ? "1.2.2 || >1.2.3 <1.3.0"--- True--- >>> "1.2.3" ? "1.2.2 || >1.2.3 <1.3.0"--- False--- >>> "1.2.4" ? "1.2.2 || >1.2.3 <1.3.0"--- True--- >>> "1.3.0" ? "1.2.2 || >1.2.3 <1.3.0"--- False------ - Hyphen:------ >>> "1.2.2" ? "1.2.3 - 1.2.4"--- False--- >>> "1.2.3" ? "1.2.3 - 1.2.4"--- True--- >>> "1.2.4" ? "1.2.3 - 1.2.4"--- True--- >>> "1.2.5" ? "1.2.3 - 1.2.4"--- False------ - Tilde:------ >>> "1.2.2" ? "~1.2.3"--- False--- >>> "1.2.3" ? "~1.2.3"--- True--- >>> "1.2.4" ? "~1.2.3"--- True--- >>> "1.3.0" ? "~1.2.3"--- False------ - Caret:------ >>> "1.2.2" ? "^1.2.3"--- False--- >>> "1.2.3" ? "^1.2.3"--- True--- >>> "1.2.4" ? "^1.2.3"--- True--- >>> "1.3.0" ? "^1.2.3"--- True--- >>> "2.0.0" ? "^1.2.3"--- False------ >>> "0.2.2" ? "^0.2.3"--- False--- >>> "0.2.3" ? "^0.2.3"--- True--- >>> "0.2.4" ? "^0.2.3"--- True--- >>> "0.3.0" ? "^0.2.3"--- False------ >>> "0.0.2" ? "^0.0.3"--- False--- >>> "0.0.3" ? "^0.0.3"--- True--- >>> "0.0.4" ? "^0.0.3"--- False------ - Wildcard:------ >>> "1.1.0" ? "1.2.x"--- False--- >>> "1.2.3" ? "1.2.x"--- True--- >>> "1.3.0" ? "1.2.x"--- False------ >>> "0.1.0" ? "1.x.x"--- False--- >>> "1.0.0" ? "1.x.x"--- True--- >>> "1.2.3" ? "1.x.x"--- True--- >>> "2.0.0" ? "1.x.x"--- False------ >>> "0.0.0" ? "x.x.x"--- True--- >>> "1.2.3" ? "x.x.x"--- True--- >>> "2.0.0" ? "x.x.x"--- True-) where--import qualified Salve.Internal as Salve---- $setup--- >>> import Control.Applicative--- >>> import Salve
− src/lib/Salve/Internal.hs
@@ -1,1006 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}---- | WARNING: This module should be considered private! If you find yourself--- wanting to import something from this module, please open an issue to get--- that thing exported from "Salve".-module Salve.Internal- ( Version(..)- , PreRelease(..)- , Build(..)- , Constraint(..)- , makeVersion- , initialVersion- , parseVersion- , parsePreRelease- , parseBuild- , parseConstraint- , unsafeParseVersion- , unsafeParsePreRelease- , unsafeParseBuild- , unsafeParseConstraint- , renderVersion- , renderPreRelease- , renderBuild- , renderConstraint- , isUnstable- , isStable- , fromBaseVersion- , toBaseVersion- , bumpMajor- , bumpMinor- , bumpPatch- , satisfiesConstraint- , majorLens- , minorLens- , patchLens- , preReleasesLens- , buildsLens- , Operator(..)- , Wildcard(..)- , constraintLT- , constraintLE- , constraintEQ- , constraintGE- , constraintGT- , constraintAnd- , constraintOr- , constraintHyphen- , constraintTilde- , constraintCaret- ) where--import qualified Control.Monad as Monad-import qualified Data.Char as Char-import qualified Data.Data as Data-import qualified Data.List as List-import qualified Data.Maybe as Maybe-import qualified Data.Monoid as Monoid-import qualified Data.Ord as Ord-import qualified Data.Version as Version-import qualified Data.Word as Word-import qualified GHC.Generics as Generics-import qualified Text.ParserCombinators.ReadP as ReadP---- $setup------ >>> import Control.Applicative (Const(..))--- >>> let view lens record = getConst (lens Const record)------ >>> import Data.Functor.Identity (Identity(..))--- >>> let set lens field record = runIdentity (lens (const (Identity field)) record)------ >>> import Control.Applicative ((<$>), (<*>))---- * Public---- | A semantic version number. Versions have five parts:------ 1. 'majorLens': The major version number.--- 2. 'minorLens': The minor version number.--- 3. 'patchLens': The patch version number.--- 4. 'preReleasesLens': A list of pre-release identifiers.--- 5. 'buildsLens': A list of build metadata.------ Use 'parseVersion' to create versions.-data Version = Version- { versionMajor :: Word.Word64- , versionMinor :: Word.Word64- , versionPatch :: Word.Word64- , versionPreReleases :: [PreRelease]- , versionBuilds :: [Build]- } deriving (Data.Data, Eq, Generics.Generic, Read, Show)---- | In general, versions compare in the way that you would expect. First the--- major version numbers are compared, then the minors, then the patches.------ >>> compare <$> parseVersion "1.2.3" <*> parseVersion "2.0.0"--- Just LT--- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "1.3.0"--- Just LT--- >>> compare <$> parseVersion "1.2.3" <*> parseVersion "1.2.4"--- Just LT------ Numbers are compared numerically, not alphabetically.------ >>> compare <$> parseVersion "0.0.9" <*> parseVersion "0.0.10"--- Just LT------ If all the numbers are the same, the pre-releases are compared.------ >>> compare <$> parseVersion "1.2.3-a" <*> parseVersion "1.2.3-b"--- Just LT------ A version with a pre-release is always less than a version without one as--- long as the other parts are the same.------ >>> compare <$> parseVersion "1.2.3-pre" <*> parseVersion "1.2.3"--- Just LT--- >>> compare <$> parseVersion "1.2.4-pre" <*> parseVersion "1.2.3"--- Just GT------ Builds are not considered when comparing versions.------ >>> compare <$> parseVersion "1.2.3+a" <*> parseVersion "1.2.3+b"--- Just EQ--- >>> (==) <$> parseVersion "1.2.3+a" <*> parseVersion "1.2.3+b"--- Just False-instance Ord Version where- compare x y = Monoid.mconcat- [ Ord.comparing versionMajor x y- , Ord.comparing versionMinor x y- , Ord.comparing versionPatch x y- , case both versionPreReleases (x, y) of- ([], []) -> EQ- ([], _) -> GT- (_, []) -> LT- (p, q) -> compare p q- ]---- | Pre-release information attached to a version. These can either be numeric--- or textual. They must not be empty.------ - Numeric: Can be any non-negative integer. Cannot have leading zeros.------ - Textual: Can be any string of ASCII digits, letters, or hyphens. Cannot be--- all digits, as that would be numeric.------ In general, pre-releases must match the regular expression--- @\/^[-0-9A-Za-z]+$\/@.------ Use 'parsePreRelease' to create pre-releases.-data PreRelease- = PreReleaseNumeric Word.Word64- | PreReleaseTextual String- deriving (Data.Data, Eq, Generics.Generic, Read, Show)---- | Numeric pre-releases are always less than textual pre-releases.------ >>> compare <$> parsePreRelease "1" <*> parsePreRelease "a"--- Just LT------ Numeric pre-releases are compared numerically.------ >>> compare <$> parsePreRelease "9" <*> parsePreRelease "10"--- Just LT------ Textual pre-releases are compared alphabetically.------ >>> compare <$> parsePreRelease "p10" <*> parsePreRelease "p9"--- Just LT-instance Ord PreRelease where- compare x y = case (x, y) of- (PreReleaseNumeric n, PreReleaseNumeric m) -> compare n m- (PreReleaseNumeric _, PreReleaseTextual _) -> LT- (PreReleaseTextual _, PreReleaseNumeric _) -> GT- (PreReleaseTextual s, PreReleaseTextual t) -> compare s t---- | Build metadata attached to a version. These are similar to--- 'PreRelease's with some key differences:------ 1. There is no such thing as numeric builds. Even though builds can look--- like numbers, all builds are textual.--- 2. As a result, builds that look numeric are allowed to have leading zeros.--- 3. Builds cannot be compared. That is, they do not have an 'Ord' instance.------ Use 'parseBuild' to create builds.-newtype Build = Build String- deriving (Data.Data, Eq, Generics.Generic, Read, Show)---- | Constrains allowable version numbers.------ Use 'parseConstraint' to create constraints and 'satisfiesConstraint' to see--- if a version number satisfies a constraint.-data Constraint- = ConstraintOperator Operator Version- | ConstraintHyphen Version Version- | ConstraintWildcard Wildcard- | ConstraintAnd Constraint Constraint- | ConstraintOr Constraint Constraint- deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)---- | Makes a new version number.------ >>> makeVersion 1 2 3 [unsafeParsePreRelease "pre"] [unsafeParseBuild "build"]--- Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "pre"], versionBuilds = [Build "build"]}------ This can be a useful alternative to 'parseVersion' if you want a total way--- to create a version.-makeVersion :: Word.Word64 -> Word.Word64 -> Word.Word64 -> [PreRelease] -> [Build] -> Version-makeVersion major minor patch preReleases builds = Version- { versionMajor = major- , versionMinor = minor- , versionPatch = patch- , versionPreReleases = preReleases- , versionBuilds = builds- }---- | The initial version number for development.------ >>> initialVersion--- Version {versionMajor = 0, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []}-initialVersion :: Version-initialVersion = makeVersion 0 0 0 [] []---- | Attempts to parse a version. This parser follows--- [SemVer's BNF](https://github.com/mojombo/semver/blob/eb9aac5/semver.md#backusnaur-form-grammar-for-valid-semver-versions).------ >>> parseVersion "1.2.3-p.4+b.5"--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "p",PreReleaseNumeric 4], versionBuilds = [Build "b",Build "5"]})------ Returns 'Nothing' if the parse fails.------ >>> parseVersion "wrong"--- Nothing------ Whitespace is allowed.------ >>> parseVersion " 1.2.3 "--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})-parseVersion :: String -> Maybe Version-parseVersion s = parse- (do- ReadP.skipSpaces- version <- versionP- ReadP.skipSpaces- return version)- s---- | Attempts to parse a pre-release.------ >>> parsePreRelease "pre"--- Just (PreReleaseTextual "pre")--- >>> parsePreRelease "1"--- Just (PreReleaseNumeric 1)------ Returns 'Nothing' if the parse fails.------ >>> parsePreRelease "wrong!"--- Nothing------ Numeric pre-releases cannot contain leading zeros.------ >>> parsePreRelease "01"--- Nothing-parsePreRelease :: String -> Maybe PreRelease-parsePreRelease s = parse preReleaseP s---- | Attempts to parse a build.------ >>> parseBuild "build"--- Just (Build "build")--- >>> parseBuild "1"--- Just (Build "1")------ Returns 'Nothing' if the parse fails.------ >>> parseBuild "wrong!"--- Nothing------ Unlike pre-releases, numeric builds can have leading zeros.------ >>> parseBuild "01"--- Just (Build "01")-parseBuild :: String -> Maybe Build-parseBuild s = parse buildP s---- | Attempts to parse a constraint. This parser mostly follows--- [npm's BNF](https://github.com/npm/npm/blob/d081cc6/doc/misc/semver.md#range-grammar).------ >>> parseConstraint ">1.2.3"--- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))------ Returns 'Nothing' if the parse fails.------ >>> parseConstraint "wrong"--- Nothing------ The two departures from npm's BNF are that x-ranges cannot be used with--- other operators and partial version numbers are not allowed.------ >>> parseConstraint "1.2.x"--- Just (ConstraintWildcard (WildcardPatch 1 2))--- >>> parseConstraint ">=1.2.x"--- Nothing------ >>> parseConstraint "1.2"--- Nothing--- >>> parseConstraint ">=1.2"--- Nothing-parseConstraint :: String -> Maybe Constraint-parseConstraint s = parse constraintsP s---- | Parses a version.------ >>> unsafeParseVersion "1.2.3-p.4+b.5"--- Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [PreReleaseTextual "p",PreReleaseNumeric 4], versionBuilds = [Build "b",Build "5"]}------ Raises an exception if the parse fails.------ >>> unsafeParseVersion "wrong"--- *** Exception: unsafeParseVersion: invalid version: "wrong"--- ...------ See 'parseVersion' for a safe version of this function.-unsafeParseVersion :: String -> Version-unsafeParseVersion s = case parseVersion s of- Nothing -> error ("unsafeParseVersion: invalid version: " ++ show s)- Just v -> v---- | Parses a pre-release.------ >>> unsafeParsePreRelease "pre"--- PreReleaseTextual "pre"------ Raises an exception if the parse fails.------ >>> unsafeParsePreRelease "wrong!"--- *** Exception: unsafeParsePreRelease: invalid pre-release: "wrong!"--- ...------ See 'parsePreRelease' for a safe version of this function.-unsafeParsePreRelease :: String -> PreRelease-unsafeParsePreRelease s = case parsePreRelease s of- Nothing -> error ("unsafeParsePreRelease: invalid pre-release: " ++ show s)- Just p -> p---- | Parses a build.------ >>> unsafeParseBuild "build"--- Build "build"------ Raises an exception if the parse fails.------ >>> unsafeParseBuild "wrong!"--- Build "*** Exception: unsafeParseBuild: invalid build: "wrong!"--- ...------ See 'parseBuild' for a safe version of this function.-unsafeParseBuild :: String -> Build-unsafeParseBuild s = case parseBuild s of- Nothing -> error ("unsafeParseBuild: invalid build: " ++ show s)- Just b -> b---- | Parses a constraint.------ >>> unsafeParseConstraint ">1.2.3"--- ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})------ Raises an exception if the parse fails.------ >>> unsafeParseConstraint "wrong"--- *** Exception: unsafeParseConstraint: invalid constraint: "wrong"--- ...------ See 'parseConstraint' for a safe version of this function.-unsafeParseConstraint :: String -> Constraint-unsafeParseConstraint s = case parseConstraint s of- Nothing -> error ("unsafeParseConstraint: invalid constraint: " ++ show s)- Just c -> c---- | Renders a version.------ >>> renderVersion <$> parseVersion "1.2.3-p.4+b.5"--- Just "1.2.3-p.4+b.5"-renderVersion :: Version -> String-renderVersion v = concat- [ show (versionMajor v)- , "."- , show (versionMinor v)- , "."- , show (versionPatch v)- , renderPreReleases (versionPreReleases v)- , renderBuilds (versionBuilds v)- ]---- | Renders a pre-release.------ >>> renderPreRelease <$> parsePreRelease "pre"--- Just "pre"--- >>> renderPreRelease <$> parsePreRelease "1"--- Just "1"-renderPreRelease :: PreRelease -> String-renderPreRelease p = case p of- PreReleaseNumeric n -> show n- PreReleaseTextual s -> s---- | Renders a build.------ >>> renderBuild <$> parseBuild "build"--- Just "build"--- >>> renderBuild <$> parseBuild "1"--- Just "1"-renderBuild :: Build -> String-renderBuild (Build b) = b---- | Renders a constraint.------ >>> renderConstraint <$> parseConstraint ">1.2.3"--- Just ">1.2.3"------ Parsing and rendering a constraint doesn't always return what you started--- with.------ >>> renderConstraint <$> parseConstraint "=1.2.3"--- Just "1.2.3"-renderConstraint :: Constraint -> String-renderConstraint c = case c of- ConstraintOperator o v ->- let s = renderVersion v- in case o of- OperatorLT -> '<' : s- OperatorLE -> '<' : '=' : s- OperatorEQ -> s- OperatorGE -> '>' : '=' : s- OperatorGT -> '>' : s- OperatorTilde -> '~' : s- OperatorCaret -> '^' : s- ConstraintHyphen l r -> unwords [renderVersion l, "-", renderVersion r]- ConstraintWildcard w -> case w of- WildcardMajor -> "x.x.x"- WildcardMinor m -> show m ++ ".x.x"- WildcardPatch m n -> List.intercalate "." [show m, show n, "x"]- ConstraintAnd l r -> unwords (map renderConstraint [l, r])- ConstraintOr l r -> unwords [renderConstraint l, "||", renderConstraint r]---- | Returns 'True' if the major version number is zero, 'False' otherwise.------ >>> isUnstable <$> parseVersion "0.1.2"--- Just True--- >>> isUnstable <$> parseVersion "1.0.0"--- Just False-isUnstable :: Version -> Bool-isUnstable v = versionMajor v == 0---- | Returns 'True' if the major version number is not zero, 'False' otherwise.------ >>> isStable <$> parseVersion "1.0.0"--- Just True--- >>> isStable <$> parseVersion "0.1.2"--- Just False-isStable :: Version -> Bool-isStable v = not (isUnstable v)---- | Converts from a 'Version.Version' from the @base@ package.------ >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2, 3]--- "1.2.3"------ Missing version components are set to zero.------ >>> renderVersion . fromBaseVersion $ Version.makeVersion []--- "0.0.0"--- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1]--- "1.0.0"--- >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2]--- "1.2.0"------ Extra version components are ignored.------ >>> renderVersion . fromBaseVersion $ Version.makeVersion [1, 2, 3, 4]--- "1.2.3"------ Tags are ignored.------ >>> renderVersion . fromBaseVersion $ Version.Version [] ["ignored"]--- "0.0.0"-fromBaseVersion :: Version.Version -> Version-fromBaseVersion v = case Version.versionBranch v of- (m : n : p : _) -> mkV (fromIntegral m) (fromIntegral n) (fromIntegral p)- (m : n : _) -> mkV (fromIntegral m) (fromIntegral n) 0- (m : _) -> mkV (fromIntegral m) 0 0- _ -> mkV 0 0 0---- | Converts to a 'Version.Version' from the @base@ package.------ >>> toBaseVersion <$> parseVersion "1.2.3"--- Just (Version {versionBranch = [1,2,3], versionTags = []})------ Pre-releases and builds are converted to tags.------ >>> toBaseVersion <$> parseVersion "1.2.3-pre+build"--- Just (Version {versionBranch = [1,2,3], versionTags = ["pre","build"]})-toBaseVersion :: Version -> Version.Version-toBaseVersion v = Version.Version- (map fromIntegral- [ versionMajor v- , versionMinor v- , versionPatch v- ])- (concat- [ map renderPreRelease (versionPreReleases v)- , map renderBuild (versionBuilds v)- ])---- | Increments the major version number.------ >>> bumpMajor <$> parseVersion "0.0.0"--- Just (Version {versionMajor = 1, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ The minor and patch numbers are reset to zero.------ >>> bumpMajor <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ The pre-releases and builds are removed.------ >>> bumpMajor <$> parseVersion "0.0.0-pre+build"--- Just (Version {versionMajor = 1, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ Consider using 'majorLens' if you want to arbitrarily change the major--- number, or if you don't want the other parts of the version to change.-bumpMajor :: Version -> Version-bumpMajor v = makeVersion (versionMajor v + 1) 0 0 [] []---- | Increments the minor version number.------ >>> bumpMinor <$> parseVersion "0.0.0"--- Just (Version {versionMajor = 0, versionMinor = 1, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ The patch number is reset to zero.------ >>> bumpMinor <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 1, versionMinor = 3, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ The pre-releases and builds are removed.------ >>> bumpMinor <$> parseVersion "0.0.0-pre+build"--- Just (Version {versionMajor = 0, versionMinor = 1, versionPatch = 0, versionPreReleases = [], versionBuilds = []})------ Consider using 'minorLens' if you want to arbitrarily change the minor--- number, or if you don't want the other parts of the version to change.-bumpMinor :: Version -> Version-bumpMinor v = makeVersion (versionMajor v) (versionMinor v + 1) 0 [] []---- | Increments the patch number.------ >>> bumpPatch <$> parseVersion "0.0.0"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 1, versionPreReleases = [], versionBuilds = []})------ The major and minor numbers are not changed.------ >>> bumpPatch <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 4, versionPreReleases = [], versionBuilds = []})------ The pre-releases and builds are removed.------ >>> bumpPatch <$> parseVersion "0.0.0-pre+build"--- Just (Version {versionMajor = 0, versionMinor = 0, versionPatch = 1, versionPreReleases = [], versionBuilds = []})------ Consider using 'patchLens' if you want to arbitrarily change the patch--- number, or if you don't want the other parts of the version to change.-bumpPatch :: Version -> Version-bumpPatch v = makeVersion- (versionMajor v) (versionMinor v) (versionPatch v + 1) [] []---- | Returns 'True' if the version satisfies the constraint, 'False' otherwise.------ >>> satisfiesConstraint <$> parseConstraint ">1.2.0" <*> parseVersion "1.2.3"--- Just True-satisfiesConstraint :: Constraint -> Version -> Bool-satisfiesConstraint c v = satisfiesSC (toSC c) v---- | Focuses on the major version number.------ >>> view majorLens <$> parseVersion "1.2.3-pre.4+build.5"--- Just 1--- >>> set majorLens 4 <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 4, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})-majorLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version-majorLens f v = fmap- (\ m -> v { versionMajor = m })- (f (versionMajor v))---- | Focuses on the minor version number.------ >>> view minorLens <$> parseVersion "1.2.3-pre.4+build.5"--- Just 2--- >>> set minorLens 4 <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 1, versionMinor = 4, versionPatch = 3, versionPreReleases = [], versionBuilds = []})-minorLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version-minorLens f v = fmap- (\ n -> v { versionMinor = n })- (f (versionMinor v))---- | Focuses on the patch version number.------ >>> view patchLens <$> parseVersion "1.2.3-pre.4+build.5"--- Just 3--- >>> set patchLens 4 <$> parseVersion "1.2.3"--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 4, versionPreReleases = [], versionBuilds = []})-patchLens :: Functor f => (Word.Word64 -> f Word.Word64) -> Version -> f Version-patchLens f v = fmap- (\ p -> v { versionPatch = p })- (f (versionPatch v))---- | Focuses on the pre-release identifiers.------ >>> view preReleasesLens <$> parseVersion "1.2.3-pre.4+build.5"--- Just [PreReleaseTextual "pre",PreReleaseNumeric 4]--- >>> set preReleasesLens [] <$> parseVersion "1.2.3-pre"--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})-preReleasesLens :: Functor f => ([PreRelease] -> f [PreRelease]) -> Version -> f Version-preReleasesLens f v = fmap- (\ ps -> v { versionPreReleases = ps })- (f (versionPreReleases v))---- | Focuses on the build metadata.------ >>> view buildsLens <$> parseVersion "1.2.3-pre.4+build.5"--- Just [Build "build",Build "5"]--- >>> set buildsLens [] <$> parseVersion "1.2.3+build"--- Just (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})-buildsLens :: Functor f => ([Build] -> f [Build]) -> Version -> f Version-buildsLens f v = fmap- (\ bs -> v { versionBuilds = bs })- (f (versionBuilds v))---- * Private---- ** Types--data Operator- = OperatorLT- | OperatorLE- | OperatorEQ- | OperatorGE- | OperatorGT- | OperatorTilde- | OperatorCaret- deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)--data Wildcard- = WildcardMajor- | WildcardMinor Word.Word64- | WildcardPatch Word.Word64 Word.Word64- deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)---- ** Constructors---- | Makes a new constraint that must be less than the version number.------ >>> constraintLT <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "<1.2.3"--- Just (ConstraintOperator OperatorLT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintLT :: Version -> Constraint-constraintLT v = ConstraintOperator OperatorLT v---- | Makes a new constraint that must be less than or euqal to the version--- number.------ >>> constraintLE <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "<=1.2.3"--- Just (ConstraintOperator OperatorLE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintLE :: Version -> Constraint-constraintLE v = ConstraintOperator OperatorLE v---- | Makes a new constraint that must be equal to the version number.------ >>> constraintEQ <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "=1.2.3"--- Just (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintEQ :: Version -> Constraint-constraintEQ v = ConstraintOperator OperatorEQ v---- | Makes a new constraint that must be greater than or equal to the version--- number.------ >>> constraintGE <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint ">=1.2.3"--- Just (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintGE :: Version -> Constraint-constraintGE v = ConstraintOperator OperatorGE v---- | Makes a new constraint that must be greater than the version number.------ >>> constraintGT <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint ">1.2.3"--- Just (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintGT :: Version -> Constraint-constraintGT v = ConstraintOperator OperatorGT v---- | Makes a new constraint that must satisfy both constraints.------ >>> constraintAnd <$> (constraintGE <$> parseVersion "1.2.3") <*> (constraintLT <$> parseVersion "2.0.0")--- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))--- >>> parseConstraint ">=1.2.3 <2.0.0"--- Just (ConstraintAnd (ConstraintOperator OperatorGE (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorLT (Version {versionMajor = 2, versionMinor = 0, versionPatch = 0, versionPreReleases = [], versionBuilds = []})))-constraintAnd :: Constraint -> Constraint -> Constraint-constraintAnd l r = ConstraintAnd l r---- | Makes a new constraint that must satisfy either constraint.------ >>> constraintOr <$> (constraintEQ <$> parseVersion "1.2.3") <*> (constraintGT <$> parseVersion "1.2.3")--- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))--- >>> parseConstraint "=1.2.3 || >1.2.3"--- Just (ConstraintOr (ConstraintOperator OperatorEQ (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})) (ConstraintOperator OperatorGT (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []})))-constraintOr :: Constraint -> Constraint -> Constraint-constraintOr l r = ConstraintOr l r---- | Makes a new constraint that must be between the versions, inclusive.------ >>> constraintHyphen <$> parseVersion "1.2.3" <*> parseVersion "2.3.4"--- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "1.2.3 - 2.3.4"--- Just (ConstraintHyphen (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}) (Version {versionMajor = 2, versionMinor = 3, versionPatch = 4, versionPreReleases = [], versionBuilds = []}))-constraintHyphen :: Version -> Version -> Constraint-constraintHyphen v w = ConstraintHyphen v w---- | Makes a new constraint that allows changes to the patch version number.------ >>> constraintTilde <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "~1.2.3"--- Just (ConstraintOperator OperatorTilde (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintTilde :: Version -> Constraint-constraintTilde v = ConstraintOperator OperatorTilde v---- | Makes a new constraint that allows changes that do not modify the--- left-most non-zero version number.------ >>> constraintCaret <$> parseVersion "1.2.3"--- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))--- >>> parseConstraint "^1.2.3"--- Just (ConstraintOperator OperatorCaret (Version {versionMajor = 1, versionMinor = 2, versionPatch = 3, versionPreReleases = [], versionBuilds = []}))-constraintCaret :: Version -> Constraint-constraintCaret v = ConstraintOperator OperatorCaret v---- ** Parsing--versionP :: ReadP.ReadP Version-versionP = do- major <- numberP- Monad.void (ReadP.char '.')- minor <- numberP- Monad.void (ReadP.char '.')- patch <- numberP- preReleases <- preReleasesP- builds <- buildsP- return (makeVersion major minor patch preReleases builds)--preReleasesP :: ReadP.ReadP [PreRelease]-preReleasesP = ReadP.option [] (do- Monad.void (ReadP.char '-')- ReadP.sepBy1 preReleaseP (ReadP.char '.'))--preReleaseP :: ReadP.ReadP PreRelease-preReleaseP = preReleaseNumberP ReadP.<++ preReleaseStringP--preReleaseNumberP :: ReadP.ReadP PreRelease-preReleaseNumberP = do- n <- numberP- return (PreReleaseNumeric n)--preReleaseStringP :: ReadP.ReadP PreRelease-preReleaseStringP = do- s <- ReadP.munch1 isIdentifier- if all Char.isDigit s- then ReadP.pfail- else return (PreReleaseTextual s)--buildsP :: ReadP.ReadP [Build]-buildsP = ReadP.option [] (do- Monad.void (ReadP.char '+')- ReadP.sepBy1 buildP (ReadP.char '.'))--buildP :: ReadP.ReadP Build-buildP = do- b <- ReadP.munch1 isIdentifier- return (Build b)--numberP :: ReadP.ReadP Word.Word64-numberP = zeroP ReadP.<++ nonZeroP--zeroP :: ReadP.ReadP Word.Word64-zeroP = do- Monad.void (ReadP.char '0')- return 0--nonZeroP :: ReadP.ReadP Word.Word64-nonZeroP = do- x <- ReadP.satisfy isAsciiDigitNonZero- ys <- ReadP.munch Char.isDigit- case toWord64 (stringToIntegral (x : ys)) of- Nothing -> ReadP.pfail- Just n -> return n--constraintsP :: ReadP.ReadP Constraint-constraintsP = do- spacesP- cs <- ReadP.sepBy1 constraintP orP- spacesP- return (foldr1 constraintOr cs)--constraintP :: ReadP.ReadP Constraint-constraintP = do- cs <- ReadP.sepBy1 simpleP spaces1P- return (foldr1 constraintAnd cs)--hyphenatedP :: ReadP.ReadP Constraint-hyphenatedP = do- v <- versionP- hyphenP- w <- versionP- return (constraintHyphen v w)--simpleP :: ReadP.ReadP Constraint-simpleP = ReadP.choice [hyphenatedP, wildcardConstraintP, primitiveP]--wildcardConstraintP :: ReadP.ReadP Constraint-wildcardConstraintP = do- ReadP.optional (ReadP.char '=')- w <- wildcardP- return (ConstraintWildcard w)--wildcardP :: ReadP.ReadP Wildcard-wildcardP = ReadP.choice [wildcardPatchP, wildcardMinorP, wildcardMajorP]--wildcardPatchP :: ReadP.ReadP Wildcard-wildcardPatchP = do- m <- numberP- Monad.void (ReadP.char '.')- n <- numberP- Monad.void (ReadP.char '.')- Monad.void (ReadP.satisfy isWildcard)- return (WildcardPatch m n)--wildcardMinorP :: ReadP.ReadP Wildcard-wildcardMinorP = do- m <- numberP- Monad.void (ReadP.char '.')- Monad.void (ReadP.satisfy isWildcard)- Monad.void (ReadP.char '.')- Monad.void (ReadP.satisfy isWildcard)- return (WildcardMinor m)--wildcardMajorP :: ReadP.ReadP Wildcard-wildcardMajorP = do- Monad.void (ReadP.satisfy isWildcard)- Monad.void (ReadP.char '.')- Monad.void (ReadP.satisfy isWildcard)- Monad.void (ReadP.char '.')- Monad.void (ReadP.satisfy isWildcard)- return WildcardMajor--primitiveP :: ReadP.ReadP Constraint-primitiveP = do- o <- operatorP- spacesP- v <- versionP- return (ConstraintOperator o v)--operatorP :: ReadP.ReadP Operator-operatorP = ReadP.choice- [ ReadP.string "<=" >> return OperatorLE- , ReadP.string ">=" >> return OperatorGE- , ReadP.char '<' >> return OperatorLT- , ReadP.char '>' >> return OperatorGT- , ReadP.char '=' >> return OperatorEQ- , ReadP.char '^' >> return OperatorCaret- , ReadP.char '~' >> return OperatorTilde- , return OperatorEQ- ]--hyphenP :: ReadP.ReadP ()-hyphenP = do- spaces1P- Monad.void (ReadP.char '-')- spaces1P--orP :: ReadP.ReadP ()-orP = do- spaces1P- Monad.void (ReadP.string "||")- spaces1P--spaces1P :: ReadP.ReadP ()-spaces1P = Monad.void (ReadP.munch1 (== ' '))--spacesP :: ReadP.ReadP ()-spacesP = Monad.void (ReadP.munch (== ' '))---- ** Rendering--renderPreReleases :: [PreRelease] -> String-renderPreReleases ps = if null ps- then ""- else '-' : List.intercalate "." (map renderPreRelease ps)--renderBuilds :: [Build] -> String-renderBuilds bs = if null bs- then ""- else '+' : List.intercalate "." (map renderBuild bs)---- ** Helpers--both :: (a -> b) -> (a, a) -> (b, b)-both f (x, y) = (f x, f y)--isAsciiDigitNonZero :: Char -> Bool-isAsciiDigitNonZero c = Char.isDigit c && (c /= '0')--isIdentifier :: Char -> Bool-isIdentifier c = (Char.isAscii c && Char.isAlphaNum c) || (c == '-')--isWildcard :: Char -> Bool-isWildcard c = (c == 'x') || (c == '*') || (c == 'X')--parse :: ReadP.ReadP a -> String -> Maybe a-parse p s =- let p' = ReadP.readP_to_S p- in Maybe.listToMaybe (do- (x, "") <- p' s- return x)--stringToIntegral :: Integral a => String -> a-stringToIntegral s = foldl- (\ n d -> (n * 10) + (fromIntegral (fromEnum d) - 48)) 0 s--toWord64 :: Integer -> Maybe Word.Word64-toWord64 n =- if n < 0- then Nothing- else if n > fromIntegral (maxBound :: Word.Word64)- then Nothing- else Just (fromIntegral n)---- * Simple constraints--- | Simple constraints are just as expressive as 'Constraint's, but they are--- easier to reason about. You can think of them as the desugared version of--- 'Constraint's.--data SimpleConstraint- = SCLT Version- | SCEQ Version- | SCGT Version- | SCAnd SimpleConstraint SimpleConstraint- | SCOr SimpleConstraint SimpleConstraint- deriving (Data.Data, Eq, Generics.Generic, Ord, Read, Show)--mkV :: Word.Word64 -> Word.Word64 -> Word.Word64 -> Version-mkV m n p = makeVersion m n p [] []--satisfiesSC :: SimpleConstraint -> Version -> Bool-satisfiesSC c v = case c of- SCLT u -> v < u- -- This uses `compare` rather than `==` to ignore build metadata.- SCEQ u -> compare v u == EQ- SCGT u -> v > u- SCAnd l r -> satisfiesSC l v && satisfiesSC r v- SCOr l r -> satisfiesSC l v || satisfiesSC r v--scLE :: Version -> SimpleConstraint-scLE v = SCOr (SCLT v) (SCEQ v)--scGE :: Version -> SimpleConstraint-scGE v = SCOr (SCGT v) (SCEQ v)--toSC :: Constraint -> SimpleConstraint-toSC c = case c of- ConstraintOperator o v -> case o of- OperatorLT -> SCLT v- OperatorLE -> scLE v- OperatorEQ -> SCEQ v- OperatorGE -> scGE v- OperatorGT -> SCGT v- OperatorTilde -> SCAnd- (scGE v)- (SCLT (mkV (versionMajor v) (versionMinor v + 1) 0))- OperatorCaret -> SCAnd- (scGE v)- (SCLT (case (versionMajor v, versionMinor v, versionPatch v) of- (0, 0, p) -> mkV 0 0 (p + 1)- (0, n, _) -> mkV 0 (n + 1) 0- (m, _, _) -> mkV (m + 1) 0 0))- ConstraintHyphen l h -> SCAnd (scGE l) (scLE h)- ConstraintWildcard w -> case w of- WildcardMajor -> scGE initialVersion- WildcardMinor m -> SCAnd (scGE (mkV m 0 0)) (SCLT (mkV (m + 1) 0 0))- WildcardPatch m n -> SCAnd (scGE (mkV m n 0)) (SCLT (mkV m (n + 1) 0))- ConstraintAnd l r -> SCAnd (toSC l) (toSC r)- ConstraintOr l r -> SCOr (toSC l) (toSC r)
− src/test/Main.hs
@@ -1,102 +0,0 @@--- import qualified Control.Applicative as Applicative-import qualified Control.Monad as Monad--- import qualified Data.Functor.Identity as Identity-import qualified Salve-import qualified System.Exit as Exit-import qualified Test.HUnit as Test--main :: IO ()-main = do- counts <- Test.runTestTT $ Test.TestList- [ (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "2.0.0") Test.~?= Just LT- , (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "1.3.0") Test.~?= Just LT- , (compare <$> Salve.parseVersion "1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just LT- , (compare <$> Salve.parseVersion "0.0.9" <*> Salve.parseVersion "0.0.10") Test.~?= Just LT- , (compare <$> Salve.parseVersion "1.2.3-a" <*> Salve.parseVersion "1.2.3-b") Test.~?= Just LT- , (compare <$> Salve.parseVersion "1.2.3-pre" <*> Salve.parseVersion "1.2.3") Test.~?= Just LT- , (compare <$> Salve.parseVersion "1.2.4-pre" <*> Salve.parseVersion "1.2.3") Test.~?= Just GT- , (compare <$> Salve.parseVersion "1.2.3+a" <*> Salve.parseVersion "1.2.3+b") Test.~?= Just EQ- , ((==) <$> Salve.parseVersion "1.2.3+a" <*> Salve.parseVersion "1.2.3+b") Test.~?= Just False- , (compare <$> Salve.parsePreRelease "1" <*> Salve.parsePreRelease "a") Test.~?= Just LT- , (compare <$> Salve.parsePreRelease "9" <*> Salve.parsePreRelease "10") Test.~?= Just LT- , (compare <$> Salve.parsePreRelease "p10" <*> Salve.parsePreRelease "p9") Test.~?= Just LT- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<1.2.3" <*> Salve.parseVersion "1.2.3-pre") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<=1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<=1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "<=1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "=1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "=1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "=1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "=1.2.3" <*> Salve.parseVersion "1.2.3-pre") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "=1.2.3" <*> Salve.parseVersion "1.2.3+build") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">=1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">=1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">=1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3" <*> Salve.parseVersion "1.2.4-pre") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3-pre" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3 <1.2.5" <*> Salve.parseVersion "1.2.3") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3 <1.2.5" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint ">1.2.3 <1.2.5" <*> Salve.parseVersion "1.2.5") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 || 1.2.4" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 || 1.2.4" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 || 1.2.4" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 || 1.2.4" <*> Salve.parseVersion "1.2.5") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0" <*> Salve.parseVersion "1.2.2") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0" <*> Salve.parseVersion "1.2.3") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.2 || >1.2.3 <1.3.0" <*> Salve.parseVersion "1.3.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 - 1.2.4" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 - 1.2.4" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 - 1.2.4" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.3 - 1.2.4" <*> Salve.parseVersion "1.2.5") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "~1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "~1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "~1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "~1.2.3" <*> Salve.parseVersion "1.3.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^1.2.3" <*> Salve.parseVersion "1.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^1.2.3" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^1.2.3" <*> Salve.parseVersion "1.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^1.2.3" <*> Salve.parseVersion "1.3.0") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^1.2.3" <*> Salve.parseVersion "2.0.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.2.3" <*> Salve.parseVersion "0.2.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.2.3" <*> Salve.parseVersion "0.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.2.3" <*> Salve.parseVersion "0.2.4") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.2.3" <*> Salve.parseVersion "0.3.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.0.3" <*> Salve.parseVersion "0.0.2") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.0.3" <*> Salve.parseVersion "0.0.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "^0.0.3" <*> Salve.parseVersion "0.0.4") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.x" <*> Salve.parseVersion "1.1.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.x" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.2.x" <*> Salve.parseVersion "1.3.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.x.x" <*> Salve.parseVersion "0.1.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.x.x" <*> Salve.parseVersion "1.0.0") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.x.x" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "1.x.x" <*> Salve.parseVersion "2.0.0") Test.~?= Just False- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "x.x.x" <*> Salve.parseVersion "0.0.0") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "x.x.x" <*> Salve.parseVersion "1.2.3") Test.~?= Just True- , (Salve.satisfiesConstraint <$> Salve.parseConstraint "x.x.x" <*> Salve.parseVersion "2.0.0") Test.~?= Just True- ]-- let- hasErrors = Test.errors counts /= 0- hasFailures = Test.failures counts /= 0- Monad.when (hasErrors || hasFailures) Exit.exitFailure---- view--- :: ((record -> Applicative.Const record field) -> record -> Applicative.Const field field)--- -> record--- -> field--- view lens = Applicative.getConst . lens Applicative.Const---- set--- :: ((record -> Identity.Identity field) -> record -> Identity.Identity record)--- -> field--- -> record--- -> record--- set lens field = Identity.runIdentity . lens (const $ Identity.Identity field)