Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Commit 01feced

Browse files
garybcryogenian
authored andcommitted
Use proper types for date/time values (#15)
* Use proper types for date/time values * Fix JSON codec * Fix syntax parsing
1 parent 3f71847 commit 01feced

File tree

7 files changed

+254
-164
lines changed

7 files changed

+254
-164
lines changed

bower.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,15 @@
1515
"package.json"
1616
],
1717
"dependencies": {
18+
"purescript-argonaut": "^2.0.0",
1819
"purescript-bifunctors": "^2.0.0",
20+
"purescript-datetime": "^2.1.1",
1921
"purescript-maps": "^2.0.0",
2022
"purescript-matryoshka": "^0.2.0",
2123
"purescript-newtype": "^1.2.0",
2224
"purescript-parsing": "^3.0.0",
2325
"purescript-precise": "^1.0.0",
2426
"purescript-profunctor-lenses": "^2.4.0",
25-
"purescript-strongcheck": "^2.0.0",
26-
"purescript-argonaut": "^2.0.0"
27+
"purescript-strongcheck": "^2.0.0"
2728
}
2829
}

src/Data/Json/Extended.purs

+32-34
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import Control.Lazy as Lazy
4949
import Data.Argonaut as JS
5050
import Data.Array as A
5151
import Data.Bitraversable (bitraverse)
52+
import Data.DateTime as DT
5253
import Data.Either as E
5354
import Data.Functor.Mu as Mu
5455
import Data.HugeNum as HN
@@ -61,7 +62,7 @@ import Data.StrMap as SM
6162
import Data.Traversable (for)
6263
import Data.Tuple as T
6364

64-
import Matryoshka (embed, project, cata, anaM)
65+
import Matryoshka (class Corecursive, class Recursive, anaM, cata, embed, project)
6566

6667
import Test.StrongCheck.Arbitrary as SC
6768
import Test.StrongCheck.Gen as Gen
@@ -71,11 +72,10 @@ import Data.Json.Extended.Signature hiding (getType) as Exports
7172

7273
type EJson = Mu.Mu Sig.EJsonF
7374

74-
75-
decodeEJson JS.Json E.Either String EJson
75+
decodeEJson :: forall t. Corecursive t Sig.EJsonF JS.Json E.Either String t
7676
decodeEJson = anaM Sig.decodeJsonEJsonF
7777

78-
encodeEJson EJson JS.Json
78+
encodeEJson :: forall t. Recursive t Sig.EJsonF t -> JS.Json
7979
encodeEJson = cata Sig.encodeJsonEJsonF
8080

8181
arbitraryEJsonOfSize
@@ -105,120 +105,118 @@ renderEJson ∷ EJson → String
105105
renderEJson =
106106
cata Sig.renderEJsonF
107107

108-
109108
-- | A closed parser of SQL^2 constant expressions
110-
parseEJson m. (Monad m) P.ParserT String m EJson
109+
parseEJson m. Monad m P.ParserT String m EJson
111110
parseEJson =
112111
Lazy.fix \f →
113112
embed <$>
114113
Sig.parseEJsonF f
115114

116-
117-
null EJson
115+
null t. Corecursive t Sig.EJsonF t
118116
null = embed Sig.Null
119117

120-
boolean Boolean EJson
118+
boolean t. Corecursive t Sig.EJsonF Boolean t
121119
boolean = embed <<< Sig.Boolean
122120

123-
integer Int EJson
121+
integer t. Corecursive t Sig.EJsonF Int t
124122
integer = embed <<< Sig.Integer
125123

126-
decimal HN.HugeNum EJson
124+
decimal t. Corecursive t Sig.EJsonF HN.HugeNum t
127125
decimal = embed <<< Sig.Decimal
128126

129-
string String EJson
127+
string t. Corecursive t Sig.EJsonF String t
130128
string = embed <<< Sig.String
131129

132-
timestamp String EJson
130+
timestamp t. Corecursive t Sig.EJsonF DT.DateTime t
133131
timestamp = embed <<< Sig.Timestamp
134132

135-
date String EJson
133+
date t. Corecursive t Sig.EJsonF DT.Date t
136134
date = embed <<< Sig.Date
137135

