use commands::{BasicOptions, StaticSubcommand, default_explain};
use clap::{SubCommand, ArgMatches, Arg};
use error;
use libpijul::{ROOT_KEY, Branch, Txn, Key, PatchId, Edge};
use std::path::{PathBuf, Path};
use tar::{Header, Builder};
use std::fs::File;
use flate2::write::GzEncoder;
use flate2::Compression;
use std::io::Write;
pub fn invocation() -> StaticSubcommand {
return SubCommand::with_name("dist")
.about("Produces a tar.gz archive of the repository")
.arg(Arg::with_name("archive")
.short("d")
.takes_value(true)
.required(true)
.help("File name of the output archive."))
.arg(Arg::with_name("branch")
.long("branch")
.help("The branch from which to make the archive, defaults to the current branch.")
.takes_value(true)
.required(false))
.arg(Arg::with_name("repository")
.long("repository")
.help("Repository where to work.")
.takes_value(true));
}
pub fn run(args: &ArgMatches) -> Result<(), error::Error> {
let opts = BasicOptions::from_args(args)?;
let archive_name = args.value_of("archive").unwrap();
let archive_path = {
PathBuf::from(archive_name.to_string() + ".tar.gz")
};
let repo = opts.open_repo()?;
let txn = repo.txn_begin()?;
if let Some(branch) = txn.get_branch(&opts.branch()) {
let encoder = GzEncoder::new(File::create(&archive_path)?, Compression::Best);
let mut archive = Builder::new(encoder);
let mut buffer = Vec::new();
let mut forward = Vec::new();
let mut current_path = Path::new(archive_name).to_path_buf();
archive_rec(&txn, &branch, ROOT_KEY, &mut archive, &mut buffer, &mut forward,
&mut current_path)?;
archive.into_inner()?.finish()?.flush()?;
}
Ok(())
}
pub fn archive_rec<W:Write>(
txn: &Txn, branch: &Branch, key: Key<PatchId>,
builder: &mut Builder<W>, buffer: &mut Vec<u8>,
forward: &mut Vec<(Key<PatchId>, Edge)>, current_path: &mut PathBuf
) -> Result<(), error::Error> {
let files = txn.list_files_under_node(branch, &key);
for (key, names) in files {
debug!("archive_rec: {:?} {:?}", key, names);
if names.len() > 1 {
error!("file has several names: {:?}", names);
}
current_path.push(names[0].1);
if names[0].0.is_dir() {
archive_rec(txn, branch, key, builder, buffer, forward, current_path)?;
} else {
buffer.clear();
let mut graph = txn.retrieve(&branch, &key);
txn.output_file(buffer, &mut graph, forward)?;
let mut header = Header::new_gnu();
header.set_path(¤t_path)?;
header.set_size(buffer.len() as u64);
header.set_mode(names[0].0.permissions() as u32);
header.set_cksum();
builder.append(&header, &buffer[..])?;
}
current_path.pop();
}
Ok(())
}
pub fn explain(res: Result<(), error::Error>) {
default_explain(res)
}