23个版本
0.9.1 | 2024年6月30日 |
---|---|
0.8.3 | 2024年4月25日 |
0.8.2 | 2024年2月11日 |
0.7.4 | 2023年12月11日 |
0.3.2 | 2022年11月28日 |
#593 in 解析实现
1,352每月下载量
200KB
5.5K SLoC
badpod
用于处理播客不完整源数据的Rust库。
此库不应用于
❌ 处理规范、处理过的播客源数据
❌ 与数据库交互
此库可用于
✅ 解释外部源数据,通常来自未知来源
✅ 提供有关源数据内容的反馈
动机
在需要与数据库通信的后端服务器上,通常使用严格的模式。因此,如果加载不符合模式的外部源内容,可能无法成功反序列化该内容。
这种情况在播客领域非常常见,其中播客的RSS源可能
- 结合多个标准(命名空间)
- 缺少某些元素
- 某些值的数据类型错误
- 等。
在这种情况下,我们可能需要一个更灵活的中间模式:一个不会立即抛出错误以遇到意外值的模式,而是将能够反序列化的内容强加于它,并将失败反序列化的内容存储起来,以便进一步清理或分析。
用法
在项目中包含
cargo add badpod
这将包括在您的 Cargo.toml
文件中的库的最新版本。
反序列化
let rss = match badpod::from_str(feed_str) {
Ok(rss) => rss,
Err(_) => panic!("Something went terribly wrong."),
};
理论上,badpod::from_str
应仅在两种情况下返回错误
- 源不是有效的XML
- 源根元素不是
<rss>
特性
检查标签和属性的存在
在 badpod
中,每个表示 XML 标签的字段都是一个 Vec,每个表示 XML 属性的字段都是一个 Option。这是为了反映在 XML 中,标签 可以 重复,而属性——不能。我们不强制要求在源中包含什么或多少个标签——这是您自己决定的!使其更加灵活(而不是立即抛出错误)也允许为用户提供更好的反馈。
match (value_time_split.remote_item.len(), value_time_split.value_recipient.len()) {
(0, 0) => println!("Either a single `<podcast:remoteItem>` element or one or more `<podcast:valueRecipient>` elements are required."),
(1, 0) => println!("You are referencing a remote item! Awesome!"),
(_, 0) => println!("Only a single `<podcast:remoteItem>` element is allowed."),
(0, _) => println!("You are sharing value with others during this segment! Nice!"),
(_, _) => println!("Either a single `<podcast:remoteItem>` element or one or more `<podcast:valueRecipient>` elements can be included."),
};
注意:所有表示标签的字段都使用单数形式命名,尽管它们是向量。
反序列化复杂的标签
badpod
将文本格式的复杂数据转换为更容易处理的形式。如果不可能,我们提供具有 Other
变体的枚举,这旨在表示无法反序列化的数据及其失败的原因。
match geo {
podcast::Geo::Ok {
// f64
latitude,
// f64
longitude,
// Option<f64>
altitude,
// Option<f64>
uncertainty,
} => {
println!("Successfully extracted geographical coordinates!")
}
podcast::Geo::Other((s, reason)) => {
println!("Could not parse coordinates from \"{s}\": {reason}.")
}
};
Other
变体在具有许多变体的枚举中特别有用。如果您没有与 Other
匹配,您知道反序列化生成了合理预期的结果。
match language {
Language::English(region) => println!("A variant of English!"),
Language::Lithuanian => println!("Lithuanian!"),
Language::Other((s, _)) => println!("Unexpected language code \"{s}\"."),
_ => println!("Some other valid language!"),
};
但是,仅仅因为您匹配了一个不是 Other
的变体,并不意味着它对您来说是有效的值。例如,MimeEnclosure 将 AudioOpus
作为其变体之一,但如果你要求源只包含由 Apple Podcasts 支持的格式的媒体文件,那么你将想要拒绝它。再次强调,badpod
仅是分析源的工具;您决定“正确”的源必须是什么样子。
标签感知反序列化
许多标签使用相同的数据类型,但以不同的方式编码。例如,<podcast:locked>
和 <itunes:explicit>
都本质上是布尔值,但前者序列化为 "yes"
/"no"
,后者为 "true"
/"false"
。在 badpod
中,它们都被反序列化为 Bool。
match channel.itunes_explicit.get(0) {
Some(is_explicit) => {
match is_explicit {
Bool::Ok(b) => {
println!("is explicit? \"{b}\"")
}
Bool::Other((s, reason)) => println!("could not parse \"{s}\": {reason}"),
}
}
None => println!("<itunes:explicit> not found."),
};
打印枚举
尽管这个crate主要用于反序列化,但在某些情况下需要将枚举转换回字符串。在 badpod
中,所有枚举都实现了 Display 特性
// Outputs "cs".
println!("{}", Language::Czech);
// Outputs "en".
println!("{}", Language::English(LanguageEnglish::Default));
// Outputs "en-gb".
println!("{}", Language::English(LanguageEnglish::UnitedKingdom));
依赖项
~5.5–7.5MB
~155K SLoC