mirror of
https://github.com/OMGeeky/google-apis-rs.git
synced 2026-01-02 17:42:16 +01:00
feat(CLI): struct value parsing
This works already for simple request values, but doens't generate compiling code for structures with Parts in them. Nonetheless, it's a big step towards finishing the overall issue. Related to #64
This commit is contained in:
@@ -168,21 +168,24 @@ ${SPLIT_END}
|
||||
for (fndfi, v) in enumerate(cursor):
|
||||
if v != FIELD_SEP:
|
||||
break
|
||||
return '-%s %s ' % (STRUCT_FLAG, ''.join(cursor[:fndfi]) + FIELD_SEP.join(cursor[fndfi:]))
|
||||
res = ''.join(cursor[:fndfi]) + FIELD_SEP.join(cursor[fndfi:])
|
||||
if not res.endswith(FIELD_SEP):
|
||||
res += FIELD_SEP
|
||||
return res
|
||||
|
||||
def cursor_arg():
|
||||
def cursor_arg(field):
|
||||
prefix = ''
|
||||
if cursor_tokens:
|
||||
res = cursor_fmt(cursor_tokens)
|
||||
prefix = cursor_fmt(cursor_tokens)
|
||||
del cursor_tokens[:]
|
||||
return res
|
||||
return ''
|
||||
return prefix + field
|
||||
%>\
|
||||
% for fn in sorted(schema.fields.keys()):
|
||||
<%
|
||||
f = schema.fields[fn]
|
||||
%>\
|
||||
% if isinstance(f, SchemaEntry):
|
||||
* **${cursor_arg()}-${STRUCT_FLAG} ${mangle_subcommand(fn)}=${field_to_value(f)}**
|
||||
* **-${STRUCT_FLAG} ${cursor_arg(mangle_subcommand(fn))}=${field_to_value(f)}**
|
||||
- ${f.property.get('description', NO_DESC) | xml_escape, indent_all_but_first_by(2)}
|
||||
% if f.container_type == CTYPE_ARRAY:
|
||||
- Each invocation of this argument appends the given value to the array.
|
||||
|
||||
@@ -14,6 +14,7 @@ STRUCT_FLAG = 'r'
|
||||
UPLOAD_FLAG = 'u'
|
||||
OUTPUT_FLAG = 'o'
|
||||
VALUE_ARG = 'v'
|
||||
KEY_VALUE_ARG = 'kv'
|
||||
SCOPE_FLAG = 'scope'
|
||||
CONFIG_DIR_FLAG = 'config-dir'
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
from util import (put_and, supports_scopes)
|
||||
from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG,
|
||||
CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG,
|
||||
CONFIG_DIR_FLAG)
|
||||
|
||||
v_arg = '<%s>' % VALUE_ARG
|
||||
CONFIG_DIR_FLAG, KEY_VALUE_ARG)
|
||||
%>\
|
||||
<%def name="new(c)">\
|
||||
<%
|
||||
@@ -29,7 +27,7 @@ Usage:
|
||||
# end for each required property
|
||||
|
||||
if mc.request_value:
|
||||
args.append('-%s %s...' % (STRUCT_FLAG, v_arg))
|
||||
args.append('-%s %s...' % (STRUCT_FLAG, '<%s>' % KEY_VALUE_ARG))
|
||||
struct_used = True
|
||||
# end request_value
|
||||
|
||||
@@ -41,7 +39,7 @@ Usage:
|
||||
# end upload handling
|
||||
|
||||
if mc.optional_props or parameters is not UNDEFINED:
|
||||
args.append('[-%s %s]...' % (PARAM_FLAG, v_arg))
|
||||
args.append('[-%s %s]...' % (PARAM_FLAG, '<%s>' % VALUE_ARG))
|
||||
param_used = True
|
||||
# end paramters
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
upload_action_fn)
|
||||
from cli import (mangle_subcommand, new_method_context, PARAM_FLAG, STRUCT_FLAG, UPLOAD_FLAG, OUTPUT_FLAG, VALUE_ARG,
|
||||
CONFIG_DIR, SCOPE_FLAG, is_request_value_property, FIELD_SEP, docopt_mode, FILE_ARG, MIME_ARG, OUT_ARG,
|
||||
cmd_ident, call_method_ident, arg_ident, POD_TYPES, flag_ident, ident, JSON_TYPE_VALUE_MAP)
|
||||
cmd_ident, call_method_ident, arg_ident, POD_TYPES, flag_ident, ident, JSON_TYPE_VALUE_MAP,
|
||||
KEY_VALUE_ARG, to_cli_schema, SchemaEntry, CTYPE_POD)
|
||||
|
||||
v_arg = '<%s>' % VALUE_ARG
|
||||
SOPT = 'self.opt.'
|
||||
@@ -26,7 +27,7 @@
|
||||
%>\
|
||||
mod cmn;
|
||||
use cmn::{InvalidOptionsError, CLIError, JsonTokenStorage, arg_from_str, writer_from_opts, parse_kv_arg,
|
||||
input_file_from_opts, input_mime_from_opts};
|
||||
input_file_from_opts, input_mime_from_opts, FieldCursor, FieldError};
|
||||
|
||||
use std::default::Default;
|
||||
use std::str::FromStr;
|
||||
@@ -145,6 +146,8 @@ self.opt.${cmd_ident(method)} {
|
||||
if parameters is not UNDEFINED:
|
||||
global_parameter_names = list(pn for pn in sorted(parameters.keys()) if pn not in optional_prop_names)
|
||||
handle_props = optional_props or parameters is not UNDEFINED
|
||||
if mc.request_value:
|
||||
request_cli_schema = to_cli_schema(c, mc.request_value)
|
||||
%>\
|
||||
## REQUIRED PARAMETERS
|
||||
% for p in mc.required_props:
|
||||
@@ -154,7 +157,7 @@ self.opt.${cmd_ident(method)} {
|
||||
opt_ident = to_opt_arg_ident(p)
|
||||
%>\
|
||||
% if is_request_value_property(mc, p):
|
||||
let ${prop_name}: api::${prop_type} = Default::default();
|
||||
let mut ${prop_name}: api::${prop_type} = Default::default();
|
||||
% elif p.type != 'string':
|
||||
let ${prop_name}: ${prop_type} = arg_from_str(&${opt_ident}, err, "<${mangle_subcommand(p.name)}>", "${p.type}");
|
||||
% endif # handle request value
|
||||
@@ -194,7 +197,7 @@ for parg in ${SOPT + arg_ident(VALUE_ARG)}.iter() {
|
||||
% endif
|
||||
call = call.${mangle_ident(setter_fn_name(p))}(\
|
||||
% if ptype != 'string':
|
||||
arg_from_str(${value_unwrap}, err, "${mangle_subcommand(p.name)}", "${p.type}")\
|
||||
arg_from_str(${value_unwrap}, err, "${mangle_subcommand(p.name)}", "${ptype}")\
|
||||
% else:
|
||||
${value_unwrap}\
|
||||
% endif # handle conversion
|
||||
@@ -233,6 +236,68 @@ ${value_unwrap}\
|
||||
}
|
||||
}
|
||||
% endif # handle call parameters
|
||||
% if mc.request_value:
|
||||
<%
|
||||
def flatten_schema_fields(schema, res, cur=list()):
|
||||
if len(cur) == 0:
|
||||
cur = list()
|
||||
for fn, f in schema.fields.iteritems():
|
||||
cur.append(fn)
|
||||
if isinstance(f, SchemaEntry):
|
||||
res.append((f, list(cur)))
|
||||
else:
|
||||
flatten_schema_fields(f, res, cur)
|
||||
cur.pop()
|
||||
# endfor
|
||||
# end utility
|
||||
|
||||
schema_fields = list()
|
||||
flatten_schema_fields(request_cli_schema, schema_fields)
|
||||
%>\
|
||||
let mut field_name: FieldCursor = Default::default();
|
||||
for kvarg in ${SOPT + arg_ident(KEY_VALUE_ARG)}.iter() {
|
||||
let (key, value) = parse_kv_arg(&*kvarg, err);
|
||||
if let Err(field_err) = field_name.set(&*key) {
|
||||
err.issues.push(field_err);
|
||||
}
|
||||
match &field_name.to_string()[..] {
|
||||
% for fv, f in schema_fields:
|
||||
<%
|
||||
# TODO: Deduplicate !
|
||||
ptype = fv.actual_property.type
|
||||
if ptype == 'string' and 'Count' in f[-1]:
|
||||
ptype = 'int64'
|
||||
value_unwrap = 'value.unwrap_or("%s")' % JSON_TYPE_VALUE_MAP[ptype]
|
||||
pname = FIELD_SEP.join(mangle_subcommand(ft) for ft in f)
|
||||
|
||||
struct_field = 'request.' + '.'.join('%s.as_mut().unwrap()' % mangle_ident(ft) for ft in f[:-1])
|
||||
if len(f) > 1:
|
||||
struct_field += '.'
|
||||
struct_field += mangle_ident(f[-1])
|
||||
%>\
|
||||
"${pname}" => {
|
||||
% if fv.container_type == CTYPE_POD:
|
||||
${struct_field} = Some(\
|
||||
% else:
|
||||
if ${struct_field}.is_none() {
|
||||
${struct_field} = Some(Default::default());
|
||||
}
|
||||
${struct_field}.as_mut().unwrap().push(\
|
||||
% endif
|
||||
% if ptype != 'string':
|
||||
arg_from_str(${value_unwrap}, err, "${pname}", "${ptype}")\
|
||||
% else:
|
||||
${value_unwrap}.to_string()\
|
||||
% endif
|
||||
);
|
||||
},
|
||||
% endfor # each nested field
|
||||
_ => {
|
||||
err.issues.push(CLIError::Field(FieldError::Unknown(field_name.to_string())));
|
||||
}
|
||||
}
|
||||
}
|
||||
% endif # handle struct parsing
|
||||
% if mc.media_params:
|
||||
let protocol =
|
||||
% for p in mc.media_params:
|
||||
|
||||
@@ -13,7 +13,7 @@ use std::io::{Write, Read, stdout};
|
||||
|
||||
use std::default::Default;
|
||||
|
||||
const FIELD_SEP: char = 'c';
|
||||
const FIELD_SEP: char = '.';
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FieldCursor(Vec<String>);
|
||||
@@ -26,6 +26,10 @@ impl ToString for FieldCursor {
|
||||
|
||||
impl FieldCursor {
|
||||
pub fn set(&mut self, value: &str) -> Result<(), CLIError> {
|
||||
if value.len() == 0 {
|
||||
return Err(CLIError::Field(FieldError::Empty))
|
||||
}
|
||||
|
||||
let mut first_is_field_sep = false;
|
||||
let mut char_count: usize = 0;
|
||||
let mut last_c = FIELD_SEP;
|
||||
@@ -34,24 +38,24 @@ impl FieldCursor {
|
||||
let mut field = String::new();
|
||||
let mut fields = self.0.clone();
|
||||
|
||||
let push_field = |fields: &mut Vec<String>, field: &mut String| {
|
||||
if field.len() > 0 {
|
||||
fields.push(field.clone());
|
||||
field.truncate(0);
|
||||
let push_field = |fs: &mut Vec<String>, f: &mut String| {
|
||||
if f.len() > 0 {
|
||||
fs.push(f.clone());
|
||||
f.truncate(0);
|
||||
}
|
||||
};
|
||||
|
||||
for (cid, c) in value.chars().enumerate() {
|
||||
char_count = cid + 1;
|
||||
char_count += 1;
|
||||
|
||||
if cid == 0 && c == FIELD_SEP {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
if c == FIELD_SEP {
|
||||
if cid == 0 {
|
||||
first_is_field_sep = true;
|
||||
}
|
||||
num_conscutive_field_seps += 1;
|
||||
if last_c == FIELD_SEP {
|
||||
if cid > 0 && last_c == FIELD_SEP {
|
||||
if fields.pop().is_none() {
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty))
|
||||
return Err(CLIError::Field(FieldError::PopOnEmpty(value.to_string())))
|
||||
}
|
||||
} else {
|
||||
push_field(&mut fields, &mut field);
|
||||
@@ -75,7 +79,7 @@ impl FieldCursor {
|
||||
fields.truncate(0);
|
||||
}
|
||||
if char_count > 1 && num_conscutive_field_seps == 1 {
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep))
|
||||
return Err(CLIError::Field(FieldError::TrailingFieldSep(value.to_string())))
|
||||
}
|
||||
|
||||
self.0 = fields;
|
||||
@@ -88,7 +92,7 @@ impl FieldCursor {
|
||||
}
|
||||
|
||||
pub fn parse_kv_arg<'a>(kv: &'a str, err: &mut InvalidOptionsError)
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
-> (&'a str, Option<&'a str>) {
|
||||
let mut add_err = || err.issues.push(CLIError::InvalidKeyValueSyntax(kv.to_string()));
|
||||
match kv.rfind('=') {
|
||||
None => {
|
||||
@@ -252,18 +256,24 @@ impl fmt::Display for InputError {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum FieldError {
|
||||
PopOnEmpty,
|
||||
TrailingFieldSep,
|
||||
PopOnEmpty(String),
|
||||
TrailingFieldSep(String),
|
||||
Unknown(String),
|
||||
Empty,
|
||||
}
|
||||
|
||||
|
||||
impl fmt::Display for FieldError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FieldError::PopOnEmpty
|
||||
=> writeln!(f, "Cannot move up on empty field cursor"),
|
||||
FieldError::TrailingFieldSep
|
||||
=> writeln!(f, "Single field separator may not be last character"),
|
||||
FieldError::PopOnEmpty(ref field)
|
||||
=> writeln!(f, "'{}': Cannot move up on empty field cursor", field),
|
||||
FieldError::TrailingFieldSep(ref field)
|
||||
=> writeln!(f, "'{}': Single field separator may not be last character", field),
|
||||
FieldError::Unknown(ref field)
|
||||
=> writeln!(f, "Field '{}' does not exist", field),
|
||||
FieldError::Empty
|
||||
=> writeln!(f, "Field names must not be empty"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user