aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Lua/Marshaling/Version.hs
blob: 2af36e5c8e4eec90d58e7f3eae6dc2ddf9408da8 (plain)
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
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE LambdaCase           #-}
{-# LANGUAGE OverloadedStrings    #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{- |
   Module      : Text.Pandoc.Lua.Marshaling.Version
   Copyright   : © 2019-2021 Albert Krewinkel
   License     : GNU GPL, version 2 or above

   Maintainer  : Albert Krewinkel <tarleb+pandoc@moltkeplatz.de>
   Stability   : alpha

Marshaling of @'Version'@s. The marshaled elements can be compared using
default comparison operators (like @>@ and @<=@).
-}
module Text.Pandoc.Lua.Marshaling.Version
  ( peekVersion
  , pushVersion
  , peekVersionFuzzy
  )
  where

import Data.Maybe (fromMaybe)
import Data.Version (Version (..), makeVersion, parseVersion, showVersion)
import HsLua as Lua
import Safe (lastMay)
import Text.ParserCombinators.ReadP (readP_to_S)
import qualified Text.Pandoc.UTF8 as UTF8

instance Peekable Version where
  peek = forcePeek . peekVersionFuzzy

instance Pushable Version where
  push = pushVersion

-- | Push a @'Version'@ element to the Lua stack.
pushVersion :: LuaError e => Pusher e Version
pushVersion = pushUD typeVersion

peekVersionFuzzy :: LuaError e => Peeker e Version
peekVersionFuzzy idx = retrieving "Version" $ liftLua (Lua.ltype idx) >>= \case
  Lua.TypeUserdata -> peekVersion idx
  Lua.TypeString -> do
    versionStr <- peekString idx
    let parses = readP_to_S parseVersion versionStr
    case lastMay parses of
      Just (v, "") -> return v
      _  -> Lua.failPeek $
            UTF8.fromString $ "could not parse as Version: " ++ versionStr

  Lua.TypeNumber -> do
    (makeVersion . (:[])) <$> peekIntegral idx

  Lua.TypeTable ->
    makeVersion <$> peekList peekIntegral idx

  _ ->
    Lua.failPeek "could not peek Version"

peekVersion :: LuaError e => Peeker e Version
peekVersion = peekUD typeVersion

typeVersion :: LuaError e => DocumentedType e Version
typeVersion = deftype "Version"
  [ operation Eq $ defun "__eq"
    ### liftPure2 (==)
    <#> parameter peekVersionFuzzy "Version" "v1" ""
    <#> parameter peekVersionFuzzy "Version" "v2" ""
    =#> functionResult pushBool "boolean" "true iff v1 == v2"
  , operation Lt $ defun "__lt"
    ### liftPure2 (<)
    <#> parameter peekVersionFuzzy "Version" "v1" ""
    <#> parameter peekVersionFuzzy "Version" "v2" ""
    =#> functionResult pushBool "boolean" "true iff v1 < v2"
  , operation Le $ defun "__le"
    ### liftPure2 (<=)
    <#> parameter peekVersionFuzzy "Version" "v1" ""
    <#> parameter peekVersionFuzzy "Version" "v2" ""
    =#> functionResult pushBool "boolean" "true iff v1 <= v2"
  , operation Len $ defun "__len"
    ### liftPure (length . versionBranch)
    <#> parameter peekVersionFuzzy "Version" "v1" ""
    =#> functionResult pushIntegral "integer" "number of version components"
  , operation Tostring $ defun "__tostring"
    ### liftPure showVersion
    <#> parameter peekVersionFuzzy "Version" "version" ""
    =#> functionResult pushString "string" "stringified version"
  ]
  [ method $ defun "must_be_at_least"
    ### must_be_at_least
    <#> parameter peekVersionFuzzy "Version" "self" "version to check"
    <#> parameter peekVersionFuzzy "Version" "reference" "minimum version"
    <#> optionalParameter peekString "string" "msg" "alternative message"
    =?> "Returns no result, and throws an error if this version is older than reference"
  ]

-- | Throw an error if this version is older than the given version.
-- FIXME: This function currently requires the string library to be
-- loaded.
must_be_at_least :: LuaError e
                 => Version -> Version -> Maybe String
                 -> LuaE e NumResults
must_be_at_least actual expected mMsg = do
  let msg = fromMaybe versionTooOldMessage mMsg
  if expected <= actual
    then return 0
    else do
      Lua.getglobal' "string.format"
      Lua.push msg
      Lua.push (showVersion expected)
      Lua.push (showVersion actual)
      Lua.call 3 1
      Lua.error

-- | Default error message when a version is too old. This message is
-- formatted in Lua with the expected and actual versions as arguments.
versionTooOldMessage :: String
versionTooOldMessage = "expected version %s or newer, got %s"