mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-03-30 20:45:52 +00:00
* include server version range in transport handle * xftp handshake * remove coment * simplify * comments
163 lines
5.2 KiB
Haskell
163 lines
5.2 KiB
Haskell
{-# LANGUAGE ConstrainedClassMethods #-}
|
|
{-# LANGUAGE FlexibleContexts #-}
|
|
{-# LANGUAGE FlexibleInstances #-}
|
|
{-# LANGUAGE FunctionalDependencies #-}
|
|
{-# LANGUAGE NamedFieldPuns #-}
|
|
{-# LANGUAGE OverloadedStrings #-}
|
|
{-# LANGUAGE PatternSynonyms #-}
|
|
{-# LANGUAGE TypeFamilies #-}
|
|
|
|
module Simplex.Messaging.Version
|
|
( Version,
|
|
VersionRange (minVersion, maxVersion),
|
|
VersionScope,
|
|
pattern VersionRange,
|
|
VersionI (..),
|
|
VersionRangeI (..),
|
|
Compatible,
|
|
pattern Compatible,
|
|
mkVersionRange,
|
|
safeVersionRange,
|
|
versionToRange,
|
|
isCompatible,
|
|
isCompatibleRange,
|
|
proveCompatible,
|
|
compatibleVersion,
|
|
compatibleVRange,
|
|
compatibleVRange',
|
|
)
|
|
where
|
|
|
|
import Control.Applicative (optional)
|
|
import qualified Data.Aeson as J
|
|
import qualified Data.Aeson.Encoding as JE
|
|
import Data.Aeson.Types ((.:), (.=))
|
|
import qualified Data.Aeson.Types as JT
|
|
import qualified Data.Attoparsec.ByteString.Char8 as A
|
|
import Simplex.Messaging.Encoding
|
|
import Simplex.Messaging.Encoding.String
|
|
import Simplex.Messaging.Version.Internal (Version (..))
|
|
|
|
pattern VersionRange :: Version v -> Version v -> VersionRange v
|
|
pattern VersionRange v1 v2 <- VRange v1 v2
|
|
|
|
{-# COMPLETE VersionRange #-}
|
|
|
|
data VersionRange v = VRange
|
|
{ minVersion :: Version v,
|
|
maxVersion :: Version v
|
|
}
|
|
deriving (Eq, Show)
|
|
|
|
instance J.FromJSON (VersionRange v) where
|
|
parseJSON (J.Object v) = do
|
|
minVersion <- v .: "minVersion"
|
|
maxVersion <- v .: "maxVersion"
|
|
pure VRange {minVersion, maxVersion}
|
|
parseJSON invalid =
|
|
JT.prependFailure "bad VersionRange, " (JT.typeMismatch "Object" invalid)
|
|
|
|
instance J.ToJSON (VersionRange v) where
|
|
toEncoding VRange {minVersion, maxVersion} = JE.pairs $ ("minVersion" .= minVersion) <> ("maxVersion" .= maxVersion)
|
|
toJSON VRange {minVersion, maxVersion} = J.object ["minVersion" .= minVersion, "maxVersion" .= maxVersion]
|
|
|
|
class VersionScope v
|
|
|
|
-- | construct valid version range, to be used in constants
|
|
mkVersionRange :: Version v -> Version v -> VersionRange v
|
|
mkVersionRange v1 v2
|
|
| v1 <= v2 = VRange v1 v2
|
|
| otherwise = error "invalid version range"
|
|
|
|
safeVersionRange :: Version v -> Version v -> Maybe (VersionRange v)
|
|
safeVersionRange v1 v2
|
|
| v1 <= v2 = Just $ VRange v1 v2
|
|
| otherwise = Nothing
|
|
|
|
versionToRange :: Version v -> VersionRange v
|
|
versionToRange v = VRange v v
|
|
|
|
instance VersionScope v => Encoding (VersionRange v) where
|
|
smpEncode (VRange v1 v2) = smpEncode (v1, v2)
|
|
smpP =
|
|
maybe (fail "invalid version range") pure
|
|
=<< safeVersionRange <$> smpP <*> smpP
|
|
|
|
instance VersionScope v => StrEncoding (VersionRange v) where
|
|
strEncode (VRange v1 v2)
|
|
| v1 == v2 = strEncode v1
|
|
| otherwise = strEncode v1 <> "-" <> strEncode v2
|
|
strP = do
|
|
v1 <- strP
|
|
v2 <- maybe (pure v1) (const strP) =<< optional (A.char '-')
|
|
maybe (fail "invalid version range") pure $ safeVersionRange v1 v2
|
|
|
|
class VersionScope v => VersionI v a | a -> v where
|
|
type VersionRangeT v a
|
|
version :: a -> Version v
|
|
toVersionRangeT :: a -> VersionRange v -> VersionRangeT v a
|
|
|
|
class VersionScope v => VersionRangeI v a | a -> v where
|
|
type VersionT v a
|
|
versionRange :: a -> VersionRange v
|
|
toVersionRange :: a -> VersionRange v -> a
|
|
toVersionT :: a -> Version v -> VersionT v a
|
|
|
|
instance VersionScope v => VersionI v (Version v) where
|
|
type VersionRangeT v (Version v) = VersionRange v
|
|
version = id
|
|
toVersionRangeT _ vr = vr
|
|
|
|
instance VersionScope v => VersionRangeI v (VersionRange v) where
|
|
type VersionT v (VersionRange v) = Version v
|
|
versionRange = id
|
|
toVersionRange _ vr = vr
|
|
toVersionT _ v = v
|
|
|
|
newtype Compatible a = Compatible_ a
|
|
|
|
pattern Compatible :: a -> Compatible a
|
|
pattern Compatible a <- Compatible_ a
|
|
|
|
{-# COMPLETE Compatible #-}
|
|
|
|
isCompatible :: VersionI v a => a -> VersionRange v -> Bool
|
|
isCompatible x (VRange v1 v2) = let v = version x in v1 <= v && v <= v2
|
|
|
|
isCompatibleRange :: VersionRangeI v a => a -> VersionRange v -> Bool
|
|
isCompatibleRange x (VRange min2 max2) = min1 <= max2 && min2 <= max1
|
|
where
|
|
VRange min1 max1 = versionRange x
|
|
|
|
proveCompatible :: VersionI v a => a -> VersionRange v -> Maybe (Compatible a)
|
|
proveCompatible x vr = x `mkCompatibleIf` (x `isCompatible` vr)
|
|
|
|
compatibleVersion :: VersionRangeI v a => a -> VersionRange v -> Maybe (Compatible (VersionT v a))
|
|
compatibleVersion x vr =
|
|
toVersionT x (min max1 max2) `mkCompatibleIf` isCompatibleRange x vr
|
|
where
|
|
max1 = maxVersion $ versionRange x
|
|
max2 = maxVersion vr
|
|
|
|
-- | intersection of version ranges
|
|
compatibleVRange :: VersionRangeI v a => a -> VersionRange v -> Maybe (Compatible a)
|
|
compatibleVRange x vr =
|
|
compatibleVRange_ x (max min1 min2) (min max1 max2)
|
|
where
|
|
VRange min1 max1 = versionRange x
|
|
VRange min2 max2 = vr
|
|
|
|
-- | version range capped by compatible version
|
|
compatibleVRange' :: VersionRangeI v a => a -> Version v -> Maybe (Compatible a)
|
|
compatibleVRange' x v
|
|
| v <= max1 = compatibleVRange_ x min1 v
|
|
| otherwise = Nothing
|
|
where
|
|
VRange min1 max1 = versionRange x
|
|
|
|
compatibleVRange_ :: VersionRangeI v a => a -> Version v -> Version v -> Maybe (Compatible a)
|
|
compatibleVRange_ x v1 v2 = Compatible_ . toVersionRange x <$> safeVersionRange v1 v2
|
|
|
|
mkCompatibleIf :: a -> Bool -> Maybe (Compatible a)
|
|
x `mkCompatibleIf` cond = if cond then Just $ Compatible_ x else Nothing
|