use bytes::Bytes;
use libipld::{Cid, Ipld, IpldCodec};
use libipld_core::multihash::{Code, MultihashDigest};
use proptest::{
prelude::{Rng, RngCore},
strategy::Strategy,
test_runner::TestRng,
};
use roaring_graphs::{arb_dag, DirectedAcyclicGraph, Vertex};
use std::{
collections::{BTreeMap, HashSet},
fmt::Debug,
ops::Range,
};
use wnfs_common::encode;
pub fn arb_ipld_dag<T: Debug + Clone>(
vertex_count: impl Into<Range<Vertex>>,
edge_probability: f64,
generate_block: impl Fn(Vec<Cid>, &mut TestRng) -> (Cid, T) + Clone,
) -> impl Strategy<Value = (Vec<(Cid, T)>, Cid)> {
arb_dag(vertex_count, edge_probability)
.prop_perturb(move |dag, mut rng| dag_to_nodes(&dag, &mut rng, generate_block.clone()))
}
pub fn links_to_ipld(cids: Vec<Cid>, _: &mut TestRng) -> (Cid, Ipld) {
let ipld = Ipld::List(cids.into_iter().map(Ipld::Link).collect());
let bytes = encode(&ipld, IpldCodec::DagCbor).unwrap();
let cid = Cid::new_v1(IpldCodec::DagCbor.into(), Code::Blake3_256.digest(&bytes));
(cid, ipld)
}
pub fn links_to_dag_cbor(cids: Vec<Cid>, _: &mut TestRng) -> (Cid, Bytes) {
let ipld = Ipld::List(cids.into_iter().map(Ipld::Link).collect());
let bytes: Bytes = encode(&ipld, IpldCodec::DagCbor).unwrap().into();
let cid = Cid::new_v1(IpldCodec::DagCbor.into(), Code::Blake3_256.digest(&bytes));
(cid, bytes)
}
pub fn links_to_padded_ipld(
padding_bytes: usize,
) -> impl Fn(Vec<Cid>, &mut TestRng) -> (Cid, Ipld) + Clone {
move |cids, rng| {
let mut padding = vec![0u8; padding_bytes];
rng.fill_bytes(&mut padding);
let codec = match rng.gen_bool(0.5) {
true if cids.is_empty() => IpldCodec::Raw,
_ => IpldCodec::DagCbor,
};
let ipld = if cids.is_empty() && codec == IpldCodec::Raw {
Ipld::Bytes(padding)
} else {
Ipld::Map(BTreeMap::from([
("data".into(), Ipld::Bytes(padding)),
(
"links".into(),
Ipld::List(cids.into_iter().map(Ipld::Link).collect()),
),
]))
};
let bytes = encode(&ipld, codec).unwrap();
let cid = Cid::new_v1(codec.into(), Code::Blake3_256.digest(&bytes));
(cid, ipld)
}
}
pub fn dag_to_nodes<T>(
dag: &DirectedAcyclicGraph,
rng: &mut TestRng,
generate_node: impl Fn(Vec<Cid>, &mut TestRng) -> (Cid, T) + Clone,
) -> (Vec<(Cid, T)>, Cid) {
let mut blocks = Vec::new();
let mut visited = HashSet::new();
let (cid, block) = dag_to_nodes_helper(dag, 0, rng, generate_node, &mut blocks, &mut visited);
blocks.push((cid, block));
(blocks, cid)
}
fn dag_to_nodes_helper<T>(
dag: &DirectedAcyclicGraph,
root: Vertex,
rng: &mut TestRng,
generate_node: impl Fn(Vec<Cid>, &mut TestRng) -> (Cid, T) + Clone,
arr: &mut Vec<(Cid, T)>,
visited: &mut HashSet<Vertex>,
) -> (Cid, T) {
let mut child_blocks = Vec::new();
if root >= dag.get_vertex_count() {
println!("{root}, {}", dag.get_vertex_count());
}
for child in dag.iter_children(root) {
if visited.contains(&child) {
continue;
}
visited.insert(child);
child_blocks.push(dag_to_nodes_helper(
dag,
child,
rng,
generate_node.clone(),
arr,
visited,
));
}
let result = generate_node(child_blocks.iter().map(|(cid, _)| *cid).collect(), rng);
arr.extend(child_blocks);
result
}