tangled
alpha
login
or
join now
anil.recoil.org
/
ocaml-jsonfeed
3
fork
atom
OCaml library for JSONfeed parsing and creation
3
fork
atom
overview
issues
pulls
pipelines
switch error types
anil.recoil.org
4 months ago
c8429dac
fe1e6e0b
+20
-28
6 changed files
expand all
collapse all
unified
split
README.md
example
feed_parser.ml
feed_validator.ml
lib
jsonfeed.ml
jsonfeed.mli
test
test_jsonfeed.ml
+1
-1
README.md
···
67
67
| Some title -> Printf.printf "- %s\n" title
68
68
| None -> ()
69
69
) (Jsonfeed.items feed)
70
70
-
| Error (`Msg err) ->
70
70
+
| Error err ->
71
71
Printf.eprintf "Parse error: %s\n" err
72
72
73
73
(* Parse from file *)
+2
-2
example/feed_parser.ml
···
182
182
Format.printf "✓ Round-trip successful: feeds are equal\n"
183
183
else
184
184
Format.printf "✗ Round-trip failed: feeds differ\n"
185
185
-
| Error (`Msg err) ->
185
185
+
| Error err ->
186
186
Format.eprintf "✗ Round-trip failed: %s\n" err)
187
187
-
| Error (`Msg err) ->
187
187
+
| Error err ->
188
188
Format.eprintf "Error parsing feed: %s\n" err
189
189
with
190
190
| Sys_error msg ->
+3
-3
example/feed_validator.ml
···
256
256
257
257
(match Jsonfeed.of_string invalid_json1 with
258
258
| Ok _ -> Format.printf "✗ Should have failed (missing version)\n"
259
259
-
| Error (`Msg err) ->
259
259
+
| Error err ->
260
260
Format.printf "✓ Correctly rejected invalid feed: %s\n" err);
261
261
262
262
(* Missing required title field *)
···
267
267
268
268
(match Jsonfeed.of_string invalid_json2 with
269
269
| Ok _ -> Format.printf "✗ Should have failed (missing title)\n"
270
270
-
| Error (`Msg err) ->
270
270
+
| Error err ->
271
271
Format.printf "✓ Correctly rejected invalid feed: %s\n" err);
272
272
273
273
(* Item without id *)
···
281
281
282
282
(match Jsonfeed.of_string invalid_json3 with
283
283
| Ok _ -> Format.printf "✗ Should have failed (item without id)\n"
284
284
-
| Error (`Msg err) ->
284
284
+
| Error err ->
285
285
Format.printf "✓ Correctly rejected invalid feed: %s\n" err);
286
286
287
287
Format.printf "\n"
+2
-3
lib/jsonfeed.ml
···
72
72
73
73
(* JSON parsing and serialization *)
74
74
75
75
-
type error = [ `Msg of string ]
75
75
+
type error = string
76
76
77
77
-
let error_msgf fmt = Format.kasprintf (fun s -> Error (`Msg s)) fmt
77
77
+
let error_msgf fmt = Format.kasprintf (fun s -> Error s) fmt
78
78
79
79
(* JSON parsing helpers *)
80
80
···
350
350
(* JSON serialization *)
351
351
352
352
let to_jsonm enc feed =
353
353
-
(* Simplified serialization using Jsonm *)
354
353
let enc_field name value_fn =
355
354
ignore (Jsonm.encode enc (`Lexeme (`Name name)));
356
355
value_fn ()
+8
-15
lib/jsonfeed.mli
···
4
4
type-safe parsing and serialization of JSON Feed documents. JSON Feed is a
5
5
syndication format similar to RSS and Atom, but using JSON instead of XML.
6
6
7
7
-
{b Key Features:}
8
8
-
- Type-safe construction with compile-time validation
9
9
-
- Support for all JSON Feed 1.1 fields
10
10
-
- RFC 3339 date parsing with Ptime integration
11
11
-
- Streaming parsing and serialization with Jsonm
12
12
-
- Comprehensive documentation and examples
13
13
-
14
7
{b Quick Start:}
15
8
{[
16
9
(* Create a simple feed *)
···
33
26
(* Parse from string *)
34
27
match Jsonfeed.of_string json with
35
28
| Ok feed -> Printf.printf "Feed: %s\n" (Jsonfeed.title feed)
36
36
-
| Error (`Msg err) -> Printf.eprintf "Error: %s\n" err
29
29
+
| Error err -> Printf.eprintf "Error: %s\n" err
37
30
]}
38
31
39
32
@see <https://www.jsonfeed.org/version/1.1/> JSON Feed Specification *)
···
195
188
(** {1 Parsing and Serialization} *)
196
189
197
190
(** Error type for parsing operations. *)
198
198
-
type error = [ `Msg of string ]
191
191
+
type error = string
199
192
200
193
(** [of_jsonm decoder] parses a JSON Feed from a Jsonm decoder.
201
194
···
203
196
with streaming JSON processing pipelines.
204
197
205
198
@param decoder A Jsonm decoder positioned at the start of a JSON Feed document
206
206
-
@return [Ok feed] on success, [Error (`Msg err)] on parse error
199
199
+
@return [Ok feed] on success, [Error err] on parse error
207
200
208
201
{b Example:}
209
202
{[
210
203
let decoder = Jsonm.decoder (`String json_string) in
211
204
match Jsonfeed.of_jsonm decoder with
212
205
| Ok feed -> (* process feed *)
213
213
-
| Error (`Msg err) -> (* handle error *)
206
206
+
| Error err -> (* handle error *)
214
207
]} *)
215
215
-
val of_jsonm : Jsonm.decoder -> (t, [> error]) result
208
208
+
val of_jsonm : Jsonm.decoder -> (t, error) result
216
209
217
210
(** [to_jsonm encoder feed] serializes a JSON Feed to a Jsonm encoder.
218
211
···
234
227
(** [of_string s] parses a JSON Feed from a string.
235
228
236
229
@param s A JSON string containing a JSON Feed document
237
237
-
@return [Ok feed] on success, [Error (`Msg err)] on parse error
230
230
+
@return [Ok feed] on success, [Error err] on parse error
238
231
239
232
{b Example:}
240
233
{[
···
245
238
}|} in
246
239
match Jsonfeed.of_string json with
247
240
| Ok feed -> Printf.printf "Parsed: %s\n" (Jsonfeed.title feed)
248
248
-
| Error (`Msg err) -> Printf.eprintf "Error: %s\n" err
241
241
+
| Error err -> Printf.eprintf "Error: %s\n" err
249
242
]} *)
250
250
-
val of_string : string -> (t, [> error]) result
243
243
+
val of_string : string -> (t, error) result
251
244
252
245
(** [to_string ?minify feed] serializes a JSON Feed to a string.
253
246
+4
-4
test/test_jsonfeed.ml
···
235
235
| Ok feed ->
236
236
Alcotest.(check string) "title" "Test Feed" (Jsonfeed.title feed);
237
237
Alcotest.(check int) "items" 0 (List.length (Jsonfeed.items feed))
238
238
-
| Error (`Msg err) ->
238
238
+
| Error err ->
239
239
Alcotest.fail (Printf.sprintf "Parse failed: %s" err)
240
240
241
241
let test_feed_parse_with_item () =
···
258
258
Alcotest.(check string) "item id" "https://example.com/1" (Item.id item);
259
259
Alcotest.(check (option string)) "content_html" (Some "<p>Hello</p>") (Item.content_html item)
260
260
| _ -> Alcotest.fail "Expected 1 item")
261
261
-
| Error (`Msg err) ->
261
261
+
| Error err ->
262
262
Alcotest.fail (Printf.sprintf "Parse failed: %s" err)
263
263
264
264
let test_feed_roundtrip () =
···
288
288
Alcotest.(check int) "items count"
289
289
(List.length (Jsonfeed.items feed1))
290
290
(List.length (Jsonfeed.items feed2))
291
291
-
| Error (`Msg err) ->
291
291
+
| Error err ->
292
292
Alcotest.fail (Printf.sprintf "Round-trip failed: %s" err)
293
293
294
294
let test_feed_parse_invalid_missing_content () =
···
303
303
}|} in
304
304
match Jsonfeed.of_string json with
305
305
| Ok _ -> Alcotest.fail "Should reject item without content"
306
306
-
| Error (`Msg err) ->
306
306
+
| Error err ->
307
307
Alcotest.(check bool) "has error" true
308
308
(contains_substring err "content")
309
309