1use std::io;
2use std::io::prelude::*;
3
4use anyhow::{anyhow, Context, Result};
5use clap::Parser;
6use tracing::*;
7
8use crate::backend;
9use crate::blob;
10use crate::hashing::ObjectId;
11use crate::index;
12use crate::pack;
13use crate::snapshot;
14use crate::tree;
15
16#[derive(Debug, Parser)]
18pub struct Args {
19 #[clap(subcommand)]
20 subcommand: Subcommand,
21}
22
23#[derive(Debug, Parser)]
24pub enum Subcommand {
25 #[clap(verbatim_doc_comment)]
29 Blob { id: ObjectId },
30
31 #[clap(verbatim_doc_comment)]
36 Pack { id: ObjectId },
37
38 #[clap(verbatim_doc_comment)]
44 Index { id: ObjectId },
45
46 #[clap(verbatim_doc_comment)]
52 Snapshot { id_prefix: String },
53}
54
55pub fn run(repository: &camino::Utf8Path, args: Args) -> Result<()> {
56 unsafe {
57 crate::prettify::prettify_serialize();
58 }
59
60 let (_cfg, cached_backend) = backend::open(repository, backend::CacheBehavior::Normal)?;
61
62 match args.subcommand {
63 Subcommand::Blob { id } => {
64 let index = index::build_master_index(&cached_backend)?;
65 let blob_map = index::blob_to_pack_map(&index)?;
66 let containing_pack_id = blob_map
67 .get(&id)
68 .ok_or_else(|| anyhow!("Can't find blob {} in the index", id))?;
69 info!("Blob {} found in pack {}", id, containing_pack_id);
70 let index_manifest = index.packs.get(containing_pack_id).unwrap();
71
72 let mut reader = cached_backend.read_pack(containing_pack_id)?;
73
74 let (manifest_entry, blob) = pack::extract_blob(&mut reader, &id, index_manifest)?;
75
76 debug_assert!(manifest_entry.id == id);
77 assert!(!blob.is_empty());
78 match manifest_entry.blob_type {
79 blob::Type::Chunk => io::stdout().write_all(&blob)?,
80 blob::Type::Tree => {
81 let tree: tree::Tree = ciborium::from_reader(&*blob)
82 .with_context(|| format!("CBOR decoding of tree {} failed", id))?;
83 serde_json::to_writer(io::stdout(), &tree)?;
84 }
85 }
86 }
87 Subcommand::Pack { id } => {
88 let manifest = pack::load_manifest(&id, &cached_backend)?;
89 serde_json::to_writer(io::stdout(), &manifest)?;
90 }
91 Subcommand::Index { id } => {
92 let index = index::load(&id, &cached_backend)?;
93 serde_json::to_writer(io::stdout(), &index)?;
94 }
95 Subcommand::Snapshot { id_prefix } => {
96 let chrono_list = snapshot::load_chronologically(&cached_backend)?;
97 let (snapshot, _id) = snapshot::find(&chrono_list, &id_prefix)?;
98 serde_json::to_writer(io::stdout(), &snapshot)?;
99 }
100 }
101 Ok(())
102}