module Hasql.Connection.ServerVersion
  ( ServerVersion (..),
    toText,
    fromInt,
    minimum,

    -- * PQ operations
    load,
  )
where

import Hasql.Platform.Prelude hiding (minimum)
import Hasql.Pq qualified as Pq
import TextBuilder qualified

data ServerVersion = ServerVersion Int Int Int
  deriving stock (ServerVersion -> ServerVersion -> Bool
(ServerVersion -> ServerVersion -> Bool)
-> (ServerVersion -> ServerVersion -> Bool) -> Eq ServerVersion
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ServerVersion -> ServerVersion -> Bool
== :: ServerVersion -> ServerVersion -> Bool
$c/= :: ServerVersion -> ServerVersion -> Bool
/= :: ServerVersion -> ServerVersion -> Bool
Eq, Eq ServerVersion
Eq ServerVersion =>
(ServerVersion -> ServerVersion -> Ordering)
-> (ServerVersion -> ServerVersion -> Bool)
-> (ServerVersion -> ServerVersion -> Bool)
-> (ServerVersion -> ServerVersion -> Bool)
-> (ServerVersion -> ServerVersion -> Bool)
-> (ServerVersion -> ServerVersion -> ServerVersion)
-> (ServerVersion -> ServerVersion -> ServerVersion)
-> Ord ServerVersion
ServerVersion -> ServerVersion -> Bool
ServerVersion -> ServerVersion -> Ordering
ServerVersion -> ServerVersion -> ServerVersion
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: ServerVersion -> ServerVersion -> Ordering
compare :: ServerVersion -> ServerVersion -> Ordering
$c< :: ServerVersion -> ServerVersion -> Bool
< :: ServerVersion -> ServerVersion -> Bool
$c<= :: ServerVersion -> ServerVersion -> Bool
<= :: ServerVersion -> ServerVersion -> Bool
$c> :: ServerVersion -> ServerVersion -> Bool
> :: ServerVersion -> ServerVersion -> Bool
$c>= :: ServerVersion -> ServerVersion -> Bool
>= :: ServerVersion -> ServerVersion -> Bool
$cmax :: ServerVersion -> ServerVersion -> ServerVersion
max :: ServerVersion -> ServerVersion -> ServerVersion
$cmin :: ServerVersion -> ServerVersion -> ServerVersion
min :: ServerVersion -> ServerVersion -> ServerVersion
Ord, Int -> ServerVersion -> ShowS
[ServerVersion] -> ShowS
ServerVersion -> String
(Int -> ServerVersion -> ShowS)
-> (ServerVersion -> String)
-> ([ServerVersion] -> ShowS)
-> Show ServerVersion
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ServerVersion -> ShowS
showsPrec :: Int -> ServerVersion -> ShowS
$cshow :: ServerVersion -> String
show :: ServerVersion -> String
$cshowList :: [ServerVersion] -> ShowS
showList :: [ServerVersion] -> ShowS
Show)

-- |
-- >>> fromInt 10000
-- ServerVersion 1 0 0
--
-- >>> fromInt 100001
-- ServerVersion 10 1 0
--
-- >>> fromInt 110000
-- ServerVersion 11 0 0
--
-- >>> fromInt 90105
-- ServerVersion 9 1 5
--
-- >>> fromInt 90200
-- ServerVersion 9 2 0
--
-- Ref: https://www.postgresql.org/docs/18/libpq-status.html#LIBPQ-PQSERVERVERSION
fromInt :: Int -> ServerVersion
fromInt :: Int -> ServerVersion
fromInt Int
x =
  if Int
x Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
100_000
    then Int -> ServerVersion
fromPre10Int Int
x
    else Int -> ServerVersion
fromPost10Int Int
x

fromPost10Int :: Int -> ServerVersion
fromPost10Int :: Int -> ServerVersion
fromPost10Int Int
x =
  let (Int
major, Int
minor) = Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
divMod Int
x Int
10_000
   in Int -> Int -> Int -> ServerVersion
ServerVersion Int
major Int
minor Int
0

fromPre10Int :: Int -> ServerVersion
fromPre10Int :: Int -> ServerVersion
fromPre10Int = State Int ServerVersion -> Int -> ServerVersion
forall s a. State s a -> s -> a
evalState do
  Int
patch <- (Int -> (Int, Int)) -> StateT Int Identity Int
forall a. (Int -> (a, Int)) -> StateT Int Identity a
forall s (m :: * -> *) a. MonadState s m => (s -> (a, s)) -> m a
state ((Int, Int) -> (Int, Int)
forall a b. (a, b) -> (b, a)
swap ((Int, Int) -> (Int, Int))
-> (Int -> (Int, Int)) -> Int -> (Int, Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (Int -> Int -> (Int, Int)) -> Int -> Int -> (Int, Int)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
divMod Int
100)
  Int
minor <- (Int -> (Int, Int)) -> StateT Int Identity Int
forall a. (Int -> (a, Int)) -> StateT Int Identity a
forall s (m :: * -> *) a. MonadState s m => (s -> (a, s)) -> m a
state ((Int, Int) -> (Int, Int)
forall a b. (a, b) -> (b, a)
swap ((Int, Int) -> (Int, Int))
-> (Int -> (Int, Int)) -> Int -> (Int, Int)
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (Int -> Int -> (Int, Int)) -> Int -> Int -> (Int, Int)
forall a b c. (a -> b -> c) -> b -> a -> c
flip Int -> Int -> (Int, Int)
forall a. Integral a => a -> a -> (a, a)
divMod Int
100)
  Int
major <- StateT Int Identity Int
forall s (m :: * -> *). MonadState s m => m s
get
  pure (Int -> Int -> Int -> ServerVersion
ServerVersion Int
major Int
minor Int
patch)

toText :: ServerVersion -> Text
toText :: ServerVersion -> Text
toText (ServerVersion Int
major Int
minor Int
patch) =
  (TextBuilder -> Text
TextBuilder.toText (TextBuilder -> Text)
-> ([TextBuilder] -> TextBuilder) -> [TextBuilder] -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. [TextBuilder] -> TextBuilder
forall a. Monoid a => [a] -> a
mconcat)
    [ Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
major,
      TextBuilder
".",
      Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
minor,
      TextBuilder
".",
      Int -> TextBuilder
forall a. Integral a => a -> TextBuilder
TextBuilder.decimal Int
patch
    ]

-- | Minimum supported version.
minimum :: ServerVersion
minimum :: ServerVersion
minimum = Int -> Int -> Int -> ServerVersion
ServerVersion Int
9 Int
0 Int
0

-- | Load from PQ connection.
load :: Pq.Connection -> IO ServerVersion
load :: Connection -> IO ServerVersion
load Connection
connection =
  Int -> ServerVersion
fromInt (Int -> ServerVersion) -> IO Int -> IO ServerVersion
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Connection -> IO Int
Pq.serverVersion Connection
connection