use crate::command_options::{
Arity, DEFAULT_MODE, OptionError, OptionSpec, ParsedOptions, UNREAD, WrapperOccurrence,
parse_options, row,
};
use crate::command_options_query::{is_enabled, is_stated, last_occurrence, positional_tokens, value_bytes};
use std::ffi::OsString;
pub const ATOMIC: u16 = 1;
pub const DRY_RUN: u16 = 2;
pub const FORCE: u16 = 4;
pub const FORCE_WITH_LEASE: u16 = 8;
pub const FORCE_IF_INCLUDES: u16 = 16;
pub const MIRROR: u16 = 32;
pub const DELETE: u16 = 64;
pub const ALL: u16 = 128;
pub const TAGS: u16 = 256;
pub const PRUNE: u16 = 512;
pub const FOLLOW_TAGS: u16 = 1024;
pub const REPO: u16 = 2048;
pub const PORCELAIN: u16 = 4096;
pub const SET_UPSTREAM: u16 = 8192;
pub const NO_VERIFY: u16 = 16384;
pub const RECURSE_SUBMODULES: u16 = 32768;
pub const PUSH_TABLE: &[OptionSpec] = &[
row(UNREAD, Some(b'v'), Some("verbose"), Arity::None, true),
row(UNREAD, Some(b'q'), Some("quiet"), Arity::None, true),
row(REPO, None, Some("repo"), Arity::Required, true),
row(ALL, None, Some("all"), Arity::None, true),
row(ALL, None, Some("branches"), Arity::None, true),
row(MIRROR, None, Some("mirror"), Arity::None, true),
row(DELETE, Some(b'd'), Some("delete"), Arity::None, true),
row(TAGS, None, Some("tags"), Arity::None, true),
row(DRY_RUN, Some(b'n'), Some("dry-run"), Arity::None, true),
row(PORCELAIN, None, Some("porcelain"), Arity::None, true),
row(FORCE, Some(b'f'), Some("force"), Arity::None, true),
row(
FORCE_WITH_LEASE,
None,
Some("force-with-lease"),
Arity::Optional,
true,
),
row(FORCE_IF_INCLUDES, None, Some("force-if-includes"), Arity::None, true),
row(
RECURSE_SUBMODULES,
None,
Some("recurse-submodules"),
Arity::Required,
true,
),
row(UNREAD, None, Some("thin"), Arity::None, true),
row(UNREAD, None, Some("receive-pack"), Arity::Required, true),
row(UNREAD, None, Some("exec"), Arity::Required, true),
row(SET_UPSTREAM, Some(b'u'), Some("set-upstream"), Arity::None, true),
row(UNREAD, None, Some("progress"), Arity::None, true),
row(PRUNE, None, Some("prune"), Arity::None, true),
row(NO_VERIFY, None, Some("no-verify"), Arity::None, true),
row(FOLLOW_TAGS, None, Some("follow-tags"), Arity::None, true),
row(UNREAD, None, Some("signed"), Arity::Optional, true),
row(ATOMIC, None, Some("atomic"), Arity::None, true),
row(
UNREAD,
Some(b'o'),
Some("push-option"),
Arity::Required,
true,
),
row(UNREAD, Some(b'4'), Some("ipv4"), Arity::None, false),
row(UNREAD, Some(b'6'), Some("ipv6"), Arity::None, false),
];
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PushRegion {
pub atomic_stated: bool,
pub dry_run: bool,
pub no_verify: bool,
pub wrapper: Vec<WrapperOccurrence>,
}
pub fn parse_push_region(
region: &[OsString],
wrapper_flags: &[&[u8]],
) -> Result<PushRegion, OptionError> {
let parsed: ParsedOptions = parse_options(region, PUSH_TABLE, DEFAULT_MODE, wrapper_flags)?;
return Ok(PushRegion {
atomic_stated: is_stated(&parsed, ATOMIC),
dry_run: is_enabled(&parsed, DRY_RUN),
no_verify: is_enabled(&parsed, NO_VERIFY),
wrapper: parsed.wrapper,
});
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PushShape {
pub repository: Option<OsString>,
pub refspecs: Vec<OsString>,
pub branches: bool,
pub tags: bool,
pub mirror: bool,
pub deletes: bool,
pub recurse_submodules: Option<OsString>,
}
fn option_value(parsed: &ParsedOptions, region: &[OsString], id: u16) -> Option<OsString> {
let occurrence = last_occurrence(parsed, id)?;
let value = occurrence.value?;
return Some(<OsString as std::os::unix::ffi::OsStringExt>::from_vec(value_bytes(region, value).to_vec()));
}
pub fn push_shape(region: &[OsString]) -> Option<PushShape> {
let parsed: ParsedOptions = parse_options(region, PUSH_TABLE, DEFAULT_MODE, &[]).ok()?;
let mut positionals: Vec<OsString> = Vec::new();
for index in positional_tokens(&parsed, region.len()) {
positionals.push(region[index].clone());
}
let repository: Option<OsString> =
if positionals.is_empty() { option_value(&parsed, region, REPO) } else { Some(positionals.remove(0)) };
let mut refspecs: Vec<OsString> = Vec::new();
let mut words = positionals.into_iter();
while let Some(word) = words.next() {
if word.as_encoded_bytes() == b"tag"
&& let Some(name) = words.next()
{
let mut joined: OsString = OsString::from("refs/tags/");
joined.push(name);
refspecs.push(joined);
} else {
refspecs.push(word);
}
}
return Some(PushShape {
repository,
refspecs,
branches: is_enabled(&parsed, ALL),
tags: is_enabled(&parsed, TAGS),
mirror: is_enabled(&parsed, MIRROR),
deletes: is_enabled(&parsed, DELETE),
recurse_submodules: option_value(&parsed, region, RECURSE_SUBMODULES),
});
}
const NOT_STACKABLE: &[u16] = &[
DRY_RUN,
FORCE,
FORCE_WITH_LEASE,
FORCE_IF_INCLUDES,
MIRROR,
DELETE,
ALL,
TAGS,
PRUNE,
FOLLOW_TAGS,
REPO,
PORCELAIN,
SET_UPSTREAM,
];
pub fn stackable_option_tokens(region: &[OsString]) -> Option<Vec<OsString>> {
let parsed: ParsedOptions = parse_options(region, PUSH_TABLE, DEFAULT_MODE, &[]).ok()?;
for id in NOT_STACKABLE {
if is_enabled(&parsed, *id) {
return None;
}
}
let positionals: Vec<usize> = positional_tokens(&parsed, region.len());
for index in &positionals {
if region[*index].as_encoded_bytes().first() == Some(&b'+') {
return None;
}
}
let mut tokens: Vec<OsString> = Vec::new();
for (index, token) in region.iter().enumerate() {
if !positionals.contains(&index) {
tokens.push(token.clone());
}
}
return Some(tokens);
}
#[cfg(test)]
#[path = "command_push_tests.rs"]
mod tests;