138-
time String EJson
136+
time t. Corecursive t Sig.EJsonF DT.Time t
139137
time = embed <<< Sig.Time
140138

141-
interval String EJson
139+
interval t. Corecursive t Sig.EJsonF String t
142140
interval = embed <<< Sig.Interval
143141

144-
objectId String EJson
142+
objectId t. Corecursive t Sig.EJsonF String t
145143
objectId = embed <<< Sig.ObjectId
146144

147-
array Array EJson EJson
145+
array t. Corecursive t Sig.EJsonF Array t t
148146
array = embed <<< Sig.Array
149147

150-
map Map.Map EJson EJson EJson
148+
map t. Corecursive t Sig.EJsonF Map.Map t t t
151149
map = embed <<< Sig.Map <<< A.fromFoldable <<< Map.toList
152150

153-
map' SM.StrMap EJson EJson
151+
map' t. Corecursive t Sig.EJsonF SM.StrMap t t
154152
map' = embed <<< Sig.Map <<< F.map go <<< A.fromFoldable <<< SM.toList
155153
where
156154
go (T.Tuple a b) = T.Tuple (string a) b
157155

158-
getType EJson EJsonType
156+
getType t. Recursive t Sig.EJsonF t EJsonType
159157
getType = Sig.getType <<< project
160158

161-
_Null Prism' EJson Unit
159+
_Null t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t Unit
162160
_Null = prism' (const null) $ project >>> case _ of
163161
Sig.NullM.Just unit
164162
_ → M.Nothing
165163

166-
_String Prism' EJson String
164+
_String t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t String
167165
_String = prism' string $ project >>> case _ of
168166
Sig.String s → M.Just s
169167
_ → M.Nothing
170168

171-
_Boolean Prism' EJson Boolean
169+
_Boolean t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t Boolean
172170
_Boolean = prism' boolean $ project >>> case _ of
173171
Sig.Boolean b → M.Just b
174172
_ → M.Nothing
175173

176-
_Integer Prism' EJson Int
174+
_Integer t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t Int
177175
_Integer = prism' integer $ project >>> case _ of
178176
Sig.Integer i → M.Just i
179177
_ → M.Nothing
180178

181-
_Decimal Prism' EJson HN.HugeNum
179+
_Decimal t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t HN.HugeNum
182180
_Decimal = prism' decimal $ project >>> case _ of
183181
Sig.Decimal d → M.Just d
184182
_ → M.Nothing
185183

186-
_Timestamp Prism' EJson String
184+
_Timestamp t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t DT.DateTime
187185
_Timestamp = prism' timestamp $ project >>> case _ of
188186
Sig.Timestamp t → M.Just t
189187
_ → M.Nothing
190188

191-
_Date Prism' EJson String
189+
_Date t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t DT.Date
192190
_Date = prism' date $ project >>> case _ of
193191
Sig.Date d → M.Just d
194192
_ → M.Nothing
195193

196-
_Time Prism' EJson String
194+
_Time t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t DT.Time
197195
_Time = prism' time $ project >>> case _ of
198196
Sig.Time t → M.Just t
199197
_ → M.Nothing
200198

201-
_Interval Prism' EJson String
199+
_Interval t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t String
202200
_Interval = prism' interval $ project >>> case _ of
203201
Sig.Interval i → M.Just i
204202
_ → M.Nothing
205203

206-
_ObjectId Prism' EJson String
204+
_ObjectId t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t String
207205
_ObjectId = prism' objectId $ project >>> case _ of
208206
Sig.ObjectId id → M.Just id
209207
_ → M.Nothing
210208

211-
_Array Prism' EJson (Array EJson)
209+
_Array t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t (Array t)
212210
_Array = prism' array $ project >>> case _ of
213211
Sig.Array xs → M.Just xs
214212
_ → M.Nothing
215213

216-
_Map Prism' EJson (Map.Map EJson EJson)
214+
_Map t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF, Ord t) Prism' t (Map.Map t t)
217215
_Map = prism' map $ project >>> case _ of
218216
Sig.Map kvs → M.Just $ Map.fromFoldable kvs
219217
_ → M.Nothing
220218

