From a4b0f56b8c984a72e56dec092ad6c5f9d49015fc Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Sun, 22 Oct 2023 19:43:20 +0200 Subject: [PATCH] a building version of the enums-change (cli is broken). (at least youtube3 builds, the others get generated successfully) --- etc/api/type-api.yaml | 2 + src/generator/lib/enum_utils.py | 96 +++++++++++++++++++ src/generator/lib/util.py | 23 +++-- src/generator/templates/api/api/enums.rs.mako | 25 +++++ src/generator/templates/api/api/mod.rs.mako | 3 + src/generator/templates/api/lib/enum.mako | 50 +++++++++- src/generator/templates/api/lib/mbuild.mako | 12 +-- src/generator/templates/api/lib/rbuild.mako | 3 +- 8 files changed, 200 insertions(+), 14 deletions(-) create mode 100644 src/generator/lib/enum_utils.py create mode 100644 src/generator/templates/api/api/enums.rs.mako diff --git a/etc/api/type-api.yaml b/etc/api/type-api.yaml index 8f68b9a6fb..99fe0a9502 100644 --- a/etc/api/type-api.yaml +++ b/etc/api/type-api.yaml @@ -32,6 +32,8 @@ make: output_dir: src - source: api/call_builders.rs output_dir: src + - source: api/enums.rs + output_dir: src cargo: keywords: [protocol, web, api] doc_base_url: https://docs.rs diff --git a/src/generator/lib/enum_utils.py b/src/generator/lib/enum_utils.py new file mode 100644 index 0000000000..98ddf573f5 --- /dev/null +++ b/src/generator/lib/enum_utils.py @@ -0,0 +1,96 @@ +from .rust_type import RustType +from .types import Base +from .util import Context, UNUSED_TYPE_MARKER, schema_markers, canonical_type_name, remove_invalid_chars_in_ident, \ + singular + + +def is_property_enum(s: dict) -> bool: + enum = s.get("enum") + enum_description = s.get("enumDescriptions") + if enum is None: + return False + if enum_description is None: + print("could not find enumDescriptions for enum in s", s) + return True + + +def get_enum_type(schema_name: str, property_name: str, t, s) -> RustType: + if schema_name is None: + # print('schema_name was None:', property_name, t) + schema_name = "" + if len(schema_name) == 0: + schema = "" + else: + schema = remove_invalid_chars_in_ident(singular(schema_name)) + name = canonical_type_name(schema + + remove_invalid_chars_in_ident(canonical_type_name(property_name)) + + "Enum") + # print(name, '\n') + return Base(name) + + +def _add_enum_value(k, pk, pv, enums: dict): + if is_property_enum(pv): + enum_type = get_enum_type(k, pk, None, None) + # print('enum type:', enum_type) + enums[enum_type] = (k, pk, enum_type, pv) + return enums + + inner_pv = _get_inner_enum(pv) + if inner_pv: + return _add_enum_value(k, pk, inner_pv, enums) + + return enums + + +def _get_inner_enum(pv: dict): + items = pv.get('items') + if items and is_property_enum(items): + return items + additional = pv.get('additionalProperties') + if additional and is_property_enum(additional): + return additional + + return None + + +def _parse_method_id(method_id: str, c: Context) -> str: + parts = method_id.split('.') + if len(parts) != 3: + return method_id + context = c.rtc_map.get(parts[1]) + + if context is None or parts[0] != context: + print('context was not equal to first part: ', context, parts[0], method_id) + return method_id + + methods = c.rta_map.get(parts[1]) + if methods is None or parts[2] not in methods: + print('third part was not in methods', methods, parts[2], method_id) + + return parts[1] + + +def find_enums_in_context(c: Context) -> list: + enums = {} + for k, s in c.schemas.items(): + if UNUSED_TYPE_MARKER not in schema_markers(s, c, transitive=True): + # properties = s.get('properties') + # printed_name = False + if s.properties: + for pv, pk in zip(s.properties.values(), s.properties.keys()): + enums = _add_enum_value(k, pk, pv, enums) + + for k, v in c.fqan_map.items(): + # print(k) + k = _parse_method_id(k, c) + if v.parameters: + for pk, pv in v.parameters.items(): + enums = _add_enum_value(k, pk, pv, enums) + return list(enums.values()) + + +def to_enum_variant_name(name: str) -> str: + c_name = canonical_type_name(name) + c_name = remove_invalid_chars_in_ident(c_name) + return c_name diff --git a/src/generator/lib/util.py b/src/generator/lib/util.py index 1c93035c5b..467950cdb7 100644 --- a/src/generator/lib/util.py +++ b/src/generator/lib/util.py @@ -371,9 +371,16 @@ def nested_type_name(sn, pn): # Make properties which are reserved keywords usable def mangle_ident(n): - n = camel_to_under(n).replace('-', '.').replace('.', '_').replace('$', '') + n = camel_to_under(n) + return remove_invalid_chars_in_ident(n) + + +def remove_invalid_chars_in_ident(n): + n = n.replace('-', '.').replace('.', '_').replace('$', '') if n in RESERVED_WORDS: - return n + '_' + n = n + '_' + if n[0] in '0123456789': + n = '_' + n return n @@ -421,6 +428,7 @@ def to_rust_type_inner( allow_optionals=True, _is_recursive=False ) -> RustType: + from .enum_utils import is_property_enum, get_enum_type def nested_type(nt) -> RustType: if 'items' in nt: nt = nt['items'] @@ -448,6 +456,9 @@ def to_rust_type_inner( rt = Option(Box(rt)) return wrap_type(rt) try: + if is_property_enum(t): + x = get_enum_type(schema_name, property_name, t, schemas) + return wrap_type(x) # prefer format if present rust_type = RUST_TYPE_MAP[t.get("format", t["type"])] if rust_type == Vec(None): @@ -478,10 +489,10 @@ def is_nested_type(s): # convert a rust-type to something that would be taken as input of a function # even though our storage type is different -def activity_input_type(schemas, p): +def activity_input_type(schemas, p, parent=None): if 'input_type' in p: return p.input_type - n = activity_rust_type(schemas, p, allow_optionals=False) + n = activity_rust_type(schemas, p, allow_optionals=False, parent=parent) if n == 'String': n = 'str' # pods are copied anyway @@ -580,8 +591,8 @@ def activity_split(fqan: str) -> Tuple[str, str, str]: # Shorthand to get a type from parameters of activities -def activity_rust_type(schemas, p, allow_optionals=True): - return to_rust_type(schemas, None, p.name, p, allow_optionals=allow_optionals) +def activity_rust_type(schemas, p, allow_optionals=True, parent=None): + return to_rust_type(schemas, parent, p.name, p, allow_optionals=allow_optionals) # the inverse of activity-split, but needs to know the 'name' of the API diff --git a/src/generator/templates/api/api/enums.rs.mako b/src/generator/templates/api/api/enums.rs.mako new file mode 100644 index 0000000000..b59e565459 --- /dev/null +++ b/src/generator/templates/api/api/enums.rs.mako @@ -0,0 +1,25 @@ +<%namespace name="lib" file="../lib/lib.mako"/>\ +<%namespace name="enum" file="../lib/enum.mako"/>\ +<%namespace name="util" file="../../../lib/util.mako"/>\ +<%namespace name="rbuild" file="../lib/rbuild.mako"/>\ +<%namespace name="mbuild" file="../lib/mbuild.mako"/>\ +<%namespace name="schema" file="../lib/schema.mako"/>\ +<% + from generator.lib.util import (new_context, hub_type, hub_type_params_s) + from generator.lib.enum_utils import (find_enums_in_context) + + c = new_context(schemas, resources) + hub_type = hub_type(c.schemas, util.canonical_name()) + ht_params = hub_type_params_s() + + enums = find_enums_in_context(c) + + default_user_agent = "google-api-rust-client/" + cargo.build_version +%>\ +use super::*; + + + +% for schema_name,property_name,enum_type, e in enums: +${enum.new(enum_type, e, c)} +% endfor diff --git a/src/generator/templates/api/api/mod.rs.mako b/src/generator/templates/api/api/mod.rs.mako index 6ce0d89ee1..867dfdbb09 100644 --- a/src/generator/templates/api/api/mod.rs.mako +++ b/src/generator/templates/api/api/mod.rs.mako @@ -46,3 +46,6 @@ pub use method_builders::*; mod call_builders; pub use call_builders::*; + +mod enums; +pub use enums::*; diff --git a/src/generator/templates/api/lib/enum.mako b/src/generator/templates/api/lib/enum.mako index 7d9f8cd843..1aa27d97d6 100644 --- a/src/generator/templates/api/lib/enum.mako +++ b/src/generator/templates/api/lib/enum.mako @@ -9,6 +9,8 @@ upload_action_fn, METHODS_BUILDER_MARKER_TRAIT, DELEGATE_TYPE, to_extern_crate_name, rust_doc_sanitize) + from generator.lib.enum_utils import (to_enum_variant_name) + def pretty_name(name): return ' '.join(split_camelcase_s(name).split('.')) %>\ @@ -64,4 +66,50 @@ impl Default for Scope { ${scope_url_to_variant(name, default_url)} } } - \ No newline at end of file + + + +<%def name="new(enum_type, e, c)">\ +#[derive(Clone, Copy, Eq, Hash, Debug, PartialEq, Serialize, Deserialize)] +% if e.get('description'): +/// ${e.description} +% endif +pub enum ${enum_type} { +% for (variant_name,description) in zip(e.get('enum'), e.get('enumDescriptions')): + <% #print(variant_name, '=>', description) + %> + % if description: + + /// ${description} + /// + % endif\ + /// value: + /// "${variant_name}" + ${to_enum_variant_name(variant_name)}, +% endfor +} + +impl AsRef for ${enum_type} { + fn as_ref(&self) -> &str { + match *self { + % for variant in e.get('enum'): + ${enum_type}::${to_enum_variant_name(variant)} => "${variant_name}", + % endfor + } + } +} + +impl<'a> Into> for &'a ${enum_type} { + fn into(self) -> std::borrow::Cow<'a, str> { + self.as_ref().into() + } +} + +% if e.get('default') is not None: +impl Default for ${enum_type} { + fn default() -> ${enum_type} { + ${enum_type}::${to_enum_variant_name(e.get('default'))} + } +} +% endif + diff --git a/src/generator/templates/api/lib/mbuild.mako b/src/generator/templates/api/lib/mbuild.mako index 7b943b0c90..c901c25d60 100644 --- a/src/generator/templates/api/lib/mbuild.mako +++ b/src/generator/templates/api/lib/mbuild.mako @@ -125,9 +125,9 @@ pub struct ${ThisType} % for p in params: pub(super) ${property(p.name)}:\ % if is_required_property(p): - ${activity_rust_type(schemas, p, allow_optionals=False)}, + ${activity_rust_type(schemas, p, allow_optionals=False, parent=resource)}, % else: - ${activity_rust_type(schemas, p)}, + ${activity_rust_type(schemas, p, parent=resource)}, % endif % endfor ## A generic map for additinal parameters. Sometimes you can set some that are documented online only @@ -223,11 +223,11 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\ ############################################################################################### <%def name="_setter_fn(resource, method, m, p, part_prop, ThisType, c)">\ <% - InType = activity_input_type(schemas, p) + InType = activity_input_type(schemas, p, parent=resource) if is_repeated_property(p): p.repeated = False - InType = activity_input_type(schemas, p) + InType = activity_input_type(schemas, p, parent=resource) p.repeated = True def show_part_info(m, p): @@ -312,7 +312,7 @@ ${self._setter_fn(resource, method, m, p, part_prop, ThisType, c)}\ # could also just skip the first element, but ... let's be safe if request_value and request_value.id == p.get(TREF): continue - v = rnd_arg_val_for_type(activity_input_type(schemas, p)) + v = rnd_arg_val_for_type(activity_input_type(schemas, p, parent=resource)) # we chose to replace random strings with their meaning, as indicated by the name ! if is_string_value(v): v = '"%s"' % p.name @@ -590,7 +590,7 @@ match result { params.push("${p.name}", ${to_string_impl("value")}); } % else: - params.push("${p.name}", ${to_string_impl(pname)}); + params.push("${p.name}", &${to_string_impl(pname)}); % endif % endfor diff --git a/src/generator/templates/api/lib/rbuild.mako b/src/generator/templates/api/lib/rbuild.mako index 6024858b03..b0e1125b1d 100644 --- a/src/generator/templates/api/lib/rbuild.mako +++ b/src/generator/templates/api/lib/rbuild.mako @@ -19,6 +19,7 @@ hub_type_name = hub_type(schemas, util.canonical_name()) rb_params = rb_type_params_s(resource, c) ThisType = rb_type(resource) + rb_params + parent = resource %>\ % if resource == METHODS_RESOURCE: /// A builder providing access to all free methods, which are not associated with a particular resource. @@ -65,7 +66,7 @@ impl${rb_params} ${ThisType} { method_args = '' if required_props: - method_args = ', ' + ', '.join('%s: %s' % (mangle_ident(p.name), activity_input_type(schemas, p)) for p in required_props) + method_args = ', ' + ', '.join('%s: %s' % (mangle_ident(p.name), activity_input_type(schemas, p, parent=parent)) for p in required_props) mb_tparams = mb_type_params_s(m) # we would could have information about data requirements for each property in it's dict.