mirror of
https://github.com/facebook/sapling.git
synced 2024-10-10 08:47:12 +03:00
Back out "Improved internal representation of GetbundleArgs.bundlecaps"
Summary: mononoke: Original commit changeset: 6cb5124c7893 Reviewed By: StanislavGlebik Differential Revision: D13751021 fbshipit-source-id: b80da7ebbaaca3324078efda15704c185050b35f
This commit is contained in:
parent
234c33a241
commit
aa4a1deb0a
@ -142,41 +142,8 @@ pub struct GetbundleArgs {
|
||||
pub heads: Vec<HgNodeHash>,
|
||||
/// List of space-delimited hex nodes that the client has in common with the server
|
||||
pub common: Vec<HgNodeHash>,
|
||||
/**
|
||||
* Bundlecaps complex object contains parsed bundlecaps entries.
|
||||
* First layer contains names of the groups as "treeonly" or "bundle2".
|
||||
* In some cases group can contain some value, like "bundle2=pushkey bookmarks",
|
||||
* it means that on next level it will contain map of this parameters.
|
||||
* In case if internal parameters have values, like "bundle2=changegroup=01,02" it will
|
||||
* be parsed in the last level - HashSet.
|
||||
*
|
||||
* Example of data that can be parsed in bundlecaps object
|
||||
* (url-encoded symbols were replaced for readability purposes):
|
||||
* {
|
||||
* "treeonly",
|
||||
* "bundle2=
|
||||
* HG20
|
||||
* bookmarks
|
||||
* changegroup=01,02
|
||||
* digests=md5,sha1,sha512
|
||||
* error=abort,unsupportedcontent,pushraced,pushkey
|
||||
* hgtagsfnodes
|
||||
* listkeys
|
||||
* phases=heads
|
||||
* pushkey
|
||||
* remote-changegroup=http,https
|
||||
* remotefilelog=True
|
||||
* treemanifest=True
|
||||
* treeonly=True",
|
||||
* "remotefilelog",
|
||||
* "HG20"
|
||||
* }
|
||||
*
|
||||
* To use this structure, you can simply navigate it with default collection api, eg:
|
||||
* bundlecaps.contains_key("treeonly")
|
||||
* bundlecaps["bundle2"]["changegroup"].contains("01")
|
||||
*/
|
||||
pub bundlecaps: HashMap<String, HashMap<String, HashSet<String>>>,
|
||||
/// Comma-delimited set of strings defining client bundle capabilities.
|
||||
pub bundlecaps: HashSet<Vec<u8>>,
|
||||
/// Comma-delimited list of strings of ``pushkey`` namespaces. For each namespace listed, a bundle2 part will be included with the content of that namespace.
|
||||
pub listkeys: Vec<Vec<u8>>,
|
||||
/// phases: Boolean indicating whether phases data is requested
|
||||
@ -185,8 +152,11 @@ pub struct GetbundleArgs {
|
||||
|
||||
impl Debug for GetbundleArgs {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let listkeys: Vec<_> = self
|
||||
.listkeys
|
||||
let bcaps: HashSet<_> = self.bundlecaps
|
||||
.iter()
|
||||
.map(|s| String::from_utf8_lossy(&s))
|
||||
.collect();
|
||||
let listkeys: Vec<_> = self.listkeys
|
||||
.iter()
|
||||
.map(|s| String::from_utf8_lossy(&s))
|
||||
.collect();
|
||||
@ -197,7 +167,7 @@ impl Debug for GetbundleArgs {
|
||||
.field("heads", &heads)
|
||||
.field("common_len", &self.common.len())
|
||||
.field("common", &common)
|
||||
.field("bundlecaps", &self.bundlecaps)
|
||||
.field("bundlecaps", &bcaps)
|
||||
.field("listkeys", &listkeys)
|
||||
.field("phases", &self.phases)
|
||||
.finish()
|
||||
|
@ -4,9 +4,8 @@
|
||||
// This software may be used and distributed according to the terms of the
|
||||
// GNU General Public License version 2 or any later version.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use std::iter;
|
||||
use std::iter::FromIterator;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
@ -14,10 +13,10 @@ use nom::{is_alphanumeric, is_digit, Err, ErrorKind, FindSubstring, IResult, Nee
|
||||
|
||||
use HgNodeHash;
|
||||
|
||||
use {GetbundleArgs, GettreepackArgs, Request, SingleRequest};
|
||||
use batch;
|
||||
use errors;
|
||||
use errors::*;
|
||||
use {GetbundleArgs, GettreepackArgs, Request, SingleRequest};
|
||||
|
||||
const BAD_UTF8_ERR_CODE: u32 = 111;
|
||||
|
||||
@ -46,40 +45,26 @@ named!(
|
||||
|
||||
/// Return an identifier of the form [a-zA-Z_][a-zA-Z0-9_]*. Returns Incomplete
|
||||
/// if it manages to reach the end of input, as there may be more identifier coming.
|
||||
fn ident_alphanum(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
fn ident(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
for (idx, item) in input.iter().enumerate() {
|
||||
match *item as char {
|
||||
'a'...'z' | 'A'...'Z' | '_' => continue,
|
||||
'0'...'9' if idx > 0 => continue,
|
||||
_ => {
|
||||
if idx > 0 {
|
||||
return IResult::Done(&input[idx..], &input[0..idx]);
|
||||
} else {
|
||||
return IResult::Error(Err::Code(ErrorKind::AlphaNumeric));
|
||||
}
|
||||
}
|
||||
_ => if idx > 0 {
|
||||
return IResult::Done(&input[idx..], &input[0..idx]);
|
||||
} else {
|
||||
return IResult::Error(Err::Code(ErrorKind::AlphaNumeric));
|
||||
},
|
||||
}
|
||||
}
|
||||
IResult::Incomplete(Needed::Unknown)
|
||||
}
|
||||
|
||||
/// Return an identifier of the form [a-zA-Z0-9_-]*.
|
||||
named!(
|
||||
ident,
|
||||
take_till1!(|ch| match ch as char {
|
||||
'0'...'9' | 'a'...'z' | 'A'...'Z' | '_' | '-' => false,
|
||||
_ => true,
|
||||
})
|
||||
);
|
||||
|
||||
/// As above, but assumes input is complete, so reaching the end of input means
|
||||
/// the identifier is the entire input.
|
||||
fn ident_complete<P>(parser: P) -> impl Fn(&[u8]) -> IResult<&[u8], &[u8]>
|
||||
where
|
||||
P: Fn(&[u8]) -> IResult<&[u8], &[u8]>,
|
||||
{
|
||||
move |input| match parser(input) {
|
||||
IResult::Incomplete(_) => IResult::Done(&b""[..], input),
|
||||
fn ident_complete(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
match ident(input) {
|
||||
IResult::Incomplete(_) => IResult::Done(b"", input),
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
@ -127,11 +112,7 @@ named!(
|
||||
named!(
|
||||
param_kv<HashMap<Vec<u8>, Vec<u8>>>,
|
||||
do_parse!(
|
||||
key: ident_alphanum
|
||||
>> tag!(b" ")
|
||||
>> len: integer
|
||||
>> tag!(b"\n")
|
||||
>> val: take!(len)
|
||||
key: ident >> tag!(b" ") >> len: integer >> tag!(b"\n") >> val: take!(len)
|
||||
>> (iter::once((key.to_vec(), val.to_vec())).collect())
|
||||
)
|
||||
);
|
||||
@ -256,8 +237,7 @@ fn notsemi(b: u8) -> bool {
|
||||
named!(
|
||||
cmd<(Vec<u8>, Vec<u8>)>,
|
||||
do_parse!(
|
||||
cmd: take_until_and_consume1!(" ")
|
||||
>> args: take_while!(notsemi)
|
||||
cmd: take_until_and_consume1!(" ") >> args: take_while!(notsemi)
|
||||
>> ((cmd.to_vec(), args.to_vec()))
|
||||
)
|
||||
);
|
||||
@ -374,13 +354,14 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
named!(ident_string<&[u8], String>,
|
||||
map!(ident_complete(ident), |x| String::from_utf8_lossy(x).into_owned())
|
||||
);
|
||||
|
||||
named!(ident_string_alphanum<&[u8], String>,
|
||||
map!(ident_complete(ident_alphanum), |x| String::from_utf8_lossy(x).into_owned())
|
||||
);
|
||||
/// Parse an ident, and map it to `String`.
|
||||
fn ident_string(inp: &[u8]) -> IResult<&[u8], String> {
|
||||
match ident_complete(inp) {
|
||||
IResult::Done(rest, s) => IResult::Done(rest, String::from_utf8_lossy(s).into_owned()),
|
||||
IResult::Incomplete(n) => IResult::Incomplete(n),
|
||||
IResult::Error(e) => IResult::Error(e),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse utf8 string, assumes that input is complete
|
||||
fn utf8_string_complete(inp: &[u8]) -> IResult<&[u8], String> {
|
||||
@ -395,55 +376,8 @@ fn bytes_complete(inp: &[u8]) -> IResult<&[u8], Bytes> {
|
||||
IResult::Done(b"", res)
|
||||
}
|
||||
|
||||
// Tags encoded in url-encode format which used in bundlecaps args
|
||||
const SPACE_TAG: &str = "%0A";
|
||||
const EQUALS_TAG: &str = "%3D";
|
||||
const COMMA_TAG: &str = "%2C";
|
||||
|
||||
// Parses list of bundlecaps values separated with encoded comma: 1,x,y
|
||||
named!(bundlecaps_values_list<&[u8], HashSet<String>>,
|
||||
alt_complete!(
|
||||
map!(separated_list_complete!(tag!(COMMA_TAG), ident_string), HashSet::from_iter) |
|
||||
map!(ident_string, |id| HashSet::from_iter(vec!(id)))
|
||||
)
|
||||
);
|
||||
|
||||
// Parses bundlecap param with encoded commas and equal chars: list=1,x,y
|
||||
named!(bundlecaps_param<&[u8], (String, HashSet<String>)>,
|
||||
alt_complete!(
|
||||
separated_pair!(ident_string, tag!(EQUALS_TAG), bundlecaps_values_list) |
|
||||
map!(ident_string, |id| (id, HashSet::new()))
|
||||
)
|
||||
);
|
||||
|
||||
// Parses bundlecap params with encoded commas, equal and empty space chars: "block list=1,x,y"
|
||||
named!(bundlecaps_params<&[u8], HashMap<String, HashSet<String>>>,
|
||||
do_parse!(
|
||||
char!('=') >>
|
||||
data: separated_list_complete!(tag!(SPACE_TAG), bundlecaps_param) >>
|
||||
(HashMap::from_iter(data))
|
||||
)
|
||||
);
|
||||
|
||||
// Parses bundlecap argument
|
||||
named!(
|
||||
bundlecaps_arg<&[u8], (String, HashMap<String, HashSet<String>>)>,
|
||||
alt_complete!(
|
||||
pair!(ident_string, bundlecaps_params) |
|
||||
map!(ident_string, |x| (x, HashMap::new()))
|
||||
)
|
||||
);
|
||||
|
||||
// Parses bundlecap arguments list
|
||||
named!(
|
||||
bundlecaps_args<&[u8], HashMap<String, HashMap<String, HashSet<String>>>>,
|
||||
map!(separated_list_complete!(char!(','), bundlecaps_arg), HashMap::from_iter)
|
||||
);
|
||||
|
||||
macro_rules! replace_expr {
|
||||
($_t:tt $sub:expr) => {
|
||||
$sub
|
||||
};
|
||||
($_t:tt $sub:expr) => {$sub};
|
||||
}
|
||||
|
||||
macro_rules! count_tts {
|
||||
@ -569,8 +503,8 @@ fn parse_with_params(
|
||||
| command!("capabilities", Capabilities, parse_params, {})
|
||||
| call!(parse_command, "debugwireargs", parse_params, 2+1,
|
||||
|kv| Ok(Debugwireargs {
|
||||
one: parseval(&kv, "one", ident_complete(ident_alphanum))?.to_vec(),
|
||||
two: parseval(&kv, "two", ident_complete(ident_alphanum))?.to_vec(),
|
||||
one: parseval(&kv, "one", ident_complete)?.to_vec(),
|
||||
two: parseval(&kv, "two", ident_complete)?.to_vec(),
|
||||
all_args: kv,
|
||||
}))
|
||||
| call!(parse_command, "getbundle", parse_params, 0+1,
|
||||
@ -582,14 +516,14 @@ fn parse_with_params(
|
||||
// If those params are needed, they should be parsed here.
|
||||
heads: parseval_default(&kv, "heads", hashlist)?,
|
||||
common: parseval_default(&kv, "common", hashlist)?,
|
||||
bundlecaps: parseval_default(&kv, "bundlecaps", bundlecaps_args)?,
|
||||
bundlecaps: parseval_default(&kv, "bundlecaps", commavalues)?.into_iter().collect(),
|
||||
listkeys: parseval_default(&kv, "listkeys", commavalues)?,
|
||||
phases: parseval_default(&kv, "phases", boolean)?,
|
||||
})))
|
||||
| command!("heads", Heads, parse_params, {})
|
||||
| command!("hello", Hello, parse_params, {})
|
||||
| command!("listkeys", Listkeys, parse_params, {
|
||||
namespace => ident_string_alphanum,
|
||||
namespace => ident_string,
|
||||
})
|
||||
| command!("lookup", Lookup, parse_params, {
|
||||
key => utf8_string_complete,
|
||||
@ -632,32 +566,22 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_ident() {
|
||||
assert_eq!(ident(b"01 "), IResult::Done(&b" "[..], &b"01"[..]));
|
||||
assert_eq!(ident(b"foo"), IResult::Done(&b""[..], &b"foo"[..]));
|
||||
assert_eq!(
|
||||
ident(b"1234 "),
|
||||
IResult::Error(Err::Code(ErrorKind::AlphaNumeric))
|
||||
);
|
||||
assert_eq!(
|
||||
ident(b" 1234 "),
|
||||
IResult::Error(Err::Code(ErrorKind::AlphaNumeric))
|
||||
);
|
||||
assert_eq!(ident(b"foo"), IResult::Incomplete(Needed::Unknown));
|
||||
assert_eq!(ident(b"foo "), IResult::Done(&b" "[..], &b"foo"[..]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ident_alphanum() {
|
||||
assert_eq!(
|
||||
ident_alphanum(b"1234 "),
|
||||
IResult::Error(Err::Code(ErrorKind::AlphaNumeric))
|
||||
);
|
||||
assert_eq!(
|
||||
ident_alphanum(b" 1234 "),
|
||||
IResult::Error(Err::Code(ErrorKind::AlphaNumeric))
|
||||
);
|
||||
assert_eq!(ident_alphanum(b"foo"), IResult::Incomplete(Needed::Unknown));
|
||||
assert_eq!(
|
||||
ident_alphanum(b"foo "),
|
||||
IResult::Done(&b" "[..], &b"foo"[..])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_param_star() {
|
||||
let p = b"* 0\ntrailer";
|
||||
assert_eq!(param_star(p), IResult::Done(&b"trailer"[..], hashmap! {}));
|
||||
assert_eq!(param_star(p), IResult::Done(&b"trailer"[..], hashmap!{}));
|
||||
|
||||
let p = b"* 1\n\
|
||||
foo 12\n\
|
||||
@ -690,7 +614,7 @@ mod test {
|
||||
|
||||
// no trailer
|
||||
let p = b"* 0\n";
|
||||
assert_eq!(param_star(p), IResult::Done(&b""[..], hashmap! {}));
|
||||
assert_eq!(param_star(p), IResult::Done(&b""[..], hashmap!{}));
|
||||
|
||||
let p = b"* 1\n\
|
||||
foo 12\n\
|
||||
@ -885,7 +809,7 @@ mod test {
|
||||
batch_params(p, 0),
|
||||
IResult::Done(
|
||||
&b""[..],
|
||||
hashmap! {
|
||||
hashmap!{
|
||||
b"foo".to_vec() => b"bar".to_vec(),
|
||||
}
|
||||
)
|
||||
@ -897,7 +821,7 @@ mod test {
|
||||
batch_params(p, 0),
|
||||
IResult::Done(
|
||||
&b""[..],
|
||||
hashmap! {
|
||||
hashmap!{
|
||||
b"foo".to_vec() => b"bar".to_vec(),
|
||||
b"biff".to_vec() => b"bop".to_vec(),
|
||||
b"esc:,;=".to_vec() => b"esc:,;=".to_vec(),
|
||||
@ -907,13 +831,13 @@ mod test {
|
||||
|
||||
let p = b"";
|
||||
|
||||
assert_eq!(batch_params(p, 0), IResult::Done(&b""[..], hashmap! {}));
|
||||
assert_eq!(batch_params(p, 0), IResult::Done(&b""[..], hashmap!{}));
|
||||
|
||||
let p = b"foo=";
|
||||
|
||||
assert_eq!(
|
||||
batch_params(p, 0),
|
||||
IResult::Done(&b""[..], hashmap! {b"foo".to_vec() => b"".to_vec()})
|
||||
IResult::Done(&b""[..], hashmap!{b"foo".to_vec() => b"".to_vec()})
|
||||
);
|
||||
}
|
||||
|
||||
@ -1290,7 +1214,7 @@ mod test_parse {
|
||||
Request::Single(SingleRequest::Getbundle(GetbundleArgs {
|
||||
heads: vec![],
|
||||
common: vec![],
|
||||
bundlecaps: HashMap::new(),
|
||||
bundlecaps: hashset![],
|
||||
listkeys: vec![],
|
||||
phases: false,
|
||||
})),
|
||||
@ -1317,57 +1241,13 @@ mod test_parse {
|
||||
Request::Single(SingleRequest::Getbundle(GetbundleArgs {
|
||||
heads: vec![hash_ones()],
|
||||
common: vec![hash_twos(), hash_threes()],
|
||||
bundlecaps: ["cap1", "CAP2", "cap3"]
|
||||
.iter()
|
||||
.map(|s| (s.to_string(), HashMap::new()))
|
||||
.collect(),
|
||||
bundlecaps: hashset![b"cap1".to_vec(), b"CAP2".to_vec(), b"cap3".to_vec()],
|
||||
listkeys: vec![b"key1".to_vec(), b"key2".to_vec()],
|
||||
phases: true,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_getbundle_bundlecaps() {
|
||||
let inp = "getbundle\n\
|
||||
* 1\n\
|
||||
bundlecaps 51\n\
|
||||
block,entries=entry%0Alist%3D1%2Cx%2Cy%0Asingle%3Dx";
|
||||
|
||||
let expected_entries_list: HashSet<String> =
|
||||
["1", "x", "y"].iter().map(|x| x.to_string()).collect();
|
||||
|
||||
let expected_single: HashSet<String> = ["x"].iter().map(|x| x.to_string()).collect();
|
||||
|
||||
let expected_entries: HashMap<String, HashSet<String>> = [
|
||||
("entry".to_string(), HashSet::new()),
|
||||
("list".to_string(), expected_entries_list),
|
||||
("single".to_string(), expected_single),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
let expected_bundlecaps: HashMap<String, HashMap<String, HashSet<String>>> = [
|
||||
("block".to_string(), HashMap::new()),
|
||||
("entries".to_string(), expected_entries),
|
||||
]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
test_parse(
|
||||
inp,
|
||||
Request::Single(SingleRequest::Getbundle(GetbundleArgs {
|
||||
heads: vec![],
|
||||
common: vec![],
|
||||
bundlecaps: expected_bundlecaps,
|
||||
listkeys: vec![],
|
||||
phases: false,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_heads() {
|
||||
let inp = "heads\n";
|
||||
|
@ -338,13 +338,9 @@ impl RepoClient {
|
||||
.map(|head| HgChangesetId::new(head))
|
||||
.collect(),
|
||||
self.lca_hint.clone(),
|
||||
if args.phases
|
||||
&& args.bundlecaps
|
||||
.get("bundle2")
|
||||
.and_then(|x| x.get("phases"))
|
||||
.filter(|x| x.contains("heads"))
|
||||
.is_some()
|
||||
{
|
||||
// TODO (liubovd): We will need to check that common phases exchange method is supported
|
||||
// T38356449
|
||||
if args.phases {
|
||||
Some(self.phases_hint.clone())
|
||||
} else {
|
||||
None
|
||||
@ -718,7 +714,7 @@ impl HgCommands for RepoClient {
|
||||
info!(self.ctx.logger(), "Getbundle: {:?}", args);
|
||||
|
||||
let value = json!({
|
||||
"bundlecaps": &args.bundlecaps,
|
||||
"bundlecaps": format_utf8_bytes_list(&args.bundlecaps),
|
||||
"common": format_nodes_list(&args.common),
|
||||
"heads": format_nodes_list(&args.heads),
|
||||
"listkeys": format_utf8_bytes_list(&args.listkeys),
|
||||
|
Loading…
Reference in New Issue
Block a user