221-
_Map' Prism' EJson (SM.StrMap EJson)
219+
_Map' t. (Corecursive t Sig.EJsonF, Recursive t Sig.EJsonF) Prism' t (SM.StrMap t)
222220
_Map' = prism' map' $ project >>> case _ of
223221
Sig.Map kvs → SM.fromFoldable <$> for kvs (bitraverse (preview _String) pure)
224222
_ → M.Nothing

src/Data/Json/Extended/Signature/Core.purs

+5-4
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ module Data.Json.Extended.Signature.Core
66
import Prelude
77

88
import Data.Bifunctor as BF
9+
import Data.DateTime as DT
910
import Data.Eq (class Eq1, eq1)
1011
import Data.Foldable as F
11-
import Data.Traversable as T
1212
import Data.HugeNum as HN
1313
import Data.Int as Int
1414
import Data.Json.Extended.Type as JT
1515
import Data.List as L
1616
import Data.Monoid (mempty)
1717
import Data.Ord (class Ord1)
18+
import Data.Traversable as T
1819
import Data.Tuple (Tuple(..))
1920
import Data.TacitString (TacitString)
2021

@@ -25,9 +26,9 @@ data EJsonF a
2526
| Boolean Boolean
2627
| Integer Int
2728
| Decimal HN.HugeNum
28-
| Timestamp String
29-
| Date String
30-
| Time String
29+
| Timestamp DT.DateTime
30+
| Date DT.Date
31+
| Time DT.Time
3132
| Interval String
3233
| ObjectId String
3334
| Array (Array a)

src/Data/Json/Extended/Signature/Gen.purs

+31-4
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ module Data.Json.Extended.Signature.Gen
77
import Prelude
88

99
import Data.Array as A
10+
import Data.DateTime as DT
11+
import Data.Enum (toEnum)
12+
import Data.HugeNum as HN
1013
import Data.Json.Extended.Signature.Core (EJsonF(..))
14+
import Data.Maybe (fromMaybe)
1115
import Data.Tuple as T
12-
import Data.HugeNum as HN
1316

1417
import Test.StrongCheck.Arbitrary as SC
1518
import Test.StrongCheck.Gen as Gen
@@ -21,9 +24,9 @@ arbitraryBaseEJsonF =
2124
, Integer <$> SC.arbitrary
2225
, Decimal <$> arbitraryDecimal
2326
, String <$> SC.arbitrary
24-
, Timestamp <$> SC.arbitrary
25-
, Date <$> SC.arbitrary
26-
, Time <$> SC.arbitrary
27+
, Timestamp <$> arbitraryDateTime
28+
, Date <$> arbitraryDate
29+
, Time <$> arbitraryTime
2730
, Interval <$> SC.arbitrary
2831
, ObjectId <$> SC.arbitrary
2932
, pure Null
@@ -73,3 +76,27 @@ arbitraryDecimal ∷ Gen.Gen HN.HugeNum
7376
arbitraryDecimal =
7477
HN.fromNumber
7578
<$> SC.arbitrary
79+
80+
arbitraryDateTime Gen.Gen DT.DateTime
81+
arbitraryDateTime = DT.DateTime <$> arbitraryDate <*> arbitraryTime
82+
83+
arbitraryDate Gen.Gen DT.Date
84+
arbitraryDate = do
85+
year ← Gen.chooseInt 1950 2050
86+
month ← Gen.chooseInt 1 12
87+
day ← Gen.chooseInt 1 31
88+
pure $ DT.canonicalDate
89+
(fromMaybe bottom (toEnum year))
90+
(fromMaybe bottom (toEnum month))
91+
(fromMaybe bottom (toEnum day))
92+
93+
arbitraryTime Gen.Gen DT.Time
94+
arbitraryTime = do
95+
hour ← Gen.chooseInt 0 23
96+
minute ← Gen.chooseInt 0 59
97+
second ← Gen.chooseInt 0 59
98+
pure $ DT.Time
99+
(fromMaybe bottom (toEnum hour))
100+
(fromMaybe bottom (toEnum minute))
101+
(fromMaybe bottom (toEnum second))
102+
bottom

src/Data/Json/Extended/Signature/Json.purs

+29-13
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@ import Prelude
44

55
import Control.Alt ((<|>))
66

