-
-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathVersion.hs
More file actions
154 lines (144 loc) · 5.47 KB
/
Version.hs
File metadata and controls
154 lines (144 loc) · 5.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module : HsLua.Module.Version
Copyright : © 2019-2026 Albert Krewinkel
License : MIT
Maintainer : Albert Krewinkel <tarleb@hslua.org>
Lua module to work with version specifiers.
-}
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 = defmodule "Version"
`withDescription` "Version specifier handling"
`withFunctions` [must_be_at_least]
`withOperations`
[ operation Call $ lambda
### liftPure2 (\_ v -> v)
<#> parameter (const $ pure ()) "table" "module table" "ignored"
<#> versionParam "version" "{Version|string|{integer,...}|number}"
=#> udresult typeVersion "new Version object"
]
-- | 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`."
]