Skip to content

Commit f2809fd

Browse files
authored
Merge pull request #1 from imprologic/feature/boilerplate
First release
2 parents f59a503 + 431e839 commit f2809fd

File tree

12 files changed

+2293
-0
lines changed

12 files changed

+2293
-0
lines changed

.github/workflows/dart.yaml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Dart CI
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
- uses: dart-lang/setup-dart@v1
16+
with:
17+
sdk: 'stable'
18+
19+
- name: Get dependencies
20+
run: dart pub get
21+
22+
- name: Run codegen (build_runner)
23+
run: dart run build_runner build --delete-conflicting-outputs
24+
25+
- name: Run tests
26+
run: dart test -r expanded

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,6 @@ doc/api/
2727

2828
.flutter-plugins
2929
.flutter-plugins-dependencies
30+
31+
# Mac stuff
32+
.DS_Store

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# JsonFeed Parser for Dart
2+
3+
A Dart library for parsing [JSON Feed](https://www.jsonfeed.org/version/1.1/) format feeds. This package provides strongly-typed models and utilities for working with JSON Feed data, including validation and error handling.
4+
5+
## Features
6+
7+
- Fully-typed models for JSON Feed, Item, Author, Attachment, and Hub (using [freezed](https://pub.dev/packages/freezed) and [json_serializable](https://pub.dev/packages/json_serializable))
8+
- Simple API for parsing JSON Feed strings
9+
- Custom exception handling for invalid feeds
10+
- Null safety and modern Dart support
11+
12+
## Getting Started
13+
14+
Add this package to your `pubspec.yaml`:
15+
16+
```yaml
17+
dependencies:
18+
jsonfeed:
19+
git:
20+
url: https://github.com/imprologic/json_feed.git
21+
```
22+
23+
Import and use in your Dart code:
24+
25+
```dart
26+
import 'package:jsonfeed/json_feed.dart';
27+
28+
void main() {
29+
final jsonString = '{ ... }'; // your JSON Feed string
30+
try {
31+
final feed = parseJsonFeed(jsonString);
32+
print(feed.title);
33+
} catch (e) {
34+
print('Failed to parse feed: $e');
35+
}
36+
}
37+
```
38+
39+
## Code Generation
40+
41+
This package uses code generation for immutable models. Run the following command to generate the necessary files:
42+
43+
```bash
44+
dart run build_runner watch -d
45+
```
46+
47+
## Running Tests
48+
49+
To run the unit tests:
50+
51+
```bash
52+
dart test
53+
```
54+
55+
## Project Structure
56+
57+
- `lib/json_feed.dart`: Main library entry point and parser
58+
- `lib/src/models.dart`: Data models for JSON Feed and related types
59+
- `test/json_feed_test.dart`: Unit tests
60+
61+
## License
62+
63+
See [LICENSE](LICENSE).
64+

analysis_options.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
analyzer:
3+
errors:
4+
invalid_annotation_target: ignore

example/main.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'package:json_feed/json_feed.dart';
2+
import 'dart:io';
3+
4+
void main() {
5+
final sample = File('example/sample_feed.json').readAsStringSync();
6+
final feed = parseJsonFeed(sample);
7+
print('Feed title: ${feed.title}');
8+
print('Item count: ${feed.items.length}');
9+
}

lib/json_feed.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
library json_feed;
2+
3+
export 'src/models.dart';
4+
5+
import 'dart:convert';
6+
7+
import 'src/models.dart';
8+
9+
class JsonFeedParseException implements Exception {
10+
final String message;
11+
JsonFeedParseException(this.message);
12+
@override
13+
String toString() => 'JsonFeedParseException: $message';
14+
}
15+
16+
JsonFeed parseJsonFeed(String jsonString) {
17+
final decoded = json.decode(jsonString);
18+
if (decoded is! Map<String, dynamic>)
19+
throw JsonFeedParseException('Top-level JSON must be an object.');
20+
21+
// validate minimal requirements per spec
22+
final version = decoded['version'];
23+
if (version is! String || version.isEmpty)
24+
throw JsonFeedParseException('Missing required "version" string.');
25+
final items = decoded['items'];
26+
if (items is! List)
27+
throw JsonFeedParseException('Missing required "items" array.');
28+
// authors fallback and extension extraction can be added here
29+
30+
// Use generated fromJson
31+
final feed = JsonFeed.fromJson(decoded);
32+
33+
return feed;
34+
}

lib/src/models.dart

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import 'package:freezed_annotation/freezed_annotation.dart';
2+
3+
part 'models.freezed.dart';
4+
part 'models.g.dart';
5+
6+
@freezed
7+
abstract class Author with _$Author {
8+
const factory Author({String? name, String? url, String? avatar}) = _Author;
9+
10+
factory Author.fromJson(Map<String, dynamic> json) => _$AuthorFromJson(json);
11+
}
12+
13+
@freezed
14+
abstract class Attachment with _$Attachment {
15+
const factory Attachment({
16+
required String url,
17+
@JsonKey(name: 'mime_type') required String mimeType,
18+
String? title,
19+
@JsonKey(name: 'size_in_bytes') int? sizeInBytes,
20+
@JsonKey(name: 'duration_in_seconds') double? durationInSeconds,
21+
}) = _Attachment;
22+
23+
factory Attachment.fromJson(Map<String, dynamic> json) =>
24+
_$AttachmentFromJson(json);
25+
}
26+
27+
@freezed
28+
abstract class Hub with _$Hub {
29+
const factory Hub({required String type, required String url}) = _Hub;
30+
31+
factory Hub.fromJson(Map<String, dynamic> json) => _$HubFromJson(json);
32+
}
33+
34+
@freezed
35+
abstract class Item with _$Item {
36+
const factory Item({
37+
required String id,
38+
String? url,
39+
@JsonKey(name: 'external_url') String? externalUrl,
40+
String? title,
41+
@JsonKey(name: 'content_html') String? contentHtml,
42+
@JsonKey(name: 'content_text') String? contentText,
43+
String? summary,
44+
String? image,
45+
@JsonKey(name: 'banner_image') String? bannerImage,
46+
@JsonKey(name: 'date_published') String? datePublished,
47+
@JsonKey(name: 'date_modified') String? dateModified,
48+
List<Author>? authors,
49+
List<String>? tags,
50+
String? language,
51+
List<Attachment>? attachments,
52+
}) = _Item;
53+
54+
factory Item.fromJson(Map<String, dynamic> json) => _$ItemFromJson(json);
55+
}
56+
57+
@freezed
58+
abstract class JsonFeed with _$JsonFeed {
59+
const factory JsonFeed({
60+
required String version,
61+
required String title,
62+
@JsonKey(name: 'home_page_url') String? homePageUrl,
63+
@JsonKey(name: 'feed_url') String? feedUrl,
64+
String? description,
65+
@JsonKey(name: 'user_comment') String? userComment,
66+
@JsonKey(name: 'next_url') String? nextUrl,
67+
String? icon,
68+
String? favicon,
69+
List<Author>? authors,
70+
String? language,
71+
bool? expired,
72+
List<Hub>? hubs,
73+
@Default([]) List<Item> items,
74+
}) = _JsonFeed;
75+
76+
factory JsonFeed.fromJson(Map<String, dynamic> json) =>
77+
_$JsonFeedFromJson(json);
78+
}

0 commit comments

Comments
 (0)