swh-mosaic 0.3.1

MOdular Storage of Archived and Indexed Contents from Software Heritage
Documentation
// Copyright (C) 2026  The Software Heritage developers
// See the AUTHORS file at the top-level directory of this distribution
// License: GNU General Public License version 3, or any later version
// See top-level LICENSE file for more information

//! implementation of the `swh-mosaic extract` subcommand

use std::fs::File;
use std::io::Write;
use std::path::Path;

use anyhow::Result;
use hex::FromHex;

use crate::reader::{MmapMosaicReader, MosaicReaderError};
use crate::IdxDescription;

fn write_file(key: &String, content: &[u8]) -> Result<()> {
    let mut file = File::create(key)?;
    file.write_all(content)?;

    Ok(())
}

/// Extract objects (all if `keys` is empty) from a MOSAIC file to current folder
pub fn extract(
    filename: &Path,
    index: &IdxDescription,
    keys: &Vec<String>,
    all: &bool,
) -> Result<()> {
    let reader = match MmapMosaicReader::new(filename, *index) {
        Ok(reader) => reader,
        Err(error) => match error.downcast_ref::<MosaicReaderError>() {
            Some(MosaicReaderError::IndexNotFound { idx_description }) => panic!(
                "Cannot find index {},
            maybe it does not exist in this file. Use `info` to list available indexes.",
                idx_description
            ),
            _ => return Err(error),
        },
    };

    if *all {
        for pair in reader.iter() {
            let (key, content) = pair?;
            let key_hex = hex::encode(key);
            if let Err(error) = write_file(&key_hex, &content) {
                println!("Error while writing object {}: {}", key_hex, error);
            }
        }
    } else {
        for k in keys {
            let key = match <Vec<u8>>::from_hex(k) {
                Ok(slice) => slice,
                Err(_) => {
                    println!("Skipping malformed hex key: {}", k);
                    continue;
                }
            };
            let cnt = match reader.lookup(&key) {
                Ok(o) => match o {
                    Some(object) => object,
                    None => {
                        println!("Skipping key not found: {}", k);
                        continue;
                    }
                },
                Err(_) => {
                    println!("Skipping key not found: {}", k);
                    continue;
                }
            };
            if let Err(error) = write_file(k, &cnt) {
                println!("Error while writing object {}: {}", k, error);
            }
        }
    }

    Ok(())
}