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:
Artem Goncharov 2024-12-19 17:40:51 +01:00
parent 5d38233548
commit 9c8491f2b5
14 changed files with 516 additions and 74 deletions

View File

@ -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)

View File

@ -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"

View File

@ -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(

View File

@ -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

View File

@ -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(

View File

@ -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)

View File

@ -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")

View File

@ -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)

View File

@ -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;

View File

@ -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 %}

View 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(())
}
}

View File

@ -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

View File

@ -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

View File

@ -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