1use live_data::{FeedDescriptor, FeedManifest, FormatPreference, TransportTag};
4
5#[derive(Debug, Clone)]
9pub struct FeedCatalog {
10 manifest: FeedManifest,
11}
12
13impl FeedCatalog {
14 pub fn new(manifest: FeedManifest) -> Self {
16 Self { manifest }
17 }
18
19 pub fn from_json(bytes: &[u8]) -> serde_json::Result<Self> {
21 let manifest = serde_json::from_slice(bytes)?;
22 Ok(Self::new(manifest))
23 }
24
25 pub fn from_json_str(s: &str) -> serde_json::Result<Self> {
27 Self::from_json(s.as_bytes())
28 }
29
30 pub fn manifest(&self) -> &FeedManifest {
31 &self.manifest
32 }
33
34 pub fn into_manifest(self) -> FeedManifest {
35 self.manifest
36 }
37
38 pub fn protocol_version(&self) -> u32 {
39 self.manifest.protocol_version
40 }
41
42 pub fn server_version(&self) -> &str {
43 &self.manifest.server_version
44 }
45
46 pub fn feeds(&self) -> &[FeedDescriptor] {
47 &self.manifest.feeds
48 }
49
50 pub fn len(&self) -> usize {
51 self.manifest.feeds.len()
52 }
53
54 pub fn is_empty(&self) -> bool {
55 self.manifest.feeds.is_empty()
56 }
57
58 pub fn find(&self, name: &str) -> Option<&FeedDescriptor> {
60 self.manifest.feeds.iter().find(|f| f.name == name)
61 }
62
63 pub fn filter_by_tag<'a>(
65 &'a self,
66 tag: &'a str,
67 ) -> impl Iterator<Item = &'a FeedDescriptor> + 'a {
68 self.manifest
69 .feeds
70 .iter()
71 .filter(move |f| f.tags.iter().any(|t| t == tag))
72 }
73
74 pub fn filter_by_transport(
76 &self,
77 t: TransportTag,
78 ) -> impl Iterator<Item = &FeedDescriptor> {
79 self.manifest.feeds.iter().filter(move |f| f.transports.contains(&t))
80 }
81
82 pub fn filter_by_format(
84 &self,
85 f: FormatPreference,
86 ) -> impl Iterator<Item = &FeedDescriptor> {
87 self.manifest.feeds.iter().filter(move |feed| feed.formats.contains(&f))
88 }
89
90 pub fn search<'a>(
92 &'a self,
93 query: &'a str,
94 ) -> impl Iterator<Item = &'a FeedDescriptor> + 'a {
95 let q = query.to_ascii_lowercase();
96 self.manifest.feeds.iter().filter(move |f| {
97 f.name.to_ascii_lowercase().contains(&q)
98 || f.description
99 .as_ref()
100 .map(|d| d.to_ascii_lowercase().contains(&q))
101 .unwrap_or(false)
102 })
103 }
104}
105
106impl From<FeedManifest> for FeedCatalog {
107 fn from(m: FeedManifest) -> Self {
108 Self::new(m)
109 }
110}