{-# LANGUAGE OverloadedStrings #-}
module Postgres.Decoder
( run
, Table, table, table2, table3, table4, table5, table6
, Column, column
, Value, map, nullable, string, int, bool
) where
import qualified Database.PostgreSQL.LibPQ as PG
import qualified Prelude
import qualified Maybe
import qualified List
import qualified Debug
import qualified Dict
import qualified String
import qualified Task
import qualified Result
import qualified Http
import qualified Internal.Task
import qualified Internal.Shortcut as Shortcut
import qualified Prelude as P
import Cherry.Prelude
{-| -}
newtype Table a
= Table (PG.Result -> Task String (List a))
{-| -}
newtype Column a
= Column (PG.Result -> PG.Row -> Task String a)
{-| -}
newtype Value a
= Value (Maybe String -> Result String a)
-- RUN
{-| -}
run :: Table a -> PG.Result -> Task String (List a)
run (Table decoder) result =
decoder result
-- TABLES
{-| -}
table :: (a -> b) -> Column a -> Table b
table func (Column column1) =
Table <| \result ->
readRows result <| \row ->
Task.map func
(column1 result row)
{-| -}
table2 :: (a -> b -> c) -> Column a -> Column b -> Table c
table2 func (Column column1) (Column column2) =
Table <| \result ->
readRows result <| \row ->
Task.map2 func
(column1 result row)
(column2 result row)
{-| -}
table3 :: (a -> b -> c -> d) -> Column a -> Column b -> Column c -> Table d
table3 func (Column column1) (Column column2) (Column column3) =
Table <| \result ->
readRows result <| \row ->
Task.map3 func
(column1 result row)
(column2 result row)
(column3 result row)
{-| -}
table4 :: (a -> b -> c -> d -> e) -> Column a -> Column b -> Column c -> Column d -> Table e
table4 func (Column column1) (Column column2) (Column column3) (Column column4) =
Table <| \result ->
readRows result <| \row ->
Task.map4 func
(column1 result row)
(column2 result row)
(column3 result row)
(column4 result row)
{-| -}
table5 :: (a -> b -> c -> d -> e -> f) -> Column a -> Column b -> Column c -> Column d -> Column e -> Table f
table5 func (Column column1) (Column column2) (Column column3) (Column column4) (Column column5) =
Table <| \result ->
readRows result <| \row ->
Task.map5 func
(column1 result row)
(column2 result row)
(column3 result row)
(column4 result row)
(column5 result row)
{-| -}
table6 :: (a -> b -> c -> d -> e -> f -> g) -> Column a -> Column b -> Column c -> Column d -> Column e -> Column f -> Table g
table6 func (Column column1) (Column column2) (Column column3) (Column column4) (Column column5) (Column column6) =
Table <| \result ->
readRows result <| \row ->
Task.map6 func
(column1 result row)
(column2 result row)
(column3 result row)
(column4 result row)
(column5 result row)
(column6 result row)
-- COLUMN
{-| -}
column :: String -> Value a -> Column a
column name (Value decoder) =
Column <| \result row ->
columnNumber result name
|> Task.andThen (getValue result row)
|> Task.andThen (decoder >> Task.fromResult)
-- VALUE
{-| -}
map :: (a -> b) -> Value a -> Value b
map f (Value decoder) =
Value <| \string ->
Result.map f (decoder string)
{-| -}
nullable :: Value a -> Value (Maybe a)
nullable (Value decoder) =
Value <| \maybeValue ->
case maybeValue of
Nothing -> Result.Ok Nothing
Just _ -> Result.map Just (decoder maybeValue)
{-| -}
string :: Value String
string =
Value <| \maybeString ->
withNotNull maybeString <| \string ->
Result.Ok string
{-| -}
int :: Value Int
int =
Value <| \maybeString ->
withNotNull maybeString <| \string ->
String.toInt string
|> Result.fromMaybe "Value is not an int."
{-| -}
bool :: Value Bool
bool =
Value <| \maybeString ->
withNotNull maybeString <| \string ->
case String.toUpper string of
"T" -> Result.Ok True
"F" -> Result.Ok False
_ -> Result.Err "Value is not an boolean."
-- HELPERS
withNotNull :: Maybe String -> (String -> Result String a) -> Result String a
withNotNull maybeString func =
case maybeString of
Nothing ->
Result.Err "Illegal value: NULL"
Just string ->
func string
readRows :: PG.Result -> (PG.Row -> Task String a) -> Task String (List a)
readRows result decode =
let read done index total =
if PG.toRow index == total then
Task.sequence done
else
read (decode (PG.toRow index) : done) (index + 1) total
in
Task.andThen (read [] 0) (numberOfRows result)
numberOfRows :: PG.Result -> Task String PG.Row
numberOfRows result =
PG.ntuples result
|> Shortcut.map Result.Ok
|> Internal.Task.Task
columnNumber :: PG.Result -> String -> Task String PG.Column
columnNumber result name =
PG.fnumber result (String.toByteString name)
|> Shortcut.map (Maybe.fromHMaybe >> Result.fromMaybe "Could not find column.")
|> Internal.Task.Task
getValue :: PG.Result -> PG.Row -> PG.Column -> Task String (Maybe String)
getValue result row column =
Internal.Task.Task <| do
value <- PG.getvalue result row column
value
|> Maybe.fromHMaybe
|> Maybe.map String.fromByteString
|> Result.Ok
|> P.return