Adapt generated tui code
- generate subtypes (necessary for non-GET request) - generate TryFrom for custom structs into Request and RequestBuilder - adapt metadata to generate few missed operations Change-Id: I0efd28b86bbd03f7aed2ece4552b30e91deac110
This commit is contained in:
parent
5d38233548
commit
9c8491f2b5
@ -217,7 +217,7 @@ def main():
|
||||
metadata_path = Path(args.metadata)
|
||||
generator.load_metadata(metadata_path)
|
||||
# Resulting mod_paths
|
||||
res_mods = []
|
||||
res_mods: list[tuple[list[str | None], str, str, str | None]] = []
|
||||
|
||||
for res, res_data in generator.metadata.resources.items():
|
||||
if args.service and not res.startswith(args.service):
|
||||
@ -243,7 +243,7 @@ def main():
|
||||
).resolve()
|
||||
)
|
||||
|
||||
for mod_path, mod_name, path in generators[
|
||||
for mod_path, mod_name, path, class_name in generators[
|
||||
args.target
|
||||
].generate(
|
||||
res,
|
||||
@ -252,7 +252,7 @@ def main():
|
||||
operation_id=op_data.operation_id,
|
||||
args=op_args,
|
||||
):
|
||||
res_mods.append((mod_path, mod_name, path))
|
||||
res_mods.append((mod_path, mod_name, path, class_name))
|
||||
rust_sdk_extensions = res_data.extensions.get("rust-sdk")
|
||||
if rust_sdk_extensions:
|
||||
additional_modules = rust_sdk_extensions.setdefault(
|
||||
@ -269,17 +269,16 @@ def main():
|
||||
],
|
||||
mod,
|
||||
"",
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
if args.target in ["rust-sdk", "rust-tui"] and not args.resource:
|
||||
resource_results: dict[str, dict] = {}
|
||||
for mod_path, mod_name, path in res_mods:
|
||||
for mod_path, mod_name, path, class_name in res_mods:
|
||||
mn = "/".join(mod_path)
|
||||
x = resource_results.setdefault(
|
||||
mn, {"path": path, "mods": set()}
|
||||
)
|
||||
x["mods"].add(mod_name)
|
||||
x = resource_results.setdefault(mn, {"path": path, "mods": {}})
|
||||
x["mods"][mod_name] = class_name
|
||||
changed = True
|
||||
while changed:
|
||||
changed = False
|
||||
@ -292,15 +291,15 @@ def main():
|
||||
mn = "/".join(mod_path[0:-1])
|
||||
mod_name = mod_path[-1]
|
||||
if mn in resource_results:
|
||||
if mod_name not in resource_results[mn]["mods"]:
|
||||
resource_results[mn]["mods"].add(mod_name)
|
||||
if mod_name not in resource_results[mn]["mods"].keys():
|
||||
resource_results[mn]["mods"][mod_name] = None
|
||||
changed = True
|
||||
else:
|
||||
changed = True
|
||||
x = resource_results.setdefault(
|
||||
mn, {"path": path, "mods": set()}
|
||||
mn, {"path": path, "mods": {}}
|
||||
)
|
||||
x["mods"].add(mod_name)
|
||||
x["mods"][mod_name] = None
|
||||
|
||||
for path, gen_data in resource_results.items():
|
||||
generators[args.target].generate_mod(
|
||||
@ -308,7 +307,13 @@ def main():
|
||||
path.split("/"),
|
||||
gen_data["mods"],
|
||||
gen_data["path"],
|
||||
res.split(".")[-1].capitalize(),
|
||||
"".join(
|
||||
"".join(
|
||||
y.title()
|
||||
for z in path.split("/")[2:]
|
||||
for y in z.split("_")
|
||||
)
|
||||
),
|
||||
service_name=path.split("/")[0],
|
||||
)
|
||||
exit(0)
|
||||
|
@ -37,6 +37,14 @@ class Boolean(BasePrimitiveType):
|
||||
def get_sample(self):
|
||||
return "false"
|
||||
|
||||
def get_sdk_setter(
|
||||
self, source_var_name: str, sdk_mod_path: str, into: bool = False
|
||||
) -> str:
|
||||
if into:
|
||||
return f"*{source_var_name}"
|
||||
else:
|
||||
return f"*{source_var_name}"
|
||||
|
||||
|
||||
class Number(BasePrimitiveType):
|
||||
format: str | None = None
|
||||
@ -146,6 +154,14 @@ class Option(BaseCombinedType):
|
||||
def get_sample(self):
|
||||
return self.item_type.get_sample()
|
||||
|
||||
def get_sdk_setter(
|
||||
self, source_var_name: str, sdk_mod_path: str, into: bool = False
|
||||
) -> str:
|
||||
if into:
|
||||
return f"{self.item_type.get_sdk_setter(source_var_name, sdk_mod_path, into=into)}"
|
||||
else:
|
||||
return f"{source_var_name}.clone().map(Into::into)"
|
||||
|
||||
|
||||
class Array(BaseCombinedType):
|
||||
base_type: str = "vec"
|
||||
|
@ -134,7 +134,7 @@ class BlockStorageMetadata(MetadataBase):
|
||||
].cli_full_command.replace("delete-all", "purge")
|
||||
|
||||
if resource_name in ["backup", "snapshot", "volume"]:
|
||||
if operation_name in ["list", "delete"]:
|
||||
if operation_name in ["list_detailed", "delete"]:
|
||||
operation.targets.setdefault(
|
||||
"rust-tui",
|
||||
OperationTargetParams(
|
||||
|
@ -193,27 +193,35 @@ class ComputeMetadata(MetadataBase):
|
||||
"rust-cli"
|
||||
].cli_full_command.replace("delete-all", "purge")
|
||||
|
||||
if resource_name in [
|
||||
"aggregate",
|
||||
"flavor",
|
||||
"hypervisor",
|
||||
"server",
|
||||
"server/instance_action",
|
||||
]:
|
||||
if operation_name in ["list", "delete", "show"]:
|
||||
operation.targets.setdefault(
|
||||
"rust-tui",
|
||||
OperationTargetParams(
|
||||
module_name=operation.targets["rust-sdk"].module_name
|
||||
),
|
||||
)
|
||||
|
||||
if resource_name == "quota_set" and operation_name == "details":
|
||||
operation.targets.setdefault(
|
||||
if (
|
||||
(
|
||||
resource_name in ["aggregate", "server/instance_action"]
|
||||
and operation_name in ["list", "delete", "show"]
|
||||
)
|
||||
or (
|
||||
resource_name == "server"
|
||||
and operation_name
|
||||
in ["list_detailed", "delete", "show", "os-get-console-output"]
|
||||
)
|
||||
or (
|
||||
resource_name in ["flavor", "hypervisor"]
|
||||
and operation_name in ["list_detailed", "show"]
|
||||
)
|
||||
or (resource_name == "quota_set" and operation_name == "details")
|
||||
):
|
||||
op = operation.targets.setdefault(
|
||||
"rust-tui",
|
||||
OperationTargetParams(
|
||||
module_name=operation.targets["rust-sdk"].module_name
|
||||
),
|
||||
)
|
||||
if operation_name == "os-get-console-output":
|
||||
op.module_name = operation.targets[
|
||||
"rust-sdk"
|
||||
].module_name.replace("os_", "")
|
||||
op.sdk_mod_name = operation.targets["rust-sdk"].module_name
|
||||
op.operation_name = operation.targets[
|
||||
"rust-sdk"
|
||||
].operation_name
|
||||
|
||||
return operation
|
||||
|
@ -149,7 +149,9 @@ class IdentityMetadata(MetadataBase):
|
||||
"user",
|
||||
"user/application_credential",
|
||||
]:
|
||||
if operation_name in ["list", "delete"]:
|
||||
if operation_name in ["list", "delete"] or (
|
||||
resource_name == "user" and operation_name == "update"
|
||||
):
|
||||
operation.targets.setdefault(
|
||||
"rust-tui",
|
||||
OperationTargetParams(
|
||||
|
@ -1538,4 +1538,4 @@ class RustCliGenerator(BaseGenerator):
|
||||
|
||||
self._format_code(impl_path)
|
||||
|
||||
yield (cli_mod_path, mod_name, path)
|
||||
yield (cli_mod_path, mod_name, path, None)
|
||||
|
@ -123,6 +123,11 @@ class Struct(common_rust.Struct):
|
||||
"""Return Rust `<'lc>` lifetimes representation"""
|
||||
return f"<{', '.join(self.lifetimes)}>" if self.lifetimes else ""
|
||||
|
||||
@property
|
||||
def static_lifetime_anonymous(self):
|
||||
"""Return Rust `<'lc>` lifetimes representation"""
|
||||
return self.static_lifetime.replace("'a", "'_")
|
||||
|
||||
def get_sample(self):
|
||||
res = [self.name + "Builder::default()"]
|
||||
for field in sorted(self.fields.values(), key=lambda d: d.local_name):
|
||||
@ -505,7 +510,7 @@ class RustSdkGenerator(BaseGenerator):
|
||||
|
||||
self._format_code(impl_path)
|
||||
|
||||
yield (mod_path, mod_name, path)
|
||||
yield (mod_path, mod_name, path, class_name)
|
||||
|
||||
def generate_mod(
|
||||
self, target_dir, mod_path, mod_list, url, resource_name, service_name
|
||||
@ -591,4 +596,4 @@ class RustSdkGenerator(BaseGenerator):
|
||||
|
||||
self._format_code(impl_path)
|
||||
|
||||
return (mod_path, "find", "dummy")
|
||||
return (mod_path, "find", "dummy", "Request")
|
||||
|
@ -22,14 +22,154 @@ from codegenerator import model
|
||||
from codegenerator.common import BaseCompoundType
|
||||
from codegenerator.common import rust as common_rust
|
||||
from codegenerator.rust_sdk import TypeManager as SdkTypeManager
|
||||
from codegenerator import rust_sdk
|
||||
|
||||
|
||||
class String(common_rust.String):
|
||||
type_hint: str = "String"
|
||||
|
||||
def get_sdk_setter(
|
||||
self, source_var_name: str, sdk_mod_path: str, into: bool = False
|
||||
) -> str:
|
||||
if into:
|
||||
return f"{source_var_name}.into()"
|
||||
else:
|
||||
return f"{source_var_name}.clone()"
|
||||
|
||||
|
||||
class ArrayInput(common_rust.Array):
|
||||
pass
|
||||
original_data_type: (
|
||||
common_rust.BaseCompoundType
|
||||
| common_rust.BaseCombinedType
|
||||
| common_rust.BasePrimitiveType
|
||||
| None
|
||||
) = None
|
||||
|
||||
def get_sdk_setter(
|
||||
self,
|
||||
source_var_name: str,
|
||||
sdk_mod_path: str,
|
||||
into: bool = False,
|
||||
ord_num: int = 0,
|
||||
) -> str:
|
||||
ord_num += 1
|
||||
result: str = source_var_name
|
||||
if isinstance(self.item_type, common_rust.BaseCompoundType):
|
||||
result += f".iter().flat_map(|x| TryFrom::try_from(x)).collect::<Vec<{'::'.join(sdk_mod_path)}::{self.item_type.name}>>()"
|
||||
elif isinstance(self.item_type, common_rust.BaseCombinedType):
|
||||
if into:
|
||||
result += ".iter()"
|
||||
else:
|
||||
result += ".iter().cloned()"
|
||||
result += (
|
||||
f".map(|x{ord_num}| "
|
||||
+ self.item_type.get_sdk_setter(
|
||||
f"x{ord_num}", sdk_mod_path, into=True
|
||||
)
|
||||
+ ").collect::<Vec<_>>()"
|
||||
)
|
||||
else:
|
||||
if into:
|
||||
result += ".into_iter()"
|
||||
else:
|
||||
result += ".iter().cloned()"
|
||||
result += f".map(Into::into).collect::<Vec<_>>()"
|
||||
return result
|
||||
|
||||
|
||||
class StructField(rust_sdk.StructField):
|
||||
def get_sdk_setter(
|
||||
self,
|
||||
sdk_field: rust_sdk.StructField,
|
||||
source_var: str,
|
||||
dest_var: str,
|
||||
sdk_mod_path: str,
|
||||
into: bool = False,
|
||||
) -> str:
|
||||
result: str = ""
|
||||
source = "val" if self.is_optional else f"value.{self.local_name}"
|
||||
if self.is_optional:
|
||||
result += f"if let Some(val) = &{source_var}.{self.local_name} {{"
|
||||
if isinstance(sdk_field.data_type, rust_sdk.Struct):
|
||||
if not self.is_optional and not into:
|
||||
source = f"&{source}"
|
||||
result += (
|
||||
f"{dest_var}.{sdk_field.local_name}(TryInto::<{'::'.join(sdk_mod_path)}::{sdk_field.data_type.name}>::try_into("
|
||||
+ source
|
||||
+ ")?);"
|
||||
)
|
||||
else:
|
||||
result += (
|
||||
f"{dest_var}.{sdk_field.local_name}("
|
||||
+ self.data_type.get_sdk_setter(
|
||||
source, sdk_mod_path, into=into
|
||||
)
|
||||
+ ");"
|
||||
)
|
||||
if self.is_optional:
|
||||
result += "}\n"
|
||||
return result
|
||||
|
||||
|
||||
class Struct(rust_sdk.Struct):
|
||||
field_type_class_: Type[StructField] | StructField = StructField
|
||||
original_data_type: BaseCompoundType | BaseCompoundType | None = None
|
||||
is_required: bool = False
|
||||
|
||||
@property
|
||||
def static_lifetime(self):
|
||||
"""Return Rust `<'lc>` lifetimes representation"""
|
||||
return f"<{', '.join(self.lifetimes)}>" if self.lifetimes else ""
|
||||
|
||||
def get_sdk_builder_try_from(
|
||||
self, sdk_struct: rust_sdk.Struct, sdk_mod_path: list[str]
|
||||
) -> str:
|
||||
result: str = f"impl TryFrom<&{self.name}> for {'::'.join(sdk_mod_path)}::{sdk_struct.name}Builder{sdk_struct.static_lifetime_anonymous} {{"
|
||||
result += "type Error = Report;\n"
|
||||
result += (
|
||||
f"fn try_from(value: &{self.name}) -> Result<Self, Self::Error> {{"
|
||||
)
|
||||
result += "let mut ep_builder = Self::default();\n"
|
||||
|
||||
result += self.get_set_sdk_struct_fields(
|
||||
sdk_struct, "value", "ep_builder", sdk_mod_path
|
||||
)
|
||||
|
||||
result += "Ok(ep_builder)"
|
||||
result += "}\n"
|
||||
result += "}"
|
||||
return result
|
||||
|
||||
def get_set_sdk_struct_fields(
|
||||
self,
|
||||
sdk_struct: rust_sdk.Struct,
|
||||
source_var: str,
|
||||
dest_var: str,
|
||||
sdk_mod_path: list[str],
|
||||
) -> str:
|
||||
result: str = ""
|
||||
|
||||
for (field, field_data), (_, sdk_field_data) in zip(
|
||||
self.fields.items(), sdk_struct.fields.items()
|
||||
):
|
||||
result += field_data.get_sdk_setter(
|
||||
sdk_field_data, source_var, dest_var, sdk_mod_path, into=False
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def get_sdk_type_try_from(
|
||||
self, sdk_struct: rust_sdk.Struct, sdk_mod_path: list[str]
|
||||
) -> str:
|
||||
result: str = f"impl TryFrom<&{self.name}> for {'::'.join(sdk_mod_path)}::{sdk_struct.name}{sdk_struct.static_lifetime_anonymous} {{"
|
||||
result += "type Error = Report;\n"
|
||||
result += f"fn try_from(value: &{self.name}) -> Result<Self, Self::Error> {{\n"
|
||||
result += f"let ep_builder: {'::'.join(sdk_mod_path)}::{sdk_struct.name}Builder = TryFrom::try_from(value)?;\n"
|
||||
|
||||
result += f'ep_builder.build().wrap_err("cannot prepare request element `{self.name}`")'
|
||||
result += "}\n"
|
||||
result += "}"
|
||||
return result
|
||||
|
||||
|
||||
class TypeManager(common_rust.TypeManager):
|
||||
@ -46,7 +186,7 @@ class TypeManager(common_rust.TypeManager):
|
||||
}
|
||||
|
||||
data_type_mapping = {
|
||||
model.Struct: common_rust.Struct,
|
||||
model.Struct: Struct,
|
||||
model.Array: ArrayInput,
|
||||
model.CommaSeparatedList: ArrayInput,
|
||||
}
|
||||
@ -55,6 +195,8 @@ class TypeManager(common_rust.TypeManager):
|
||||
common_rust.RequestParameter
|
||||
)
|
||||
|
||||
sdk_type_manager: SdkTypeManager | None = None
|
||||
|
||||
def get_local_attribute_name(self, name: str) -> str:
|
||||
"""Get localized attribute name"""
|
||||
name = name.replace(".", "_")
|
||||
@ -69,6 +211,35 @@ class TypeManager(common_rust.TypeManager):
|
||||
"""Get the attribute name on the SDK side"""
|
||||
return self.get_local_attribute_name(name)
|
||||
|
||||
def link_sdk_type_manager(self, sdk_type_manager: SdkTypeManager) -> None:
|
||||
self.sdk_type_manager = sdk_type_manager
|
||||
|
||||
def get_subtypes_with_sdk(self):
|
||||
"""Get all subtypes excluding TLA"""
|
||||
for k, v in self.refs.items():
|
||||
if self.sdk_type_manager:
|
||||
if k.name == "Body":
|
||||
sdk_type = self.sdk_type_manager.get_root_data_type()
|
||||
else:
|
||||
sdk_type = self.sdk_type_manager.refs[k]
|
||||
else:
|
||||
sdk_type = None
|
||||
if (
|
||||
k
|
||||
and isinstance(
|
||||
v, (common_rust.Enum, Struct, common_rust.StringEnum)
|
||||
)
|
||||
and k.name != "Body"
|
||||
):
|
||||
yield (v, sdk_type)
|
||||
elif (
|
||||
k
|
||||
and k.name != "Body"
|
||||
and isinstance(v, self.option_type_class)
|
||||
):
|
||||
if isinstance(v.item_type, common_rust.Enum):
|
||||
yield (v.item_type, sdk_type)
|
||||
|
||||
|
||||
class RustTuiGenerator(BaseGenerator):
|
||||
def __init__(self):
|
||||
@ -170,7 +341,16 @@ class RustTuiGenerator(BaseGenerator):
|
||||
service_name = common.get_rust_service_type_from_str(
|
||||
args.service_type
|
||||
)
|
||||
class_name = f"{service_name}{res_name.title()}{args.operation_type.title()}".replace(
|
||||
operation_name: str = (
|
||||
args.operation_type
|
||||
if args.operation_type != "action"
|
||||
else args.module_name
|
||||
)
|
||||
operation_name = "".join(
|
||||
x.title()
|
||||
for x in re.split(r"[-_]", operation_name.replace("os-", ""))
|
||||
)
|
||||
class_name = f"{service_name}{''.join(x.title() for x in path_resources)}{operation_name}".replace(
|
||||
"_", ""
|
||||
)
|
||||
operation_body = operation_variant.get("body")
|
||||
@ -212,6 +392,7 @@ class RustTuiGenerator(BaseGenerator):
|
||||
sdk_type_manager.set_models(all_types)
|
||||
# else:
|
||||
# logging.warn("Ignoring response type of action")
|
||||
type_manager.link_sdk_type_manager(sdk_type_manager)
|
||||
|
||||
if method == "patch":
|
||||
# There might be multiple supported mime types. We only select ones we are aware of
|
||||
@ -258,7 +439,10 @@ class RustTuiGenerator(BaseGenerator):
|
||||
pass
|
||||
# response_def = (None,)
|
||||
response_key = None
|
||||
sdk_mod_path_base = common.get_rust_sdk_mod_path(
|
||||
sdk_mod_path_base = [
|
||||
"openstack_sdk",
|
||||
"api",
|
||||
] + common.get_rust_sdk_mod_path(
|
||||
args.service_type, args.api_version, args.module_path or path
|
||||
)
|
||||
sdk_mod_path: list[str] = sdk_mod_path_base.copy()
|
||||
@ -267,9 +451,7 @@ class RustTuiGenerator(BaseGenerator):
|
||||
|
||||
additional_imports = set()
|
||||
additional_imports.add(
|
||||
"openstack_sdk::api::"
|
||||
+ "::".join(sdk_mod_path)
|
||||
+ "::RequestBuilder"
|
||||
"::".join(sdk_mod_path) + "::RequestBuilder"
|
||||
)
|
||||
additional_imports.add(
|
||||
"openstack_sdk::{AsyncOpenStack, api::QueryAsync}"
|
||||
@ -323,7 +505,7 @@ class RustTuiGenerator(BaseGenerator):
|
||||
|
||||
self._format_code(impl_path)
|
||||
|
||||
yield (mod_path, mod_name, path)
|
||||
yield (mod_path, mod_name, path, class_name)
|
||||
|
||||
def generate_mod(
|
||||
self, target_dir, mod_path, mod_list, url, resource_name, service_name
|
||||
@ -336,9 +518,18 @@ class RustTuiGenerator(BaseGenerator):
|
||||
"/".join(mod_path[0:-1]),
|
||||
f"{mod_path[-1]}.rs",
|
||||
)
|
||||
service_name = "".join(x.title() for x in service_name.split("_"))
|
||||
|
||||
new_mod_list: dict[str, dict[str, str]] = {}
|
||||
for mod_name, class_name in mod_list.items():
|
||||
name = "".join(x.title() for x in mod_name.split("_"))
|
||||
full_name = "".join(x.title() for x in mod_path[2:]) + name
|
||||
if not class_name:
|
||||
class_name = f"{service_name}{full_name}ApiRequest"
|
||||
new_mod_list[mod_name] = {"name": name, "class_name": class_name}
|
||||
|
||||
context = {
|
||||
"mod_list": mod_list,
|
||||
"mod_list": new_mod_list,
|
||||
"mod_path": mod_path,
|
||||
"url": url,
|
||||
"resource_name": resource_name,
|
||||
@ -346,6 +537,6 @@ class RustTuiGenerator(BaseGenerator):
|
||||
}
|
||||
|
||||
# Generate methods for the GET resource command
|
||||
self._render_command(context, "rust_sdk/mod.rs.j2", impl_path)
|
||||
self._render_command(context, "rust_tui/mod.rs.j2", impl_path)
|
||||
|
||||
self._format_code(impl_path)
|
||||
|
@ -241,6 +241,11 @@ Some({{ val }})
|
||||
{%- endfor %}
|
||||
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }}.build().expect("A valid object"));
|
||||
|
||||
{%- elif param.data_type.__class__.__name__ == "Struct" %}
|
||||
{% set builder_name = param.local_name + "_builder" %}
|
||||
let {{ builder_name }}: openstack_sdk::api::{{ sdk_mod_path | join("::") }}::{{ param.data_type.name }}Builder = TryFrom::try_from(&{{ val_var}})?;
|
||||
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }}.build()?);
|
||||
|
||||
{%- elif param.data_type.__class__.__name__ == "String" %}
|
||||
{%- if is_nullable and not param.is_optional %}
|
||||
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "") }}.clone());
|
||||
@ -377,7 +382,7 @@ Some({{ val }})
|
||||
{%- elif param["setter_type"] is defined %}
|
||||
{#- Param with setter present #}
|
||||
{{ dst_var }}.{{ param.remote_name }}(
|
||||
{{ val_var }}{{ ".iter()" if not by_ref else ".into_iter().cloned()" }}
|
||||
{{ val_var }}{{ ".iter()" if not by_ref else ".iter().cloned()" }}
|
||||
);
|
||||
{%- elif original_item_type and original_item_type.__class__.__name__ == "DictionaryInput" %}
|
||||
use std::collections::BTreeMap;
|
||||
|
@ -15,7 +15,8 @@
|
||||
// WARNING: This file is automatically generated from OpenAPI schema using
|
||||
// `openstack-codegenerator`.
|
||||
{% import 'rust_macros.j2' as macros with context -%}
|
||||
use eyre::{Result, WrapErr};
|
||||
use derive_builder::Builder;
|
||||
use eyre::{Result, WrapErr, Report};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt;
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
@ -32,33 +33,68 @@ use {{ mod }};
|
||||
{%- set sdk_data_type = sdk_type_manager.get_root_data_type() %}
|
||||
|
||||
{%- if data_type.__class__.__name__ == "Struct" %}
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Builder, Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[builder(setter(strip_option))]
|
||||
pub struct {{ class_name }}{
|
||||
{%- for name, param in type_manager.parameters|dictsort if param.location != "header" %}
|
||||
{{ param.local_name }}: {{ param.type_hint }},
|
||||
{%- if param.local_name.endswith("id") and (param.local_name[:-2] + "name") not in type_manager.parameters.keys() %}
|
||||
{%- if param.type_hint.startswith("Option") %}
|
||||
#[builder(default)]
|
||||
{%- endif %}
|
||||
pub {{ param.local_name }}: {{ param.type_hint }},
|
||||
{%- if param.local_name.endswith("id") and param.local_name not in ["uuid"] and (param.local_name[:-2] + "name") not in type_manager.parameters.keys() %}
|
||||
{%- set param_type = False %}
|
||||
{{ param.local_name[:-2] }}name:
|
||||
#[builder(default)]
|
||||
pub {{ param.local_name[:-2] }}name:
|
||||
{%- if param.type_hint.startswith("Option") %}
|
||||
{{ param.type_hint }},
|
||||
{%- else %}
|
||||
Option<{{ param.type_hint }}>,
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
|
||||
{%- if data_type["fields"] is defined %}
|
||||
{#- Structure #}
|
||||
{%- for name, field in data_type.fields | dictsort %}
|
||||
{%- if field.data_type.__class__.__name__ in ["Struct"] %}
|
||||
{{ macros.docstring(field.description, indent=4) }}
|
||||
{{ field.local_name }}: {{ field.type_hint }},
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
{%- for type, sdk_type in type_manager.get_subtypes_with_sdk() %}
|
||||
{%- if type["base_type"] == "struct" %}
|
||||
/// {{ type.name }} data
|
||||
#[derive(Builder, Debug, Default, Deserialize, Clone, Eq, PartialEq, Serialize)]
|
||||
#[builder(setter(strip_option))]
|
||||
pub struct {{ type.name }} {
|
||||
{%- for _, field in type.fields | dictsort %}
|
||||
{{ macros.docstring(field.description, indent=4) }}
|
||||
{{ field.builder_macros }}
|
||||
pub {{ field.local_name }}: {{ field.type_hint }},
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
{{ type.get_sdk_builder_try_from(sdk_type, sdk_mod_path) }}
|
||||
{{ type.get_sdk_type_try_from(sdk_type, sdk_mod_path) }}
|
||||
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
impl fmt::Display for {{ class_name }} {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
{%- for name, param in type_manager.parameters | dictsort %}
|
||||
{%- if param.location != "header" %}
|
||||
{%- if param.local_name.endswith("id") %}
|
||||
{%- if param.local_name.endswith("id") and param.local_name not in ["uuid"] %}
|
||||
{%- set alt_name = param.local_name[:-2] + "name" %}
|
||||
{%- if param.type_hint.startswith("Option") %}
|
||||
if self.{{ param.local_name }}.is_some() || self.{{ alt_name }}.is_some() {
|
||||
parts.push(format!(
|
||||
"{{ param.local_name[:-3] or "name/id" }}: {}",
|
||||
"{{ param.local_name[:-3] if param.local_name|length > 3 else "name/id" }}: {}",
|
||||
self.{{ alt_name }}
|
||||
.as_ref()
|
||||
.or(self.{{ param.local_name }}.as_ref())
|
||||
@ -69,7 +105,7 @@ impl fmt::Display for {{ class_name }} {
|
||||
}
|
||||
{% else %}
|
||||
parts.push(format!(
|
||||
"{{ param.local_name[:-3] }}: {}",
|
||||
"{{ param.local_name[:-3] if param.local_name|length > 3 else "name/id" }}: {}",
|
||||
self.{{ alt_name }}
|
||||
.clone()
|
||||
.unwrap_or(self.{{ param.local_name }}.clone())
|
||||
@ -82,8 +118,9 @@ impl fmt::Display for {{ class_name }} {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&{{ class_name }}> for RequestBuilder{{ "<'_>" if sdk_type_manager.get_request_static_lifetimes(data_type) else "" }} {
|
||||
fn from(value: &{{ class_name }}) -> Self {
|
||||
impl TryFrom<&{{ class_name }}> for RequestBuilder{{ "<'_>" if sdk_type_manager.get_request_static_lifetimes(data_type) else "" }} {
|
||||
type Error = Report;
|
||||
fn try_from(value: &{{ class_name }}) -> Result<Self, Self::Error> {
|
||||
let mut ep_builder = Self::default();
|
||||
|
||||
{%- for (k, v) in type_manager.get_parameters("path") %}
|
||||
@ -112,7 +149,12 @@ impl From<&{{ class_name }}> for RequestBuilder{{ "<'_>" if sdk_type_manager.get
|
||||
{{ macros.set_request_data_from_input(type_manager, "ep_builder", v, "value" + v.local_name, by_ref=True )}}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
ep_builder
|
||||
|
||||
{%- if data_type.__class__.__name__ == "Struct" %}
|
||||
{{ data_type.get_set_sdk_struct_fields(sdk_data_type, "value", "ep_builder", sdk_mod_path) }}
|
||||
{%- endif %}
|
||||
|
||||
Ok(ep_builder)
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,10 +162,10 @@ impl ExecuteApiRequest for {{ class_name }} {
|
||||
async fn execute_request(
|
||||
&self,
|
||||
session: &mut AsyncOpenStack,
|
||||
request: &ApiRequest,
|
||||
app_tx: &UnboundedSender<Action>,
|
||||
{{ "_" if operation_type == "delete" else ""}}request: &ApiRequest,
|
||||
{{ "_" if operation_type == "delete" else ""}}app_tx: &UnboundedSender<Action>,
|
||||
) -> Result<(), CloudWorkerError> {
|
||||
let ep = Into::<RequestBuilder>::into(self)
|
||||
let ep = TryInto::<RequestBuilder>::try_into(self)?
|
||||
.build()
|
||||
.wrap_err("Cannot prepare request")?;
|
||||
{%- if operation_type == "list" %}
|
||||
@ -140,6 +182,11 @@ impl ExecuteApiRequest for {{ class_name }} {
|
||||
request: request.clone(),
|
||||
data: ep.query_async(session).await?,
|
||||
})?;
|
||||
{%- elif operation_type in ["action", "set"] %}
|
||||
app_tx.send(Action::ApiResponseData {
|
||||
request: request.clone(),
|
||||
data: ep.query_async(session).await?,
|
||||
})?;
|
||||
{%- elif operation_type == "delete" %}
|
||||
ignore(ep).query_async(session).await?;
|
||||
{%- endif %}
|
||||
|
99
codegenerator/templates/rust_tui/mod.rs.j2
Normal file
99
codegenerator/templates/rust_tui/mod.rs.j2
Normal file
@ -0,0 +1,99 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// WARNING: This file is automatically generated from OpenAPI schema using
|
||||
// `openstack-codegenerator`.
|
||||
{% if mod_path|length > 2 %}
|
||||
//! `{{ url }}` REST operations bindings of {{ service_name }}
|
||||
{%- else %}
|
||||
//! `{{ service_name|capitalize }}` Service bindings
|
||||
{%- endif %}
|
||||
|
||||
use eyre::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc::UnboundedSender;
|
||||
|
||||
use openstack_sdk::AsyncOpenStack;
|
||||
|
||||
use crate::action::Action;
|
||||
use crate::cloud_worker::common::CloudWorkerError;
|
||||
use crate::cloud_worker::types::{ApiRequest, ExecuteApiRequest};
|
||||
{%- if mod_path | length > 2 %}
|
||||
use crate::cloud_worker::{{ service_name }}ApiRequest;
|
||||
{%- endif %}
|
||||
{%- set prefix = service_name + resource_name %}
|
||||
|
||||
{% for mod in mod_list|sort %}
|
||||
pub mod {{ "r#" + mod if mod in ["trait", "type"] else mod }};
|
||||
{%- endfor %}
|
||||
|
||||
{% for mod in mod_list|sort %}
|
||||
pub use {{ "r#" + mod if mod in ["trait", "type"] else mod }}::*;
|
||||
{%- endfor %}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum {{ prefix }}ApiRequest {
|
||||
{%- for mod_name, mod_data in mod_list|dictsort %}
|
||||
/// {{ mod_data.name }}
|
||||
{{ mod_data.name }}(Box<{{ mod_data['class_name'] }}>),
|
||||
{%- endfor %}
|
||||
}
|
||||
|
||||
impl From<{{ prefix }}ApiRequest> for ApiRequest {
|
||||
fn from(item: {{ prefix }}ApiRequest) -> Self {
|
||||
ApiRequest::{{ service_name }}({{ service_name }}ApiRequest::from(item))
|
||||
}
|
||||
}
|
||||
|
||||
{% for mod, mod_data in mod_list|dictsort -%}
|
||||
{%- if mod not in ["list", "show", "get", "delete", "update", "create", "set", "details"] %}
|
||||
impl From<{{ mod_data.class_name }}> for {{ prefix }}ApiRequest {
|
||||
fn from(item: {{ mod_data.class_name }}) -> Self {
|
||||
{{ prefix }}ApiRequest::{{ mod_data.name }}(Box::new({{ mod_data.class_name }}::from(item)))
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
|
||||
{%- if mod_path | length > 2 %}
|
||||
{% for mod, mod_data in mod_list|dictsort -%}
|
||||
{%- if mod not in ["list", "show", "get", "delete", "update", "create", "set", "details"] %}
|
||||
impl From<{{ mod_data.class_name }}> for {{ service_name }}ApiRequest {
|
||||
fn from(item: {{ mod_data.class_name }}) -> Self {
|
||||
{{ service_name }}ApiRequest::{{ resource_name | title }}(Box::new({{ prefix }}ApiRequest::from(item)))
|
||||
}
|
||||
}
|
||||
{%- endif %}
|
||||
{% endfor %}
|
||||
{%- endif %}
|
||||
|
||||
impl ExecuteApiRequest for {{ prefix }}ApiRequest {
|
||||
async fn execute_request(
|
||||
&self,
|
||||
session: &mut AsyncOpenStack,
|
||||
request: &ApiRequest,
|
||||
app_tx: &UnboundedSender<Action>,
|
||||
) -> Result<(), CloudWorkerError> {
|
||||
match self {
|
||||
{% for mod, mod_data in mod_list|dictsort -%}
|
||||
{{ prefix }}ApiRequest::{{ mod_data.name }}(ref req) => {
|
||||
req.execute_request(session, request, app_tx).await?;
|
||||
}
|
||||
{% endfor %}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,14 +28,14 @@ resources:
|
||||
sdk_mod_name: list_detailed
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: volume list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
list:
|
||||
operation_id: volumes:get
|
||||
operation_type: list
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
create:
|
||||
operation_id: volumes:post
|
||||
operation_type: create
|
||||
@ -973,14 +973,14 @@ resources:
|
||||
sdk_mod_name: list_detailed
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: snapshot list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
list:
|
||||
operation_id: snapshots:get
|
||||
operation_type: list
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
create:
|
||||
operation_id: snapshots:post
|
||||
operation_type: create
|
||||
@ -1376,14 +1376,14 @@ resources:
|
||||
sdk_mod_name: list_detailed
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: backup list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
list:
|
||||
operation_id: backups:get
|
||||
operation_type: list
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
create:
|
||||
operation_id: backups:post
|
||||
operation_type: create
|
||||
|
@ -57,8 +57,6 @@ resources:
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
create:
|
||||
operation_id: flavors:post
|
||||
operation_type: create
|
||||
@ -80,6 +78,8 @@ resources:
|
||||
sdk_mod_name: list_detailed
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: flavor list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
show:
|
||||
operation_id: flavors/id:get
|
||||
operation_type: show
|
||||
@ -115,8 +115,6 @@ resources:
|
||||
sdk_mod_name: delete
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: flavor delete
|
||||
rust-tui:
|
||||
module_name: delete
|
||||
add-tenant-access:
|
||||
operation_id: flavors/id/action:post
|
||||
operation_type: action
|
||||
@ -273,6 +271,7 @@ resources:
|
||||
rust-cli:
|
||||
module_name: show
|
||||
sdk_mod_name: get
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate show
|
||||
rust-tui:
|
||||
module_name: get
|
||||
@ -285,6 +284,7 @@ resources:
|
||||
rust-cli:
|
||||
module_name: set
|
||||
sdk_mod_name: set
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate set
|
||||
delete:
|
||||
operation_id: os-aggregates/id:delete
|
||||
@ -295,6 +295,7 @@ resources:
|
||||
rust-cli:
|
||||
module_name: delete
|
||||
sdk_mod_name: delete
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate delete
|
||||
rust-tui:
|
||||
module_name: delete
|
||||
@ -311,6 +312,7 @@ resources:
|
||||
sdk_mod_name: add_host
|
||||
operation_name: add_host
|
||||
response_key: aggregate
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate add-host
|
||||
remove-host:
|
||||
operation_id: os-aggregates/id/action:post
|
||||
@ -325,6 +327,7 @@ resources:
|
||||
sdk_mod_name: remove_host
|
||||
operation_name: remove_host
|
||||
response_key: aggregate
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate remove-host
|
||||
set-metadata:
|
||||
operation_id: os-aggregates/id/action:post
|
||||
@ -339,7 +342,18 @@ resources:
|
||||
sdk_mod_name: set_metadata
|
||||
operation_name: set_metadata
|
||||
response_key: aggregate
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: aggregate set-metadata
|
||||
find:
|
||||
operation_id: os-aggregates:get
|
||||
operation_type: find
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: find
|
||||
sdk_mod_path: compute::v2::aggregate
|
||||
name_field: name
|
||||
name_filter_supported: false
|
||||
list_mod: list
|
||||
compute.aggregate/image:
|
||||
spec_file: wrk/openapi_specs/compute/v2.yaml
|
||||
api_version: v2
|
||||
@ -524,8 +538,6 @@ resources:
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
list_detailed:
|
||||
operation_id: os-hypervisors/detail:get
|
||||
operation_type: list
|
||||
@ -536,6 +548,8 @@ resources:
|
||||
module_name: list
|
||||
sdk_mod_name: list_detailed
|
||||
cli_full_command: hypervisor list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
show:
|
||||
operation_id: os-hypervisors/id:get
|
||||
operation_type: show
|
||||
@ -917,8 +931,6 @@ resources:
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-tui:
|
||||
module_name: list
|
||||
create:
|
||||
operation_id: servers:post
|
||||
operation_type: create
|
||||
@ -940,6 +952,8 @@ resources:
|
||||
sdk_mod_name: list_detailed
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: server list
|
||||
rust-tui:
|
||||
module_name: list_detailed
|
||||
show:
|
||||
operation_id: servers/id:get
|
||||
operation_type: show
|
||||
@ -1159,6 +1173,10 @@ resources:
|
||||
operation_name: os-getConsoleOutput
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: server get-console-output
|
||||
rust-tui:
|
||||
module_name: get_console_output
|
||||
sdk_mod_name: os_get_console_output
|
||||
operation_name: os-getConsoleOutput
|
||||
create-backup:
|
||||
operation_id: servers/id/action:post
|
||||
operation_type: action
|
||||
@ -1858,6 +1876,50 @@ resources:
|
||||
module_name: list
|
||||
sdk_mod_name: list
|
||||
cli_full_command: server security-groups
|
||||
compute.server/share:
|
||||
spec_file: wrk/openapi_specs/compute/v2.yaml
|
||||
api_version: v2
|
||||
operations:
|
||||
list:
|
||||
operation_id: servers/server_id/shares:get
|
||||
operation_type: list
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: list
|
||||
rust-cli:
|
||||
module_name: list
|
||||
sdk_mod_name: list
|
||||
cli_full_command: server share list
|
||||
create:
|
||||
operation_id: servers/server_id/shares:post
|
||||
operation_type: create
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: create
|
||||
rust-cli:
|
||||
module_name: create
|
||||
sdk_mod_name: create
|
||||
cli_full_command: server share create
|
||||
show:
|
||||
operation_id: servers/server_id/shares/id:get
|
||||
operation_type: show
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: get
|
||||
rust-cli:
|
||||
module_name: show
|
||||
sdk_mod_name: get
|
||||
cli_full_command: server share show
|
||||
delete:
|
||||
operation_id: servers/server_id/shares/id:delete
|
||||
operation_type: delete
|
||||
targets:
|
||||
rust-sdk:
|
||||
module_name: delete
|
||||
rust-cli:
|
||||
module_name: delete
|
||||
sdk_mod_name: delete
|
||||
cli_full_command: server share delete
|
||||
compute.server/tag:
|
||||
spec_file: wrk/openapi_specs/compute/v2.yaml
|
||||
api_version: v2
|
||||
|
@ -3042,6 +3042,8 @@ resources:
|
||||
sdk_mod_name: set
|
||||
find_implemented_by_sdk: true
|
||||
cli_full_command: user set
|
||||
rust-tui:
|
||||
module_name: set
|
||||
list:
|
||||
operation_id: users:get
|
||||
operation_type: list
|
||||
|
Loading…
x
Reference in New Issue
Block a user