-
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathData.Foreign.purs
111 lines (90 loc) · 3.72 KB
/
Data.Foreign.purs
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
module Data.Foreign
( Foreign(..)
, ForeignParser(ForeignParser)
, parseForeign
, parseJSON
, ReadForeign
, read
, prop
) where
import Prelude
import Data.Array
import Data.Either
import Data.Maybe
import Data.Tuple
import Data.Traversable
foreign import data Foreign :: *
foreign import fromString
"function fromString (str) { \
\ try { \
\ return _ps.Data_Either.Right(JSON.parse(str)); \
\ } catch (e) { \
\ return _ps.Data_Either.Left(e.toString()); \
\ } \
\}" :: String -> Either String Foreign
foreign import readPrimType
"function readPrimType (typeName) { \
\ return function (value) { \
\ if (toString.call(value) == '[object ' + typeName + ']') { \
\ return _ps.Data_Either.Right(value);\
\ } \
\ return _ps.Data_Either.Left('Value is not a ' + typeName + ''); \
\ }; \
\}" :: forall a. String -> Foreign -> Either String a
foreign import readMaybeImpl
"function readMaybeImpl (value) { \
\ return value === undefined || value === null ? _ps.Data_Maybe.Nothing : _ps.Data_Maybe.Just(value); \
\}" :: forall a. Foreign -> Maybe Foreign
foreign import readPropImpl
"function readPropImpl (k) { \
\ return function (obj) { \
\ return _ps.Data_Either.Right(obj[k]);\
\ }; \
\}" :: forall a. String -> Foreign -> Either String Foreign
foreign import showForeignImpl
"var showForeignImpl = JSON.stringify;" :: Foreign -> String
instance showForeign :: Prelude.Show Foreign where
show = showForeignImpl
data ForeignParser a = ForeignParser (Foreign -> Either String a)
parseForeign :: forall a. ForeignParser a -> Foreign -> Either String a
parseForeign (ForeignParser p) x = p x
parseJSON :: forall a. (ReadForeign a) => String -> Either String a
parseJSON json = fromString json >>= parseForeign read
instance monadForeignParser :: Prelude.Monad ForeignParser where
return x = ForeignParser \_ -> Right x
(>>=) (ForeignParser p) f = ForeignParser \x -> case p x of
Left err -> Left err
Right x' -> parseForeign (f x') x
instance applicativeForeignParser :: Prelude.Applicative ForeignParser where
pure x = ForeignParser \_ -> Right x
(<*>) (ForeignParser f) (ForeignParser p) = ForeignParser \x -> case f x of
Left err -> Left err
Right f' -> f' <$> p x
instance functorForeignParser :: Prelude.Functor ForeignParser where
(<$>) f (ForeignParser p) = ForeignParser \x -> f <$> p x
class ReadForeign a where
read :: ForeignParser a
instance readString :: ReadForeign String where
read = ForeignParser $ readPrimType "String"
instance readNumber :: ReadForeign Number where
read = ForeignParser $ readPrimType "Number"
instance readBoolean :: ReadForeign Boolean where
read = ForeignParser $ readPrimType "Boolean"
instance readArray :: (ReadForeign a) => ReadForeign [a] where
read =
let arrayItem (Tuple i x) = case parseForeign read x of
Right result -> Right result
Left err -> Left $ "Error reading item at index " ++ (show i) ++ ":\n" ++ err
in
(ForeignParser $ readPrimType "Array") >>= \xs ->
ForeignParser \_ -> arrayItem `traverse` (zip (range 0 (length xs)) xs)
instance readMaybe :: (ReadForeign a) => ReadForeign (Maybe a) where
read = (ForeignParser $ Right <<< readMaybeImpl) >>= \x ->
ForeignParser \_ -> case x of
Just x' -> parseForeign read x' >>= return <<< Just
Nothing -> return Nothing
prop :: forall a. (ReadForeign a) => String -> ForeignParser a
prop p = (ForeignParser \x -> readPropImpl p x) >>= \x ->
ForeignParser \_ -> case parseForeign read x of
Right result -> Right result
Left err -> Left $ "Error reading property '" ++ p ++ "':\n" ++ err