feedparser-rs 0.5.1__tar.gz → 0.5.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/Cargo.lock +11 -11
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/Cargo.toml +1 -1
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/PKG-INFO +1 -1
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/limits.rs +48 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/dublin_core.rs +11 -3
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/georss.rs +240 -18
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/atom.rs +160 -38
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/common.rs +187 -24
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/json.rs +188 -2
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/rss.rs +2248 -1296
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/rss10.rs +109 -59
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/common.rs +6 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/mod.rs +5 -3
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/podcast.rs +269 -0
- feedparser_rs-0.5.2/crates/feedparser-rs-core/tests/fixtures/rss10/rss10_channel_selfclosing_refs.xml +26 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_georss_cc_namespaces.rs +51 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_rss10.rs +25 -1
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/lib.rs +2 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/limits.rs +15 -9
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/common.rs +1 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/geo.rs +21 -3
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/podcast.rs +126 -3
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/pyproject.toml +1 -1
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/README.md +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/Cargo.toml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/README.md +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/benches/parsing.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/benches/types.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/error_handling.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/feeds/malformed_feed.xml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/feeds/sample_atom.xml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/feeds/sample_podcast.xml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/feeds/sample_rss.xml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/parse_file.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/parse_url.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/podcast_feed.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/examples/profile_memory.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/compat/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/error.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/http/client.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/http/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/http/response.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/http/validation.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/lib.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/cc.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/content.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/media_rss.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/slash.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/syndication.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/threading.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/options.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/detect.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/parser/namespace_detection.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/entry.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/feed.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/generics.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/thread.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/types/version.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/base_url.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/date.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/encoding.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/sanitize.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/util/text.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/encoding_integration.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/guid_entity_tests.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/http_integration.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/integration_tests.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/issue45_test.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/namespace_edge_cases.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/namespace_integration.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_adversarial_input.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_author_detail.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_entry_author_precedence.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_media_namespace.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_parser_limits.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_podcast_namespace.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_rss_optional_elements.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_slash_wfw_namespaces.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_thr_namespace.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_threading_namespace.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_timezone_preservation.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_truncated_feeds.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_url_resolution.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/tests/test_url_security.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/Cargo.toml +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/README.md +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/error.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/compat.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/datetime.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/entry.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/feed_meta.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/media.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/mod.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/parsed_feed.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/syndication.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/src/types/thread.rs +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_basic.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_bindings.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_compat.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_dict_methods.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_guid_entities.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_integration.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_itunes_flat_keys.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_subscriptable.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/tests/test_syndication.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-py/uv.lock +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/python/feedparser_rs/__init__.py +0 -0
- {feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/python/feedparser_rs/py.typed +0 -0
|
@@ -500,9 +500,9 @@ dependencies = [
|
|
|
500
500
|
|
|
501
501
|
[[package]]
|
|
502
502
|
name = "ctor"
|
|
503
|
-
version = "0.
|
|
503
|
+
version = "0.8.0"
|
|
504
504
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
505
|
-
checksum = "
|
|
505
|
+
checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98"
|
|
506
506
|
dependencies = [
|
|
507
507
|
"ctor-proc-macro",
|
|
508
508
|
"dtor",
|
|
@@ -558,9 +558,9 @@ dependencies = [
|
|
|
558
558
|
|
|
559
559
|
[[package]]
|
|
560
560
|
name = "dtor"
|
|
561
|
-
version = "0.
|
|
561
|
+
version = "0.3.0"
|
|
562
562
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
563
|
-
checksum = "
|
|
563
|
+
checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4"
|
|
564
564
|
dependencies = [
|
|
565
565
|
"dtor-proc-macro",
|
|
566
566
|
]
|
|
@@ -600,7 +600,7 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
|
600
600
|
|
|
601
601
|
[[package]]
|
|
602
602
|
name = "feedparser-rs"
|
|
603
|
-
version = "0.5.
|
|
603
|
+
version = "0.5.2"
|
|
604
604
|
dependencies = [
|
|
605
605
|
"ammonia",
|
|
606
606
|
"chrono",
|
|
@@ -623,7 +623,7 @@ dependencies = [
|
|
|
623
623
|
|
|
624
624
|
[[package]]
|
|
625
625
|
name = "feedparser-rs-node"
|
|
626
|
-
version = "0.5.
|
|
626
|
+
version = "0.5.2"
|
|
627
627
|
dependencies = [
|
|
628
628
|
"feedparser-rs",
|
|
629
629
|
"napi",
|
|
@@ -633,7 +633,7 @@ dependencies = [
|
|
|
633
633
|
|
|
634
634
|
[[package]]
|
|
635
635
|
name = "feedparser-rs-py"
|
|
636
|
-
version = "0.5.
|
|
636
|
+
version = "0.5.2"
|
|
637
637
|
dependencies = [
|
|
638
638
|
"chrono",
|
|
639
639
|
"feedparser-rs",
|
|
@@ -1330,9 +1330,9 @@ dependencies = [
|
|
|
1330
1330
|
|
|
1331
1331
|
[[package]]
|
|
1332
1332
|
name = "napi"
|
|
1333
|
-
version = "3.8.
|
|
1333
|
+
version = "3.8.4"
|
|
1334
1334
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1335
|
-
checksum = "
|
|
1335
|
+
checksum = "fb7848c221fb7bb789e02f01875287ebb1e078b92a6566a34de01ef8806e7c2b"
|
|
1336
1336
|
dependencies = [
|
|
1337
1337
|
"anyhow",
|
|
1338
1338
|
"bitflags",
|
|
@@ -1352,9 +1352,9 @@ checksum = "d376940fd5b723c6893cd1ee3f33abbfd86acb1cd1ec079f3ab04a2a3bc4d3b1"
|
|
|
1352
1352
|
|
|
1353
1353
|
[[package]]
|
|
1354
1354
|
name = "napi-derive"
|
|
1355
|
-
version = "3.5.
|
|
1355
|
+
version = "3.5.3"
|
|
1356
1356
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
1357
|
-
checksum = "
|
|
1357
|
+
checksum = "60867ff9a6f76e82350e0c3420cb0736f5866091b61d7d8a024baa54b0ec17dd"
|
|
1358
1358
|
dependencies = [
|
|
1359
1359
|
"convert_case",
|
|
1360
1360
|
"ctor",
|
|
@@ -143,6 +143,36 @@ pub struct ParserLimits {
|
|
|
143
143
|
///
|
|
144
144
|
/// Default: 20 recipients
|
|
145
145
|
pub max_value_recipients: usize,
|
|
146
|
+
|
|
147
|
+
/// Maximum number of alternate enclosures per entry
|
|
148
|
+
///
|
|
149
|
+
/// Default: 20
|
|
150
|
+
pub max_podcast_alternate_enclosures: usize,
|
|
151
|
+
|
|
152
|
+
/// Maximum number of sources per alternate enclosure
|
|
153
|
+
///
|
|
154
|
+
/// Default: 10
|
|
155
|
+
pub max_podcast_alternate_enclosure_sources: usize,
|
|
156
|
+
|
|
157
|
+
/// Maximum number of podroll entries per feed
|
|
158
|
+
///
|
|
159
|
+
/// Default: 50
|
|
160
|
+
pub max_podcast_podroll: usize,
|
|
161
|
+
|
|
162
|
+
/// Maximum number of socialInteract elements per entry
|
|
163
|
+
///
|
|
164
|
+
/// Default: 20
|
|
165
|
+
pub max_podcast_social_interact: usize,
|
|
166
|
+
|
|
167
|
+
/// Maximum number of txt records per feed or entry
|
|
168
|
+
///
|
|
169
|
+
/// Default: 20
|
|
170
|
+
pub max_podcast_txt: usize,
|
|
171
|
+
|
|
172
|
+
/// Maximum number of follow links per feed or entry
|
|
173
|
+
///
|
|
174
|
+
/// Default: 20
|
|
175
|
+
pub max_podcast_follow: usize,
|
|
146
176
|
}
|
|
147
177
|
|
|
148
178
|
impl Default for ParserLimits {
|
|
@@ -170,6 +200,12 @@ impl Default for ParserLimits {
|
|
|
170
200
|
max_podcast_funding: 20,
|
|
171
201
|
max_podcast_persons: 50,
|
|
172
202
|
max_value_recipients: 20,
|
|
203
|
+
max_podcast_alternate_enclosures: 20,
|
|
204
|
+
max_podcast_alternate_enclosure_sources: 10,
|
|
205
|
+
max_podcast_podroll: 50,
|
|
206
|
+
max_podcast_social_interact: 20,
|
|
207
|
+
max_podcast_txt: 20,
|
|
208
|
+
max_podcast_follow: 20,
|
|
173
209
|
}
|
|
174
210
|
}
|
|
175
211
|
}
|
|
@@ -209,6 +245,12 @@ impl ParserLimits {
|
|
|
209
245
|
max_podcast_funding: 5,
|
|
210
246
|
max_podcast_persons: 10,
|
|
211
247
|
max_value_recipients: 5,
|
|
248
|
+
max_podcast_alternate_enclosures: 5,
|
|
249
|
+
max_podcast_alternate_enclosure_sources: 3,
|
|
250
|
+
max_podcast_podroll: 10,
|
|
251
|
+
max_podcast_social_interact: 5,
|
|
252
|
+
max_podcast_txt: 5,
|
|
253
|
+
max_podcast_follow: 5,
|
|
212
254
|
}
|
|
213
255
|
}
|
|
214
256
|
|
|
@@ -246,6 +288,12 @@ impl ParserLimits {
|
|
|
246
288
|
max_podcast_funding: 50,
|
|
247
289
|
max_podcast_persons: 200,
|
|
248
290
|
max_value_recipients: 50,
|
|
291
|
+
max_podcast_alternate_enclosures: 100,
|
|
292
|
+
max_podcast_alternate_enclosure_sources: 50,
|
|
293
|
+
max_podcast_podroll: 200,
|
|
294
|
+
max_podcast_social_interact: 100,
|
|
295
|
+
max_podcast_txt: 100,
|
|
296
|
+
max_podcast_follow: 100,
|
|
249
297
|
}
|
|
250
298
|
}
|
|
251
299
|
|
{feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/dublin_core.rs
RENAMED
|
@@ -40,8 +40,12 @@ pub fn handle_feed_element(element: &str, text: &str, feed: &mut FeedMeta) {
|
|
|
40
40
|
feed.authors.push(Person::from_name(text));
|
|
41
41
|
}
|
|
42
42
|
"date" => {
|
|
43
|
-
// dc:date → updated (always overrides pubDate promotion)
|
|
44
43
|
if let Some(dt) = parse_date(text) {
|
|
44
|
+
if feed.published.is_none() {
|
|
45
|
+
feed.published = Some(dt);
|
|
46
|
+
feed.published_str = Some(text.to_string());
|
|
47
|
+
}
|
|
48
|
+
// dc:date → updated (always overrides pubDate promotion)
|
|
45
49
|
feed.updated = Some(dt);
|
|
46
50
|
feed.updated_str = Some(text.to_string());
|
|
47
51
|
}
|
|
@@ -115,6 +119,10 @@ pub fn handle_entry_element(element: &str, text: &str, entry: &mut Entry) {
|
|
|
115
119
|
"date" => {
|
|
116
120
|
if let Some(dt) = parse_date(text) {
|
|
117
121
|
entry.dc_date = Some(dt);
|
|
122
|
+
if entry.published.is_none() {
|
|
123
|
+
entry.published = Some(dt);
|
|
124
|
+
entry.published_str = Some(text.to_string());
|
|
125
|
+
}
|
|
118
126
|
// dc:date is more authoritative than pubDate for updated; always override
|
|
119
127
|
entry.updated = Some(dt);
|
|
120
128
|
entry.updated_str = Some(text.to_string());
|
|
@@ -236,12 +244,12 @@ mod tests {
|
|
|
236
244
|
}
|
|
237
245
|
|
|
238
246
|
#[test]
|
|
239
|
-
fn
|
|
247
|
+
fn test_entry_updated_and_published_from_dc_date() {
|
|
240
248
|
let mut entry = Entry::default();
|
|
241
249
|
handle_entry_element("date", "2024-01-15T10:30:00Z", &mut entry);
|
|
242
250
|
|
|
243
251
|
assert!(entry.updated.is_some());
|
|
244
|
-
assert!(entry.published.
|
|
252
|
+
assert!(entry.published.is_some());
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
#[test]
|
{feedparser_rs-0.5.1 → feedparser_rs-0.5.2}/crates/feedparser-rs-core/src/namespace/georss.rs
RENAMED
|
@@ -51,6 +51,14 @@ pub struct GeoLocation {
|
|
|
51
51
|
///
|
|
52
52
|
/// Default is WGS84 (latitude/longitude) if not specified
|
|
53
53
|
pub srs_name: Option<String>,
|
|
54
|
+
/// Elevation in meters (from `georss:elev`)
|
|
55
|
+
pub elev: Option<f64>,
|
|
56
|
+
/// Feature type classification (from `georss:featuretypetag`)
|
|
57
|
+
pub feature_type_tag: Option<String>,
|
|
58
|
+
/// Human-readable place name (from `georss:featurename`)
|
|
59
|
+
pub feature_name: Option<String>,
|
|
60
|
+
/// Relationship type (from `georss:relationshiptag`)
|
|
61
|
+
pub relationship_tag: Option<String>,
|
|
54
62
|
}
|
|
55
63
|
|
|
56
64
|
impl GeoLocation {
|
|
@@ -74,7 +82,7 @@ impl GeoLocation {
|
|
|
74
82
|
Self {
|
|
75
83
|
geo_type: GeoType::Point,
|
|
76
84
|
coordinates: vec![(lat, lon)],
|
|
77
|
-
|
|
85
|
+
..Default::default()
|
|
78
86
|
}
|
|
79
87
|
}
|
|
80
88
|
|
|
@@ -94,11 +102,11 @@ impl GeoLocation {
|
|
|
94
102
|
/// assert_eq!(loc.coordinates.len(), 2);
|
|
95
103
|
/// ```
|
|
96
104
|
#[must_use]
|
|
97
|
-
pub
|
|
105
|
+
pub fn line(coords: Vec<(f64, f64)>) -> Self {
|
|
98
106
|
Self {
|
|
99
107
|
geo_type: GeoType::Line,
|
|
100
108
|
coordinates: coords,
|
|
101
|
-
|
|
109
|
+
..Default::default()
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
|
|
@@ -122,11 +130,11 @@ impl GeoLocation {
|
|
|
122
130
|
/// let loc = GeoLocation::polygon(coords);
|
|
123
131
|
/// ```
|
|
124
132
|
#[must_use]
|
|
125
|
-
pub
|
|
133
|
+
pub fn polygon(coords: Vec<(f64, f64)>) -> Self {
|
|
126
134
|
Self {
|
|
127
135
|
geo_type: GeoType::Polygon,
|
|
128
136
|
coordinates: coords,
|
|
129
|
-
|
|
137
|
+
..Default::default()
|
|
130
138
|
}
|
|
131
139
|
}
|
|
132
140
|
|
|
@@ -152,7 +160,7 @@ impl GeoLocation {
|
|
|
152
160
|
Self {
|
|
153
161
|
geo_type: GeoType::Box,
|
|
154
162
|
coordinates: vec![(lower_lat, lower_lon), (upper_lat, upper_lon)],
|
|
155
|
-
|
|
163
|
+
..Default::default()
|
|
156
164
|
}
|
|
157
165
|
}
|
|
158
166
|
}
|
|
@@ -258,28 +266,80 @@ pub fn handle_entry_element(
|
|
|
258
266
|
match tag {
|
|
259
267
|
b"point" => {
|
|
260
268
|
if let Some(loc) = parse_point(text) {
|
|
261
|
-
|
|
269
|
+
let existing = entry
|
|
270
|
+
.r#where
|
|
271
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
272
|
+
existing.geo_type = loc.geo_type;
|
|
273
|
+
existing.coordinates = loc.coordinates;
|
|
274
|
+
existing.srs_name = loc.srs_name;
|
|
262
275
|
}
|
|
263
276
|
true
|
|
264
277
|
}
|
|
265
278
|
b"line" => {
|
|
266
279
|
if let Some(loc) = parse_line(text) {
|
|
267
|
-
|
|
280
|
+
let existing = entry
|
|
281
|
+
.r#where
|
|
282
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
283
|
+
existing.geo_type = loc.geo_type;
|
|
284
|
+
existing.coordinates = loc.coordinates;
|
|
285
|
+
existing.srs_name = loc.srs_name;
|
|
268
286
|
}
|
|
269
287
|
true
|
|
270
288
|
}
|
|
271
289
|
b"polygon" => {
|
|
272
290
|
if let Some(loc) = parse_polygon(text) {
|
|
273
|
-
|
|
291
|
+
let existing = entry
|
|
292
|
+
.r#where
|
|
293
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
294
|
+
existing.geo_type = loc.geo_type;
|
|
295
|
+
existing.coordinates = loc.coordinates;
|
|
296
|
+
existing.srs_name = loc.srs_name;
|
|
274
297
|
}
|
|
275
298
|
true
|
|
276
299
|
}
|
|
277
300
|
b"box" => {
|
|
278
301
|
if let Some(loc) = parse_box(text) {
|
|
279
|
-
|
|
302
|
+
let existing = entry
|
|
303
|
+
.r#where
|
|
304
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
305
|
+
existing.geo_type = loc.geo_type;
|
|
306
|
+
existing.coordinates = loc.coordinates;
|
|
307
|
+
existing.srs_name = loc.srs_name;
|
|
280
308
|
}
|
|
281
309
|
true
|
|
282
310
|
}
|
|
311
|
+
b"elev" => {
|
|
312
|
+
if let Ok(v) = text.trim().parse::<f64>()
|
|
313
|
+
&& v.is_finite()
|
|
314
|
+
{
|
|
315
|
+
entry
|
|
316
|
+
.r#where
|
|
317
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
318
|
+
.elev = Some(v);
|
|
319
|
+
}
|
|
320
|
+
true
|
|
321
|
+
}
|
|
322
|
+
b"featuretypetag" => {
|
|
323
|
+
entry
|
|
324
|
+
.r#where
|
|
325
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
326
|
+
.feature_type_tag = Some(text.to_string());
|
|
327
|
+
true
|
|
328
|
+
}
|
|
329
|
+
b"featurename" => {
|
|
330
|
+
entry
|
|
331
|
+
.r#where
|
|
332
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
333
|
+
.feature_name = Some(text.to_string());
|
|
334
|
+
true
|
|
335
|
+
}
|
|
336
|
+
b"relationshiptag" => {
|
|
337
|
+
entry
|
|
338
|
+
.r#where
|
|
339
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
340
|
+
.relationship_tag = Some(text.to_string());
|
|
341
|
+
true
|
|
342
|
+
}
|
|
283
343
|
_ => false,
|
|
284
344
|
}
|
|
285
345
|
}
|
|
@@ -305,28 +365,76 @@ pub fn handle_feed_element(
|
|
|
305
365
|
match tag {
|
|
306
366
|
b"point" => {
|
|
307
367
|
if let Some(loc) = parse_point(text) {
|
|
308
|
-
|
|
368
|
+
let existing = feed
|
|
369
|
+
.r#where
|
|
370
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
371
|
+
existing.geo_type = loc.geo_type;
|
|
372
|
+
existing.coordinates = loc.coordinates;
|
|
373
|
+
existing.srs_name = loc.srs_name;
|
|
309
374
|
}
|
|
310
375
|
true
|
|
311
376
|
}
|
|
312
377
|
b"line" => {
|
|
313
378
|
if let Some(loc) = parse_line(text) {
|
|
314
|
-
|
|
379
|
+
let existing = feed
|
|
380
|
+
.r#where
|
|
381
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
382
|
+
existing.geo_type = loc.geo_type;
|
|
383
|
+
existing.coordinates = loc.coordinates;
|
|
384
|
+
existing.srs_name = loc.srs_name;
|
|
315
385
|
}
|
|
316
386
|
true
|
|
317
387
|
}
|
|
318
388
|
b"polygon" => {
|
|
319
389
|
if let Some(loc) = parse_polygon(text) {
|
|
320
|
-
|
|
390
|
+
let existing = feed
|
|
391
|
+
.r#where
|
|
392
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
393
|
+
existing.geo_type = loc.geo_type;
|
|
394
|
+
existing.coordinates = loc.coordinates;
|
|
395
|
+
existing.srs_name = loc.srs_name;
|
|
321
396
|
}
|
|
322
397
|
true
|
|
323
398
|
}
|
|
324
399
|
b"box" => {
|
|
325
400
|
if let Some(loc) = parse_box(text) {
|
|
326
|
-
|
|
401
|
+
let existing = feed
|
|
402
|
+
.r#where
|
|
403
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()));
|
|
404
|
+
existing.geo_type = loc.geo_type;
|
|
405
|
+
existing.coordinates = loc.coordinates;
|
|
406
|
+
existing.srs_name = loc.srs_name;
|
|
407
|
+
}
|
|
408
|
+
true
|
|
409
|
+
}
|
|
410
|
+
b"elev" => {
|
|
411
|
+
if let Ok(v) = text.trim().parse::<f64>()
|
|
412
|
+
&& v.is_finite()
|
|
413
|
+
{
|
|
414
|
+
feed.r#where
|
|
415
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
416
|
+
.elev = Some(v);
|
|
327
417
|
}
|
|
328
418
|
true
|
|
329
419
|
}
|
|
420
|
+
b"featuretypetag" => {
|
|
421
|
+
feed.r#where
|
|
422
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
423
|
+
.feature_type_tag = Some(text.to_string());
|
|
424
|
+
true
|
|
425
|
+
}
|
|
426
|
+
b"featurename" => {
|
|
427
|
+
feed.r#where
|
|
428
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
429
|
+
.feature_name = Some(text.to_string());
|
|
430
|
+
true
|
|
431
|
+
}
|
|
432
|
+
b"relationshiptag" => {
|
|
433
|
+
feed.r#where
|
|
434
|
+
.get_or_insert_with(|| Box::new(GeoLocation::default()))
|
|
435
|
+
.relationship_tag = Some(text.to_string());
|
|
436
|
+
true
|
|
437
|
+
}
|
|
330
438
|
_ => false,
|
|
331
439
|
}
|
|
332
440
|
}
|
|
@@ -341,7 +449,7 @@ fn parse_point(text: &str) -> Option<GeoLocation> {
|
|
|
341
449
|
Some(GeoLocation {
|
|
342
450
|
geo_type: GeoType::Point,
|
|
343
451
|
coordinates: coords,
|
|
344
|
-
|
|
452
|
+
..Default::default()
|
|
345
453
|
})
|
|
346
454
|
} else {
|
|
347
455
|
None
|
|
@@ -358,7 +466,7 @@ fn parse_line(text: &str) -> Option<GeoLocation> {
|
|
|
358
466
|
Some(GeoLocation {
|
|
359
467
|
geo_type: GeoType::Line,
|
|
360
468
|
coordinates: coords,
|
|
361
|
-
|
|
469
|
+
..Default::default()
|
|
362
470
|
})
|
|
363
471
|
} else {
|
|
364
472
|
None
|
|
@@ -375,7 +483,7 @@ fn parse_polygon(text: &str) -> Option<GeoLocation> {
|
|
|
375
483
|
Some(GeoLocation {
|
|
376
484
|
geo_type: GeoType::Polygon,
|
|
377
485
|
coordinates: coords,
|
|
378
|
-
|
|
486
|
+
..Default::default()
|
|
379
487
|
})
|
|
380
488
|
} else {
|
|
381
489
|
None
|
|
@@ -392,7 +500,7 @@ fn parse_box(text: &str) -> Option<GeoLocation> {
|
|
|
392
500
|
Some(GeoLocation {
|
|
393
501
|
geo_type: GeoType::Box,
|
|
394
502
|
coordinates: coords,
|
|
395
|
-
|
|
503
|
+
..Default::default()
|
|
396
504
|
})
|
|
397
505
|
} else {
|
|
398
506
|
None
|
|
@@ -631,4 +739,118 @@ mod tests {
|
|
|
631
739
|
assert!(handled);
|
|
632
740
|
assert!(feed.r#where.is_none());
|
|
633
741
|
}
|
|
742
|
+
|
|
743
|
+
#[test]
|
|
744
|
+
fn test_handle_entry_element_elev() {
|
|
745
|
+
let mut entry = Entry::default();
|
|
746
|
+
let limits = ParserLimits::default();
|
|
747
|
+
|
|
748
|
+
let handled = handle_entry_element(b"elev", "1337.5", &mut entry, &limits);
|
|
749
|
+
assert!(handled);
|
|
750
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
751
|
+
assert_eq!(geo.elev, Some(1337.5));
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
#[test]
|
|
755
|
+
fn test_handle_entry_element_feature_name() {
|
|
756
|
+
let mut entry = Entry::default();
|
|
757
|
+
let limits = ParserLimits::default();
|
|
758
|
+
|
|
759
|
+
let handled = handle_entry_element(b"featurename", "Mont Mégantic", &mut entry, &limits);
|
|
760
|
+
assert!(handled);
|
|
761
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
762
|
+
assert_eq!(geo.feature_name.as_deref(), Some("Mont Mégantic"));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
#[test]
|
|
766
|
+
fn test_handle_entry_element_feature_type_tag() {
|
|
767
|
+
let mut entry = Entry::default();
|
|
768
|
+
let limits = ParserLimits::default();
|
|
769
|
+
|
|
770
|
+
let handled = handle_entry_element(b"featuretypetag", "mountain", &mut entry, &limits);
|
|
771
|
+
assert!(handled);
|
|
772
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
773
|
+
assert_eq!(geo.feature_type_tag.as_deref(), Some("mountain"));
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
#[test]
|
|
777
|
+
fn test_handle_entry_element_relationship_tag() {
|
|
778
|
+
let mut entry = Entry::default();
|
|
779
|
+
let limits = ParserLimits::default();
|
|
780
|
+
|
|
781
|
+
let handled =
|
|
782
|
+
handle_entry_element(b"relationshiptag", "is-located-at", &mut entry, &limits);
|
|
783
|
+
assert!(handled);
|
|
784
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
785
|
+
assert_eq!(geo.relationship_tag.as_deref(), Some("is-located-at"));
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
#[test]
|
|
789
|
+
fn test_extended_attrs_without_geometry() {
|
|
790
|
+
let mut entry = Entry::default();
|
|
791
|
+
let limits = ParserLimits::default();
|
|
792
|
+
|
|
793
|
+
handle_entry_element(b"featurename", "Unknown Location", &mut entry, &limits);
|
|
794
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
795
|
+
assert_eq!(geo.feature_name.as_deref(), Some("Unknown Location"));
|
|
796
|
+
assert!(geo.coordinates.is_empty());
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
#[test]
|
|
800
|
+
fn test_extended_attrs_invalid_elev() {
|
|
801
|
+
let mut entry = Entry::default();
|
|
802
|
+
let limits = ParserLimits::default();
|
|
803
|
+
|
|
804
|
+
let handled = handle_entry_element(b"elev", "not-a-number", &mut entry, &limits);
|
|
805
|
+
assert!(handled);
|
|
806
|
+
// GeoLocation not created because elev parse failed
|
|
807
|
+
assert!(entry.r#where.is_none());
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
#[test]
|
|
811
|
+
fn test_extended_attrs_elev_non_finite_ignored() {
|
|
812
|
+
let limits = ParserLimits::default();
|
|
813
|
+
|
|
814
|
+
for value in ["NaN", "Infinity", "-Infinity"] {
|
|
815
|
+
let mut entry = Entry::default();
|
|
816
|
+
let handled = handle_entry_element(b"elev", value, &mut entry, &limits);
|
|
817
|
+
assert!(handled, "element must be recognized for value {value}");
|
|
818
|
+
assert!(
|
|
819
|
+
entry.r#where.is_none(),
|
|
820
|
+
"non-finite elev '{value}' must not create GeoLocation"
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
#[test]
|
|
826
|
+
fn test_extended_attrs_before_geometry() {
|
|
827
|
+
let mut entry = Entry::default();
|
|
828
|
+
let limits = ParserLimits::default();
|
|
829
|
+
|
|
830
|
+
handle_entry_element(b"featurename", "Reverse Order", &mut entry, &limits);
|
|
831
|
+
handle_entry_element(b"elev", "500.0", &mut entry, &limits);
|
|
832
|
+
handle_entry_element(b"point", "40.0 -74.0", &mut entry, &limits);
|
|
833
|
+
|
|
834
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
835
|
+
assert_eq!(geo.geo_type, GeoType::Point);
|
|
836
|
+
assert_eq!(geo.coordinates[0], (40.0, -74.0));
|
|
837
|
+
assert_eq!(geo.feature_name.as_deref(), Some("Reverse Order"));
|
|
838
|
+
assert_eq!(geo.elev, Some(500.0));
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
#[test]
|
|
842
|
+
fn test_extended_attrs_after_geometry() {
|
|
843
|
+
let mut entry = Entry::default();
|
|
844
|
+
let limits = ParserLimits::default();
|
|
845
|
+
|
|
846
|
+
handle_entry_element(b"point", "45.256 -71.92", &mut entry, &limits);
|
|
847
|
+
handle_entry_element(b"featurename", "Mont Mégantic", &mut entry, &limits);
|
|
848
|
+
handle_entry_element(b"elev", "1337.5", &mut entry, &limits);
|
|
849
|
+
|
|
850
|
+
let geo = entry.r#where.as_ref().unwrap();
|
|
851
|
+
assert_eq!(geo.geo_type, GeoType::Point);
|
|
852
|
+
assert_eq!(geo.coordinates[0], (45.256, -71.92));
|
|
853
|
+
assert_eq!(geo.feature_name.as_deref(), Some("Mont Mégantic"));
|
|
854
|
+
assert_eq!(geo.elev, Some(1337.5));
|
|
855
|
+
}
|
|
634
856
|
}
|