@@ -0,0 +1,154 @@
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE NoImplicitPrelude #-}
5+ Module : Text.Pandoc.Lua.Marshaling.Version
6+ Copyright : © 2019 Albert Krewinkel
7+ License : GNU GPL, version 2 or above
9+ Maintainer : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
12+Marshaling of @'Version'@s. The marshaled elements can be compared using
13+default comparison operators (like @>@ and @<=@).
15+module Text.Pandoc.Lua.Marshaling.Version
22+import Data.Maybe (fromMaybe)
23+import Data.Version (Version (..), makeVersion, parseVersion, showVersion)
24+import Foreign.Lua (Lua, Optional (..), NumResults,
25+ Peekable, Pushable, StackIndex)
26+import Foreign.Lua.Types.Peekable (reportValueOnFailure)
27+import Foreign.Lua.Userdata (ensureUserdataMetatable, pushAnyWithMetatable,
29+import Safe (atMay, lastMay)
30+import Text.Pandoc.Lua.Marshaling.AnyValue (AnyValue (..))
31+import Text.ParserCombinators.ReadP (readP_to_S)
33+import qualified Foreign.Lua as Lua
34+import qualified Text.Pandoc.Lua.Util as LuaUtil
36+-- | Push a @'Version'@ element to the Lua stack.
37+pushVersion :: Version -> Lua ()
38+pushVersion version = pushAnyWithMetatable pushVersionMT version
40+ pushVersionMT = ensureUserdataMetatable versionTypeName $ do
41+ LuaUtil.addFunction "__eq" __eq
42+ LuaUtil.addFunction "__le" __le
43+ LuaUtil.addFunction "__lt" __lt
44+ LuaUtil.addFunction "__len" __len
45+ LuaUtil.addFunction "__index" __index
46+ LuaUtil.addFunction "__pairs" __pairs
47+ LuaUtil.addFunction "__tostring" __tostring
49+instance Pushable Version where
52+peekVersion :: StackIndex -> Lua Version
53+peekVersion idx = Lua.ltype idx >>= \case
55+ versionStr <- Lua.peek idx
56+ let parses = readP_to_S parseVersion versionStr
57+ case lastMay parses of
58+ Just (v, "") -> return v
59+ _ -> Lua.throwException $ "could not parse as Version: " ++ versionStr
62+ reportValueOnFailure versionTypeName
63+ (`toAnyWithName` versionTypeName)
67+ return (makeVersion [n])
70+ makeVersion <$> Lua.peek idx
73+ Lua.throwException "could not peek Version"
75+instance Peekable Version where
78+-- | Name used by Lua for the @CommonState@ type.
79+versionTypeName :: String
80+versionTypeName = "HsLua Version"
82+__eq :: Version -> Version -> Lua Bool
83+__eq v1 v2 = return (v1 == v2)
85+__le :: Version -> Version -> Lua Bool
86+__le v1 v2 = return (v1 <= v2)
88+__lt :: Version -> Version -> Lua Bool
89+__lt v1 v2 = return (v1 < v2)
91+-- | Get number of version components.
92+__len :: Version -> Lua Int
93+__len = return . length . versionBranch
96+__index :: Version -> AnyValue -> Lua NumResults
97+__index v (AnyValue k) = do
100+ Lua.TypeNumber -> do
102+ let versionPart = atMay (versionBranch v) (n - 1)
103+ Lua.push (Lua.Optional versionPart)
105+ Lua.TypeString -> do
107+ if str == "must_be_at_least"
108+ then 1 <$ Lua.pushHaskellFunction must_be_at_least
109+ else 1 <$ Lua.pushnil
110+ _ -> 1 <$ Lua.pushnil
112+-- | Create iterator.
113+__pairs :: Version -> Lua NumResults
115+ Lua.pushHaskellFunction nextFn
120+ nextFn :: AnyValue -> Optional Int -> Lua Lua.NumResults
121+ nextFn _ (Optional key) =
123+ Nothing -> case versionBranch v of
124+ [] -> 2 <$ (Lua.pushnil *> Lua.pushnil)
125+ n:_ -> 2 <$ (Lua.push (1 :: Int) *> Lua.push n)
126+ Just n -> case atMay (versionBranch v) n of
127+ Nothing -> 2 <$ (Lua.pushnil *> Lua.pushnil)
128+ Just b -> 2 <$ (Lua.push (n + 1) *> Lua.push b)
130+-- | Convert to string.
131+__tostring :: Version -> Lua String
132+__tostring v = return (showVersion v)
134+-- | Default error message when a version is too old. This message is
135+-- formatted in Lua with the expected and actual versions as arguments.
136+versionTooOldMessage :: String
137+versionTooOldMessage = "version too old: expected version %s or newer, got %s"
139+-- | Throw an error if this version is older than the given version.
140+-- FIXME: This function currently requires the string library to be
142+must_be_at_least :: Version -> Version -> Optional String -> Lua NumResults
143+must_be_at_least actual expected optMsg = do
144+ let msg = fromMaybe versionTooOldMessage (fromOptional optMsg)
145+ if expected <= actual
148+ Lua.getglobal' "string.format"
150+ Lua.push (showVersion expected)
151+ Lua.push (showVersion actual)