1use std::path::PathBuf;
4
5use anyhow::Context;
6use bytes::Bytes;
7use serde::{Deserialize, Serialize};
8use tracing::trace;
9
10use crate::{
11 format::collection::Collection,
12 store::{BaoBlobSize, ExportFormat, ExportMode, MapEntry, Store as BaoStore},
13 util::progress::{IdGenerator, ProgressSender},
14 Hash,
15};
16
17pub async fn export<D: BaoStore>(
26 db: &D,
27 hash: Hash,
28 outpath: PathBuf,
29 format: ExportFormat,
30 mode: ExportMode,
31 progress: impl ProgressSender<Msg = ExportProgress> + IdGenerator,
32) -> anyhow::Result<()> {
33 match format {
34 ExportFormat::Blob => export_blob(db, hash, outpath, mode, progress).await,
35 ExportFormat::Collection => export_collection(db, hash, outpath, mode, progress).await,
36 }
37}
38
39pub async fn export_collection<D: BaoStore>(
41 db: &D,
42 hash: Hash,
43 outpath: PathBuf,
44 mode: ExportMode,
45 progress: impl ProgressSender<Msg = ExportProgress> + IdGenerator,
46) -> anyhow::Result<()> {
47 tokio::fs::create_dir_all(&outpath).await?;
48 let collection = Collection::load_db(db, &hash).await?;
49 for (name, hash) in collection.into_iter() {
50 #[allow(clippy::needless_borrow)]
51 let path = outpath.join(pathbuf_from_name(&name));
52 export_blob(db, hash, path, mode, progress.clone()).await?;
53 }
54 Ok(())
55}
56
57pub async fn export_blob<D: BaoStore>(
59 db: &D,
60 hash: Hash,
61 outpath: PathBuf,
62 mode: ExportMode,
63 progress: impl ProgressSender<Msg = ExportProgress> + IdGenerator,
64) -> anyhow::Result<()> {
65 if let Some(parent) = outpath.parent() {
66 tokio::fs::create_dir_all(parent).await?;
67 }
68 trace!("exporting blob {} to {}", hash, outpath.display());
69 let id = progress.new_id();
70 let entry = db.get(&hash).await?.context("entry not there")?;
71 progress
72 .send(ExportProgress::Found {
73 id,
74 hash,
75 outpath: outpath.clone(),
76 size: entry.size(),
77 meta: None,
78 })
79 .await?;
80 let progress1 = progress.clone();
81 db.export(
82 hash,
83 outpath,
84 mode,
85 Box::new(move |offset| Ok(progress1.try_send(ExportProgress::Progress { id, offset })?)),
86 )
87 .await?;
88 progress.send(ExportProgress::Done { id }).await?;
89 Ok(())
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
94pub enum ExportProgress {
95 Found {
98 id: u64,
100 hash: Hash,
102 size: BaoBlobSize,
104 outpath: PathBuf,
106 meta: Option<Bytes>,
108 },
109 Progress {
113 id: u64,
115 offset: u64,
117 },
118 Done {
120 id: u64,
122 },
123 AllDone,
125 Abort(serde_error::Error),
127}
128
129fn pathbuf_from_name(name: &str) -> PathBuf {
130 let mut path = PathBuf::new();
131 for part in name.split('/') {
132 path.push(part);
133 }
134 path
135}