Skip to main content
Glama
write.rs3.63 kB
use std::{ num::TryFromIntError, path::PathBuf, }; use ::tar::{ Builder, Header, }; use petgraph::prelude::*; use thiserror::Error; use crate::{ GraphError, NameStr, ObjectTree, WriteBytes, graph::{ HashedNodeWithEntries, NodeEntry, }, tar::{ object_path, ref_path, }, }; /// Errors that can occur when creating a tar bundle of the object tree #[remain::sorted] #[derive(Debug, Error)] pub enum TarWriterError { /// When the object tree cannot be converted to a petgraph graph #[error("GraphError: {0}")] Graph(#[from] GraphError), /// When an entry cannot be added to the tar file #[error("IoError: {0}")] Io(#[from] std::io::Error), /// When the length of the entry is unable to be converted to the size entry of the tar header #[error("TryFromIntError: {0}")] TryFromInt(#[from] TryFromIntError), } /// Create a tar from an [`ObjectTree`] pub struct TarWriter { bytes: Vec<u8>, } impl TarWriter { /// Return a [`TarWriter`] populated from the provided [`ObjectTree`] pub fn new<T>(tree: &ObjectTree<T>) -> Result<Self, TarWriterError> where T: Clone + NameStr + WriteBytes + Send + Sync + 'static, { let (graph, root_idx) = tree.as_petgraph(); let mut tar_builder = Builder::new(Vec::new()); let mut dfspo = DfsPostOrder::new(graph, root_idx); while let Some(node_idx) = dfspo.next(graph) { let node = graph .node_weight(node_idx) .ok_or(GraphError::NodeWeightNotFound( node_idx.index(), "tar writer: could not find next node for index for dfspo", ))? .clone(); let mut entries = Vec::new(); for child_idx in graph.neighbors_directed(node_idx, Outgoing) { let child_node = graph .node_weight(child_idx) .ok_or(GraphError::NodeWeightNotFound( child_idx.index(), "tar writer: could not find child node for index", ))?; entries.push(NodeEntry::new( child_node.kind(), child_node.hash(), child_node.name(), )); } let tar_entry = HashedNodeWithEntries::new(node, entries); write_tar_entry( &mut tar_builder, object_path(&tar_entry.hash()), &tar_entry.to_bytes()?, )?; } let root_node = graph .node_weight(root_idx) .ok_or(GraphError::NodeWeightNotFound( root_idx.index(), "tar writer: could not find root node for index", ))?; write_tar_entry( &mut tar_builder, ref_path("root"), root_node.hash().to_string().as_bytes(), )?; tar_builder.finish()?; Ok(Self { bytes: tar_builder.into_inner()?, }) } /// Return the tar as a `Vec<u8>` pub fn bytes(self) -> Vec<u8> { self.bytes } } fn write_tar_entry( tar_builder: &mut Builder<Vec<u8>>, path: PathBuf, entry: &[u8], ) -> Result<(), TarWriterError> { let mut tar_entry_header = Header::new_gnu(); tar_entry_header.set_path(&path)?; tar_entry_header.set_size(entry.len().try_into()?); tar_entry_header.set_cksum(); tar_builder.append(&tar_entry_header, entry)?; Ok(()) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server