7-
import Data.Bifunctor (lmap)
87
import Data.Argonaut.Core as JS
98
import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.?))
109
import Data.Argonaut.Encode (encodeJson)
1110
import Data.Array as A
11+
import Data.Bifunctor (lmap)
12+
import Data.DateTime as DT
1213
import Data.Either as E
1314
import Data.HugeNum as HN
1415
import Data.Int as Int
1516
import Data.Json.Extended.Signature.Core (EJsonF(..))
17+
import Data.Json.Extended.Signature.Parse (parseDate, parseTime, parseTimestamp)
18+
import Data.Json.Extended.Signature.Render (renderDate, renderTime, renderTimestamp)
1619
import Data.Maybe as M
1720
import Data.StrMap as SM
1821
import Data.Traversable as TR
1922
import Data.Tuple as T
23+
import Text.Parsing.Parser as P
2024

2125
import Matryoshka (Algebra, CoalgebraM)
2226

@@ -27,9 +31,9 @@ encodeJsonEJsonF = case _ of
2731
Integer i → encodeJson i
2832
Decimal a → encodeJson $ HN.toNumber a
2933
String str → encodeJson str
30-
Timestamp strJS.jsonSingletonObject "$timestamp" $ encodeJson str
31-
Time strJS.jsonSingletonObject "$time" $ encodeJson str
32-
Date strJS.jsonSingletonObject "$date" $ encodeJson str
34+
Timestamp dtJS.jsonSingletonObject "$timestamp" $ encodeJson $ renderTimestamp dt
35+
Time tJS.jsonSingletonObject "$time" $ encodeJson $ renderTime t
36+
Date dJS.jsonSingletonObject "$date" $ encodeJson $ renderDate d
3337
Interval str → JS.jsonSingletonObject "$interval" $ encodeJson str
3438
ObjectId str → JS.jsonSingletonObject "$oid" $ encodeJson str
3539
Array xs → encodeJson xs
@@ -72,11 +76,11 @@ decodeJsonEJsonF =
7276
E.Either String (EJsonF JS.Json)
7377
decodeObject obj =
7478
unwrapBranch "$obj" strMapObject obj
75-
<|> unwrapLeaf "$timestamp" Timestamp obj
76-
<|> unwrapLeaf "$date" Date obj
77-
<|> unwrapLeaf "$time" Time obj
78-
<|> unwrapLeaf "$interval" Interval obj
79-
<|> unwrapLeaf "$oid" ObjectId obj
79+
<|> unwrapLeaf "$timestamp" decodeTimestamp Timestamp obj
80+
<|> unwrapLeaf "$date" decodeDate Date obj
81+
<|> unwrapLeaf "$time" decodeTime Time obj
82+
<|> unwrapLeaf "$interval" decodeJson Interval obj
83+
<|> unwrapLeaf "$oid" decodeJson ObjectId obj
8084
<|> unwrapNull obj
8185
<|> (pure $ strMapObject obj)
8286

@@ -112,14 +116,14 @@ decodeJsonEJsonF =
112116

113117
unwrapLeaf
114118
b
115-
. (DecodeJson b)
116-
String
119+
. String
120+
(JS.Json E.Either String b)
117121
(b EJsonF JS.Json)
118122
JS.JObject
119123
E.Either String (EJsonF JS.Json)
120-
unwrapLeaf key codec =
124+
unwrapLeaf key decode codec =
121125
getOnlyKey key
122-
>=> decodeJson
126+
>=> decode
123127
>>> map codec
124128

125129
getOnlyKey
@@ -131,3 +135,15 @@ decodeJsonEJsonF =
131135
obj .? key
132136
keys →
133137
E.Left $ "Expected '" <> key <> "' to be the only key, but found: " <> show keys
138+
139+
decodeTimestamp JS.Json E.Either String DT.DateTime
140+
decodeTimestamp = decodeJson >=> \val →
141+
lmap show $ P.runParser val parseTimestamp
142+
143+
decodeDate JS.Json E.Either String DT.Date
144+
decodeDate = decodeJson >=> \val →
145+
lmap show $ P.runParser val parseDate
146+
147+
decodeTime JS.Json E.Either String DT.Time
148+
decodeTime = decodeJson >=> \val →
149+
lmap show $ P.runParser val parseTime

0 commit comments

Comments
 (0)