hslua-module-version-1.1.0/0000755000000000000000000000000007346545000013764 5ustar0000000000000000hslua-module-version-1.1.0/CHANGELOG.md0000644000000000000000000000220307346545000015572 0ustar0000000000000000# Changelog `hslua-module-version` uses [PVP Versioning][]. ## hslua-module-version-1.1.0 Released 2023-03-13. - Update to hslua-2.3; this includes the addition of type initializers to the module. ## hslua-module-version-1.0.3 Released 2022-09-01. - Allow equality checks with non-version values: A *Version* value can now be compared with any value. Previously, comparing a version with a value that cannot be interpreted as a Version would result in an error, violating the principle of least surprise. ## hslua-module-version-1.0.2 Released 2022-02-19. - Relax upper bounds, allowing hslua-core-2.2.*, hslua-marshalling-2.2.\* and hslua-packaging-2.2.\*. ## hslua-module-version-1.0.1 - Relaxed upper bound of hslua-core, hslua-marshalling, and hslua-packaging, allowing their respective version 2.1. - Version objects are now modifiable; setting a list index modifies the Version object: ``` lua local v = Version '5.3.6' v[2] = 2 v[3] = nil assert(v == Version '5.2') ``` ## hslua-module-version-1.0.0 Released 2021-10-22. [PVP Versioning]: https://pvp.haskell.org hslua-module-version-1.1.0/LICENSE0000644000000000000000000000224107346545000014770 0ustar0000000000000000Copyright © 1994-2022 Lua.org, PUC-Rio. Copyright © 2007-2012 Gracjan Polak Copyright © 2012-2015 Ömer Sinan Ağacan Copyright © 2016-2023 Albert Krewinkel 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. hslua-module-version-1.1.0/README.md0000644000000000000000000000156707346545000015254 0ustar0000000000000000# hslua-module-version [![GitHub CI][CI badge]](https://github.com/hslua/hslua-module-version/actions) [![Hackage][Hackage badge]](https://hackage.haskell.org/package/hslua-module-version) [![Stackage Lts][Stackage Lts badge]](http://stackage.org/lts/package/hslua-module-version) [![Stackage Nightly][Stackage Nightly badge]](http://stackage.org/nightly/package/hslua-module-version) [![MIT license][License badge]](LICENSE) [CI badge]: https://img.shields.io/github/workflow/status/hslua/hslua/CI.svg?logo=github [Hackage badge]: https://img.shields.io/hackage/v/hslua-module-version.svg?logo=haskell [Stackage Lts badge]: http://stackage.org/package/hslua-module-version/badge/lts [Stackage Nightly badge]: http://stackage.org/package/hslua-module-version/badge/nightly [License badge]: https://img.shields.io/badge/license-MIT-blue.svg Lua module to work with version specifiers. hslua-module-version-1.1.0/hslua-module-version.cabal0000644000000000000000000000510507346545000021033 0ustar0000000000000000cabal-version: 2.2 name: hslua-module-version version: 1.1.0 synopsis: Lua module to work with version specifiers. description: Wrapper for the Data.Version.Version Haskell type. homepage: https://hslua.org/ bug-reports: https://github.com/hslua/hslua/issues license: MIT license-file: LICENSE author: Albert Krewinkel maintainer: Albert Krewinkel copyright: © 2019-2023 Albert Krewinkel category: Foreign build-type: Simple extra-doc-files: README.md CHANGELOG.md extra-source-files: test/test-version.lua tested-with: GHC == 8.4.4 , GHC == 8.6.5 , GHC == 8.8.4 , GHC == 8.10.7 , GHC == 9.0.2 , GHC == 9.2.5 , GHC == 9.4.4 source-repository head type: git location: https://github.com/hslua/hslua.git subdir: hslua-module-version common common-options build-depends: base >= 4.9.1 && < 5 , filepath >= 1.4 && < 1.5 , hslua-core >= 2.3 && < 2.4 , hslua-marshalling >= 2.3 && < 2.4 , hslua-packaging >= 2.3 && < 2.4 , text >= 1.2 && < 2.1 ghc-options: -Wall -Wcompat -Widentities -Wincomplete-uni-patterns -Wincomplete-record-updates if impl(ghc >= 8.0) ghc-options: -Wredundant-constraints if impl(ghc >= 8.2) ghc-options: -fhide-source-paths if impl(ghc >= 8.4) ghc-options: -Wmissing-export-lists -Wpartial-fields if impl(ghc >= 8.8) ghc-options: -Wmissing-deriving-strategies default-language: Haskell2010 library import: common-options hs-source-dirs: src exposed-modules: HsLua.Module.Version test-suite hslua-module-version-test import: common-options type: exitcode-stdio-1.0 hs-source-dirs: test main-is: test-hslua-module-version.hs build-depends: base , hslua-module-version , tasty , tasty-hunit , tasty-lua >= 1.0 && < 1.2 ghc-options: -threaded -rtsopts -with-rtsopts=-N hslua-module-version-1.1.0/src/HsLua/Module/0000755000000000000000000000000007346545000017014 5ustar0000000000000000hslua-module-version-1.1.0/src/HsLua/Module/Version.hs0000644000000000000000000001303507346545000020777 0ustar0000000000000000{-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-| Module : HsLua.Module.Version Copyright : © 2019-2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Lua module to work with file paths. -} module HsLua.Module.Version ( -- * Module documentedModule -- * Version objects , typeVersion , peekVersion , pushVersion , peekVersionFuzzy ) where import Prelude hiding (error) import Control.Applicative (optional) import Data.Maybe (fromMaybe) import Data.Version ( Version, makeVersion, parseVersion, showVersion, versionBranch ) import Data.List.NonEmpty as NonEmpty (last, nonEmpty) import Data.Text (Text) import HsLua.Core ( LuaError, Type (..) , call, dostring, error, ltype ) import HsLua.Marshalling ( Peeker, Pusher, failPeek, liftLua, peekIntegral, peekList, peekString , pushIntegral, pushIterator, pushString, retrieving ) import HsLua.Packaging import Text.ParserCombinators.ReadP (readP_to_S) import qualified HsLua.Core.Utf8 as UTF8 -- | The @path@ module specification. documentedModule :: LuaError e => Module e documentedModule = Module { moduleName = "Version" , moduleDescription = "Version specifier handling" , moduleFields = [] , moduleFunctions = [must_be_at_least] , moduleOperations = [ operation Call $ lambda ### liftPure2 (\_ v -> v) <#> parameter (const $ pure ()) "table" "module table" "ignored" <#> versionParam "version" "version-like object" =#> udresult typeVersion "new Version object" ] , moduleTypeInitializers = [] } -- | Type definition of Lua Version values. typeVersion :: LuaError e => DocumentedTypeWithList e Version Int typeVersion = deftype' "Version" [ operation Eq $ lambda ### liftPure2 (\a b -> fromMaybe False ((==) <$> a <*> b)) <#> parameter (optional . peekVersionFuzzy) "Version" "a" "" <#> parameter (optional . peekVersionFuzzy) "Version" "b" "" =#> boolResult "true iff v1 == v2" , operation Le $ versionComparison (<=) "true iff v1 <= v2" , operation Lt $ versionComparison (<) "true iff v1 < v2" , operation Len $ lambda ### liftPure (length . versionBranch) <#> versionParam "version" "" =#> integralResult "number of version components" , operation Pairs $ lambda ### pushIterator (\(i, n) -> 2 <$ pushIntegral i <* pushIntegral n) . zip [(1 :: Int) ..] . versionBranch <#> versionParam "version" "" =?> "iterator values" , operation Tostring $ lambda ### liftPure showVersion <#> versionParam "version" "" =#> stringResult "stringified version" ] [ method must_be_at_least ] (Just ( (pushIntegral, versionBranch) , (peekIntegral, const makeVersion))) where versionComparison f descr = lambda ### liftPure2 f <#> versionParam "v1" "" <#> versionParam "v2" "" =#> boolResult descr -- | Push a @'Version'@ element to the Lua stack. pushVersion :: LuaError e => Pusher e Version pushVersion = pushUD typeVersion -- | Retrieve a @'Version'@ object from the top of the stack. peekVersion :: LuaError e => Peeker e Version peekVersion = peekUD typeVersion -- | Retrieve a Version-like object from the top of the stack. -- -- This function uses these heuristics, depending on the Lua object -- type. -- -- * string: object is parsed as a version specifier. -- -- * table: value is expected to be a list of integers, with each -- index specifying a version branch. -- -- * userdata: assumes the value to be a Version userdata object. -- -- * number: parses the number as an integer value. -- -- Otherwise, or if the object fails to meet an expectation, peeking -- fails. peekVersionFuzzy :: LuaError e => Peeker e Version peekVersionFuzzy idx = retrieving "Version" $ liftLua (ltype idx) >>= \case TypeUserdata -> peekVersion idx TypeString -> do versionStr <- peekString idx let parses = readP_to_S parseVersion versionStr case NonEmpty.last <$> NonEmpty.nonEmpty parses of Just (v, "") -> return v _ -> failPeek $ "could not parse as Version: " <> UTF8.fromString versionStr TypeNumber -> makeVersion . (:[]) <$> peekIntegral idx TypeTable -> makeVersion <$> peekList peekIntegral idx _ -> failPeek "could not peek Version" -- | Parameter that takes a Version-like object. versionParam :: LuaError e => Text -> Text -> Parameter e Version versionParam = parameter peekVersionFuzzy "Version" -- | Throw an error if this version is older than the given version. This -- function currently the string library to be loaded. must_be_at_least :: LuaError e => DocumentedFunction e must_be_at_least = defun "must_be_at_least" ### (\actual expected mMsg -> do -- Default error message when a version is too old. This -- message is formatted in Lua with the expected and actual -- versions as arguments. let versionTooOldMessage = "expected version %s or newer, got %s" let msg = fromMaybe versionTooOldMessage mMsg if expected <= actual then return 0 else do _ <- dostring "return string.format" pushString msg pushString (showVersion expected) pushString (showVersion actual) call 3 1 error) <#> versionParam "self" "version to check" <#> versionParam "reference" "minimum version" <#> opt (stringParam "msg" "alternative message") =?> mconcat [ "Returns no result, and throws an error if this " , "version is older than `reference`." ] hslua-module-version-1.1.0/test/0000755000000000000000000000000007346545000014743 5ustar0000000000000000hslua-module-version-1.1.0/test/test-hslua-module-version.hs0000644000000000000000000000353507346545000022344 0ustar0000000000000000{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TypeApplications #-} {-| Module : Main Copyright : © 2021-2023 Albert Krewinkel License : MIT Maintainer : Albert Krewinkel Tests for the `version` Lua module. -} module Main (main) where import Control.Monad (void) import HsLua.Core (Lua, top) import HsLua.Packaging.Module (preloadModule, preloadModuleWithName, pushModule, registerModule) import HsLua.Module.Version (documentedModule) import Test.Tasty (TestTree, defaultMain, testGroup) import Test.Tasty.HUnit (assertEqual, testCase) import Test.Tasty.Lua (translateResultsFromFile) import qualified HsLua.Core as Lua main :: IO () main = do luaTestResults <- Lua.run @Lua.Exception $ do Lua.openlibs registerModule documentedModule Lua.pop 1 translateResultsFromFile "test/test-version.lua" defaultMain $ testGroup "hslua-module-version" [tests, luaTestResults] -- | HSpec tests for the Lua 'version' module tests :: TestTree tests = testGroup "HsLua version module" [ testCase "version module can be pushed to the stack" $ Lua.run (void (pushModule documentedModule) :: Lua ()) , testCase "version module can be added to the preloader" . Lua.run $ do Lua.openlibs preloadModule documentedModule assertEqual' "function not added to preloader" Lua.TypeFunction =<< do void $ Lua.getglobal "package" *> Lua.getfield top "preload" *> Lua.getfield top "Version" Lua.ltype (-1) , testCase "version module can be loaded as hsversion" . Lua.run $ do Lua.openlibs preloadModuleWithName documentedModule "hsversion" assertEqual' "loading the module fails " Lua.OK =<< Lua.dostring "require 'hsversion'" ] assertEqual' :: (Show a, Eq a) => String -> a -> a -> Lua () assertEqual' msg expected = Lua.liftIO . assertEqual msg expected hslua-module-version-1.1.0/test/test-version.lua0000644000000000000000000001055007346545000020111 0ustar0000000000000000-- -- Tests for the `version` module -- local Version = require 'Version' local tasty = require 'tasty' local group = tasty.test_group local test = tasty.test_case local assert = tasty.assert return { group 'constructor' { test('has type `userdata`', function () assert.are_same(type(Version {2}), 'userdata') end), test('accepts list of integers', function () assert.are_same(type(Version {2, 7, 3}), 'userdata') end), test('accepts a single integer', function () assert.are_same(Version(5), Version {5}) end), test('accepts version as string', function () assert.are_same( Version '4.45.1', Version {4, 45, 1} ) end), test('non-version string is rejected', function () local success, msg = pcall(function () return Version '11friends' end) assert.is_falsy(success) assert.is_truthy(tostring(msg):match('11friends')) end) }, group 'comparison' { test('smaller (equal) than', function () assert.is_truthy(Version {2, 58, 3} < Version {2, 58, 4}) assert.is_falsy(Version {2, 60, 1} < Version {2, 59, 2}) assert.is_truthy(Version {0, 14, 3} < Version {0, 14, 3, 1}) assert.is_truthy(Version {3, 58, 3} <= Version {4}) assert.is_truthy(Version {0, 14, 3} <= Version {0, 14, 3, 1}) end), test('larger (equal) than', function () assert.is_truthy(Version{2,58,3} > Version {2, 57, 4}) assert.is_truthy(Version{2,58,3} > Version {2, 58, 2}) assert.is_truthy(Version {0, 8} >= Version {0, 8}) assert.is_falsy(Version {0, 8} >= Version {0, 8, 2}) end), test('equality', function () assert.is_truthy(Version '8.8', Version {8, 8}) end), test('second argument can be a version string', function () assert.is_truthy(Version '8' < '9.1') assert.is_falsy(Version '8.8' < '8.7') end), test('equality test works with value not usable as a version', function () assert.is_falsy(Version '8' == function() end) end), test('other comparisons fail if one value is not a version', function () assert.error_matches( function () return Version '8' < (function() end) end, 'could not peek Version' ) assert.error_matches( function () return Version '8' <= (function() end) end, 'could not peek Version' ) assert.error_matches( function () return Version '8' > (function() end) end, 'could not peek Version' ) end) }, group 'conversion to string' { test('converting from and to string is a noop', function () local version_string = '1.19.4' assert.are_equal(tostring(Version(version_string)), version_string) end) }, group 'convenience functions' { test('throws error if version is too old', function () local actual = Version {2, 8} local expected = Version {2, 9} assert.error_matches( function () actual:must_be_at_least(expected) end, 'expected version 2.9 or newer, got 2.8' ) end), test('does nothing if expected version is older than actual', function () local actual = Version '2.9' local expected = Version '2.8' actual:must_be_at_least(expected) end), test('does nothing if expected version equals to actual', function () local actual = Version '2.8' local expected = Version '2.8' actual:must_be_at_least(expected) end) }, group 'list-like behavior' { test('can access version component numbers', function () local version = Version '2.7.3' assert.is_nil(version[0]) assert.are_equal(version[1], 2) assert.are_equal(version[2], 7) assert.are_equal(version[3], 3) end), test('can be iterated over', function () local version_list = {2, 7, 3} local final_index = 0 for i, v in pairs(Version(version_list)) do assert.are_equal(v, version_list[i]) final_index = i end assert.are_equal(final_index, 3) end), test('length is the number of components', function () assert.are_equal(#(Version '0'), 1) assert.are_equal(#(Version '1.6'), 2) assert.are_equal(#(Version '8.7.5'), 3) assert.are_equal(#(Version '2.9.1.5'), 4) end), test('branches can be modified', function () local v = Version '5.3.6' v[2] = 2 v[3] = nil assert.are_equal(v, Version '5.2') end) } }