co_primitives/library/
block_links.rs1use crate::{from_cbor, Block, CoReference, KnownMultiCodec, MultiCodec};
5use cid::Cid;
6use ipld_core::codec::Links;
7use serde::de::IgnoredAny;
8use std::{collections::BTreeSet, fmt::Debug};
9
10#[derive(Debug, Default, Clone)]
11pub struct BlockLinks {
12 filters: JoinFilter,
13}
14impl BlockLinks {
15 pub fn new() -> Self {
16 Self::default()
17 }
18
19 pub fn with_filter(mut self, filter: impl BlockLinksFilter + 'static) -> Self {
21 self.filters = self.filters.with_filter(filter);
22 self
23 }
24
25 pub fn has_links(&self, cid: impl Into<MultiCodec>) -> bool {
27 matches!(
28 cid.into(),
29 MultiCodec::Known(KnownMultiCodec::DagPb)
30 | MultiCodec::Known(KnownMultiCodec::DagCbor)
31 | MultiCodec::Known(KnownMultiCodec::DagJson)
32 | MultiCodec::Known(KnownMultiCodec::CoReference)
33 )
34 }
35
36 pub fn links<'a>(
42 &self,
43 block: &'a Block,
44 ) -> Result<impl Iterator<Item = Cid> + Send + Sync + use<'_, 'a>, anyhow::Error> {
45 let iter: Box<dyn Iterator<Item = Cid> + Send + Sync> =
46 if !self.filters.filter_block(block.cid(), block.data())? {
47 Box::new(std::iter::empty())
48 } else {
49 match MultiCodec::from(block.cid()) {
50 MultiCodec::Known(KnownMultiCodec::DagPb) => Box::new(ipld_dagpb::DagPbCodec::links(block.data())?),
51 MultiCodec::Known(KnownMultiCodec::DagCbor) | MultiCodec::Known(KnownMultiCodec::CoReference) => {
52 Box::new(serde_ipld_dagcbor::codec::DagCborCodec::links(block.data())?)
53 },
54 MultiCodec::Known(KnownMultiCodec::DagJson) => {
55 Box::new(serde_ipld_dagjson::codec::DagJsonCodec::links(block.data())?)
56 },
57 _ => Box::new(std::iter::empty()),
58 }
59 };
60 Ok(iter.filter(|cid| self.filters.filter(cid)))
61 }
62}
63
64pub trait BlockLinksFilter: Debug + BlockLinksFilterClone + Send + Sync {
65 fn filter(&self, cid: &Cid) -> bool;
67
68 fn filter_block(&self, cid: &Cid, data: &[u8]) -> Result<bool, anyhow::Error>;
70}
71
72pub trait BlockLinksFilterClone {
73 fn box_clone(&self) -> Box<dyn BlockLinksFilter>;
74}
75impl Clone for Box<dyn BlockLinksFilter> {
76 fn clone(&self) -> Self {
77 self.box_clone()
78 }
79}
80impl<T> BlockLinksFilterClone for T
81where
82 T: BlockLinksFilter + Clone + 'static,
83{
84 fn box_clone(&self) -> Box<dyn BlockLinksFilter> {
85 Box::new(self.clone())
86 }
87}
88
89#[derive(Debug, Default, Clone)]
90pub struct JoinFilter {
91 filters: Vec<Box<dyn BlockLinksFilter>>,
92}
93impl JoinFilter {
94 pub fn with_filter(mut self, filter: impl BlockLinksFilter + 'static) -> Self {
96 self.filters.push(Box::new(filter));
97 self
98 }
99}
100impl BlockLinksFilter for JoinFilter {
101 fn filter(&self, cid: &Cid) -> bool {
102 for filter in self.filters.iter() {
103 if !filter.filter(cid) {
104 return false;
105 }
106 }
107 true
108 }
109
110 fn filter_block(&self, cid: &Cid, data: &[u8]) -> Result<bool, anyhow::Error> {
111 for filter in self.filters.iter() {
112 if !filter.filter_block(cid, data)? {
113 return Ok(false);
114 }
115 }
116 Ok(true)
117 }
118}
119
120#[derive(Debug, Clone)]
122pub struct IgnoreFilter {
123 ignore: BTreeSet<Cid>,
125}
126impl IgnoreFilter {
127 pub fn new(ignore: BTreeSet<Cid>) -> Self {
128 Self { ignore }
129 }
130}
131impl BlockLinksFilter for IgnoreFilter {
132 fn filter(&self, cid: &Cid) -> bool {
133 !self.ignore.contains(cid)
134 }
135
136 fn filter_block(&self, _cid: &Cid, _data: &[u8]) -> Result<bool, anyhow::Error> {
137 Ok(true)
138 }
139}
140
141#[derive(Debug, Default, Clone)]
143pub struct WeakCoReferenceFilter {}
144impl WeakCoReferenceFilter {
145 pub fn new() -> Self {
146 Self::default()
147 }
148}
149impl BlockLinksFilter for WeakCoReferenceFilter {
150 fn filter(&self, _cid: &Cid) -> bool {
151 true
152 }
153
154 fn filter_block(&self, cid: &Cid, data: &[u8]) -> Result<bool, anyhow::Error> {
155 Ok(if MultiCodec::is(cid, KnownMultiCodec::CoReference) {
156 let reference: CoReference<IgnoredAny> = from_cbor(data)?;
157 match reference {
158 CoReference::Weak(_) => false,
159 }
160 } else {
161 true
162 })
163 }
164}