Artem Goncharov 600d7aca5a Drop anyhow in cli templates
- drop anyhow in favor of eyre crate
- fix few things in nova changed in the meanwhile

Change-Id: I919cac5bf92e5279a4d840ddf0196e6daa74d869
2024-07-26 20:44:14 +02:00

422 lines
17 KiB
Django/Jinja

{%- macro mod_docstring(v) %}
{%- if v %}
//! {{ v | wrap_markdown(75) | replace('\n', '\n//! ') }}
{%- endif %}
{%- endmacro %}
{%- macro docstring(doc, indent=0) %}
{#- docstring for an element #}
{%- if doc %}
{{ (' ' * indent) }}/// {{ doc | trim("\n") | wrap_markdown(79-indent-4) | replace('\n', '\n' + (' ' * indent) + '/// ') }}
{%- endif %}
{%- endmacro %}
{%- macro serde_args(k, attr) -%}
{%- set serde_args=[
('rename = "' + attr.name + '"') if attr.name != k else None,
'deserialize_with="deser_ok_or_default"' if attr.default is not none and (attr.min_version is none and
attr.max_version is none) else None,
'default' if (attr.min_version is not none or
attr.max_version is not none) else None
] -%}
{{ serde_args | reject("none") | join(',') }}
{%- endmacro %}
{%- macro arg_raw_type(attr) %}
{%- if attr.type.__name__ == "str" or attr.type is none -%}
String
{%- elif attr.type.__name__ == "int" -%}
i32
{%- elif attr.type.__name__ == "float" -%}
f32
{%- elif attr.type.__name__ == "bool" -%}
bool
{%- elif attr.type.__name__ == "dict" -%}
HashMapStringString
{%- endif %}
{%- endmacro %}
{%- macro arg_type(k, attr) %}
{%- if attr.min_version is not none or attr.max_version is not none -%}
Option
{%- endif -%}
{{ arg_raw_type(attr) }}
{%- endmacro %}
{%- macro struct_field(k, v) %}
{%- set attr=v['attr'] %}
{%- if attr.type is none or attr.type.__name__ in
["str", "int", "float", "bool", "dict"]
%}
{{ docstring(v) }}
#[structable()]
#[serde({{ serde_args(k, attr) }})]
{{ k }}: {{ arg_type(k, attr) }},
{%- endif %}
{%- endmacro %}
{%- macro cli_arg_params(params) %}
{%- for param in params %}
{%- if param.schema.type != "null" %}
{{ docstring(param.description, indent=4) }}
{%- for macros in param.param_macros %}
{{ macros }}
{%- endfor %}
{{ param.local_name }}: {{ param.type }},
{%- endif %}
{%- endfor %}
{%- endmacro %}
{%- macro sdk_builder_setter_btreemap(field) %}
{%- set is_opt = False if field.data_type.__class__.__name__ != "Option" else True %}
{%- set dt = field.data_type if not is_opt else field.data_type.item_type %}
{%- set val_type = field.data_type.value_type %}
{{ docstring(field.description, indent=4) }}
pub fn {{ field.local_name }}<I, K, V{{ ",K1, V1" if val_type.__class__.__name__ == "BTreeMap" else ""}}>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = (K, V)>,
K: Into<Cow<'a, str>>,
{%- if val_type.__class__.__name__ != "BTreeMap" %}
V: Into<{{ dt.value_type.type_hint }}>,
{% else %}
V: Iterator<Item = (K1, V1)>,
K1: Into<Cow<'a, str>>,
V1: Into<{{ val_type.value_type.type_hint }}>,
{% endif%}
{
self.{{ field.local_name }}
{%- if field.is_optional %}
.get_or_insert(None)
{%- endif %}
{%- if is_opt %}
.get_or_insert(None)
{%- endif %}
.get_or_insert_with(BTreeMap::new)
.extend(iter.map(|(k, v)| (
k.into(),
{%- if val_type.__class__.__name__ != "BTreeMap" %}
v.into()
{%- else %}
BTreeMap::from_iter(
v.into_iter()
.map(|(k1, v1)| {(k1.into(), v1.into())})
)
{%- endif %}
)));
self
}
{%- endmacro %}
{%- macro sdk_builder_setter_btreeset(field) %}
{%- set is_opt = False if field.data_type.__class__.__name__ != "Option" else True %}
{%- set dt = field.data_type if not is_opt else field.data_type.item_type %}
{{ macros.docstring(field.description, indent=4) }}
pub fn {{ field.local_name }}<I, T>(&mut self, iter: I) -> &mut Self
where
I: Iterator<Item = T>,
V: Into<{{ dt.item_type.type_hint }}>,
{
self.{{ field.local_name }}
.get_or_insert_with(BTreeSet::new)
.extend(iter.map(Into::into));
self
}
{%- endmacro %}
{#- Create DeriveBuilder setter method #}
{%- macro sdk_builder_setter(field) %}
{%- set dtc = field.data_type.__class__.__name__ %}
{%- set subdtc = field.data_type.item_type.__class__.__name__ %}
{%- if dtc == "BTreeMap" or subdtc == "BTreeMap" %}
{{ sdk_builder_setter_btreemap(field) }}
{%- elif dtc == "BTreeSet" or subdtc == "BTreeSet" %}
{{ sdk_builder_setter_btreeset(field) }}
{%- endif %}
{%- endmacro %}
{%- macro wrap_optional(val, is_nullable) %}
{%- if is_nullable is defined and is_nullable -%}
Some({{ val }})
{%- else -%}
{{ val }}
{%- endif -%}
{%- endmacro %}
{#- Macros to render setting Request data from CLI input #}
{%- macro set_request_data_from_input(manager, dst_var, param, val_var) %}
{%- set is_nullable = param.is_nullable if param.is_nullable is defined else False %}
{%- if param.type_hint in ["Option<Option<bool>>", "Option<Option<i32>>", "Option<Option<i64>>"] %}
{{ dst_var }}.{{ param.remote_name }}({{ "*" + val_var }});
{%- elif param.type_hint in ["Option<i32>", "Option<i64>", "Option<f32>", "Option<f64>", "Option<bool>"] %}
{{ dst_var }}.{{ param.remote_name }}({{ "*" + val_var }});
{%- elif param.type_hint in ["i32", "i64", "f32", "f64", "bool"] %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "" )}});
{%- elif param.data_type.__class__.__name__ in ["ArrayInput"] %}
{{ sdk_plain_array_setter(param, val_var.replace("&", ""), dst_var) }}
{%- elif param.data_type.__class__.__name__ in ["JsonValue"] %}
{%- set original_type = param.data_type.original_data_type %}
{%- if original_type and original_type.__class__.__name__ == "StructInput" %}
{#- from_value needs value and not the ref #}
{{ dst_var }}.{{ param.remote_name }}(
serde_json::from_value::<{{ sdk_mod_path[-1] }}::{{ original_type.name }}>({{ val_var }}.to_owned())?
);
{%- elif original_type and original_type.__class__.__name__ == "DictionaryInput" and original_type.value_type and original_type.value_type.__class__.__name__ == "DictionaryInput" %}
{{ dst_var }}.{{ param.remote_name }}(
serde_json::from_value::<BTreeMap<String, BTreeMap<String, {{ original_type.value_type.value_type.type_hint }}>>>({{ val_var | replace("&", "") }}.clone())
.wrap_err_with(|| "Failed to parse `{{ param.local_name }}` as dict of dicts of {{ original_type.value_type.value_type.type_hint }}")?
.into_iter()
.map(|(k, v)| (k, v.into_iter())),
);
{%- else %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "" )}}.clone());
{%- endif %}
{%- elif param.data_type.__class__.__name__ == "DictionaryInput" %}
{{ dictionary_setter(param, val_var, dst_var) }}
{%- elif param.data_type.__class__.__name__ == "StringEnum" %}
let tmp = match {{ val_var }} {
{%- for variant in param.data_type.variants.keys() | sort %}
{#- normally we should use the cli enum name, but we don't have it here and names must match anyway#}
{{ param.data_type.name }}::{{ variant }} => {
{{ sdk_mod_path[-1] }}::{{ param.data_type.name }}::{{ variant }}
}
{%- endfor %}
};
{{ dst_var }}.{{ param.remote_name }}(tmp);
{%- elif param.data_type.__class__.__name__ == "EnumGroupStruct" %}
{#- This is a reverse action of Complex SDK enum being converted into the CLI group #}
{%- for k, v in param.data_type.fields | dictsort %}
{%- if v.data_type.__class__.__name__ in ["Boolean", "BooleanFlag"] %}
if {{ val_var | replace("&", "") }}.{{ v.local_name }} {
{{ dst_var }}.{{ param.remote_name }}(
{{ sdk_mod_path[-1] }}::{{ param.data_type.name }}::{{ v.sdk_parent_enum_variant }}(
{{ sdk_mod_path[-1] }}::{{ v.remote_name }}
)
);
}
{%- elif v.data_type.__class__.__name__ == "ArrayInput" %}
{% set original_type = v.data_type.original_item_type %}
if let Some(data) = {{ val_var }}.{{ v.local_name }} {
{{ sdk_enum_array_setter(manager, param, v, "data", dst_var) }}
}
{%- endif %}
{%- endfor %}
{%- elif param.data_type.__class__.__name__ == "StructInput" %}
{% set builder_name = param.local_name + "_builder" %}
let mut {{ builder_name }} = {{ sdk_mod_path[-1] }}::{{ param.data_type.name }}Builder::default();
{%- for k, v in param.data_type.fields.items() %}
{%- if v.is_optional %}
if let Some(val) = &{{ val_var }}.{{ v.local_name }} {
{{ set_request_data_from_input(manager, builder_name, v, "val") }}
}
{%- else %}
{{ set_request_data_from_input(manager, builder_name, v, "&" + val_var + "." + v.local_name) }}
{%- endif %}
{%- endfor %}
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }}.build().expect("A valid object"));
{%- elif param.data_type.__class__.__name__ == "String" %}
{%- if is_nullable and not param.is_optional %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "") }}.clone());
{%- elif is_nullable and param.is_optional %}
{{ dst_var }}.{{ param.remote_name }}(Some({{ val_var }}.into()));
{%- elif (param.is_optional is defined and param.is_optional) or (param.is_required is defined and not param.is_required) %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var }});
{%- else %}
{{ dst_var }}.{{ param.remote_name }}(&{{ val_var | replace("&", "") }});
{%- endif %}
{%- elif param.data_type.__class__.__name__ == "Option" %}
{%- if param.data_type.item_type.__class__.__name__ == "StructInput" %}
if let Some(l{{ param.local_name }}) = &{{ val_var | replace("&", "") }} {
{% set builder_name = param.local_name + "_builder" %}
let mut {{ builder_name }} = {{ sdk_mod_path[-1] }}::{{ param.data_type.item_type.name }}Builder::default();
{%- for k, v in param.data_type.item_type.fields.items() %}
{%- if v.is_optional %}
if let Some(val) = &l{{ param.local_name }}.{{ v.local_name }} {
{{ set_request_data_from_input(manager, builder_name, v, "val") }}
}
{%- else %}
{{ set_request_data_from_input(manager, builder_name, v, "&l" + param.local_name + "." + v.local_name) }}
{%- endif %}
{%- endfor %}
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }}.build().expect("A valid object"));
}
{%- elif param.data_type.item_type.__class__.__name__ == "JsonValue" %}
{#- atm it is not possible to pass ownership of Option<Json> #}
if let Some(val) = {{ val_var }} {
{{ dst_var }}.{{ param.remote_name }}(val.clone());
}
{%- else %}
{{ dst_var }}.{{ param.remote_name }}(*{{ val_var }});
{%- endif %}
{%- else %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var }});
{%- endif %}
{%- endmacro %}
{%- macro dictionary_setter(param, val_var, dst_var) %}
{#- Set sdk dictionary from cli input -#}
{%- set original_type = param.data_type.value_type.original_data_type %}
{%- if param.data_type.value_type.__class__.__name__ == "JsonValue" and original_type.__class__.__name__ == "StructInput" %}
{% set builder_name = param.local_name + "_builder" %}
{{ dst_var}}.{{ param.remote_name }}(
{{ val_var }}
.into_iter()
.map(|(k, v)| {
serde_json::from_value(v.to_owned()).map(|v: {{ sdk_mod_path[-1] }}::{{ original_type.name }}| (k, v))
})
.collect::<Result<Vec<_>, _>>()?
.into_iter(),
);
{%- elif param.data_type.value_type.__class__.__name__ == "Option" %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "") }}.iter().cloned().map(|(k, v)| (k, v.map(Into::into))));
{%- else %}
{{ dst_var }}.{{ param.remote_name }}({{ val_var | replace("&", "") }}.iter().cloned());
{%- endif %}
{%- endmacro %}
{%- macro sdk_enum_array_setter(manager, param, field, val_var, dst_var) %}
{#- Set sdk array from cli input -#}
{%- set original_type = field.data_type.original_data_type %}
{%- if field.data_type.item_type.__class__.__name__ == "JsonValue" and original_type.__class__.__name__ == "StructInput" %}
{% set builder_name = param.local_name + "_builder" %}
let {{ builder_name }}: Vec<{{ sdk_mod_path[-1] }}::{{ original_type.name }}> = {{ val_var }}
.iter()
.flat_map(|v|
serde_json::from_value::<{{ sdk_mod_path[-1] }}::{{ original_type.name }}>(v.to_owned()))
.collect();
{{ dst_var }}.{{ param.remote_name }}(
{{ sdk_mod_path[-1] }}::{{ param.data_type.name }}::{{ field.remote_name }}({{ builder_name }})
);
{%- else %}
{#- Normal array #}
{{ dst_var }}.{{ param.remote_name }}(
{{ sdk_mod_path[-1] }}::{{ field.remote_name }}({{ val_var }}.into_iter())
);
{%- endif %}
{%- endmacro %}
{%- macro sdk_plain_array_setter(param, val_var, dst_var) %}
{%- set original_type = param.data_type.original_data_type %}
{%- set original_item_type = param.data_type.item_type.original_data_type %}
{%- if param.data_type.item_type.__class__.__name__ == "JsonValue" and original_type.__class__.__name__ == "StructInput" %}
{% set builder_name = param.local_name + "_builder" %}
let {{ builder_name }}: Vec<{{ sdk_mod_path[-1] }}::{{ original_type.name }}> = {{ val_var }}
.iter()
.flat_map(|v|
serde_json::from_value::<{{ sdk_mod_path[-1] }}::{{ original_type.name }}>(v.to_owned()))
.collect::<Vec<{{ sdk_mod_path[-1] }}:: {{ original_type.name }}>>();
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }});
{%- elif param.data_type.item_type.__class__.__name__ == "String" and original_item_type.__class__.__name__ == "StructInput" %}
{#- Single field structure replaced with only string #}
{%- set original_type = param.data_type.item_type.original_data_type %}
{%- set original_field = original_type.fields[param.data_type.item_type.original_data_type.fields.keys()|list|first] %}
{% set builder_name = param.local_name + "_builder" %}
let {{ builder_name }}: Vec<{{ sdk_mod_path[-1] }}::{{ original_type.name }}> = {{ val_var }}
.iter()
.flat_map(|v| {{ sdk_mod_path[-1] }}::{{ original_type.name }}Builder::default()
.{{ original_field.remote_name }}(v)
.build()
)
.collect();
{{ dst_var }}.{{ param.remote_name }}({{ builder_name }});
{%- elif param.data_type.item_type.__class__.__name__ == "String" and original_type.__class__.__name__ == "ArrayInput" %}
{#- Single field structure replaced with only string #}
{{ dst_var }}.{{ param.remote_name }}(
val.iter()
.cloned()
.map(|x| Vec::from([x.split(",").collect()]))
.collect::<Vec<_>>(),
);
{%- elif param["setter_type"] is defined %}
{#- Param with setter present #}
{{ dst_var }}.{{ param.remote_name }}(
{{ val_var }}.iter()
);
{%- elif original_item_type and original_item_type.__class__.__name__ == "DictionaryInput" %}
use std::collections::BTreeMap;
{{ dst_var }}.{{ param.remote_name }}(
{{ val_var }}.iter()
.map( |v| {
v.as_object()
.expect("Is a valid Json object")
.into_iter()
.map(|(k, v)| (k.into(), v.clone().into()))
.collect::<BTreeMap<_,Value>>()
})
.collect::<Vec<_>>()
);
{%- else %}
{#- Normal array #}
{{ dst_var }}.{{ param.remote_name }}(
{{ val_var }}.iter().map(Into::into).collect::<Vec<_>>()
);
{%- endif %}
{%- endmacro %}
{%- macro get_data_for_sdk(data, data_var) %}
{%- if data.__class__.__name__ == "ArrayInput" -%}
{{ data_var }}.iter().cloned()
{%- endif -%}
{%- endmacro %}
{%- macro set_cli_path_parameters(type_manager, builder, find_mode=False) %}
{%- if not find_mode %}
// Set path parameters
{%- endif %}
{%- for (k, v) in type_manager.get_parameters("path") %}
{%- if not v.is_required %}
{%- if k != "project_id" %}
if let Some(val) = &self.path.{{ v.local_name }} {
{{ builder }}.{{ v.local_name }}(val);
}
{%- else %}
if let Some(val) = &self.path.{{ v.local_name }} {
{{ builder }}.{{ v.local_name }}(val);
} else {
{{ builder }}.{{ v.local_name }}(client.get_current_project().expect("Project ID must be known").id);
}
{%- endif %}
{%- elif not find_mode and find_present and operation_type in ["show", "set", "download"] %}
let resource_id = find_data["id"]
.as_str()
.expect("Resource ID is a string")
.to_string();
{{ builder }}.{{ v.local_name }}(resource_id.clone());
{%- else %}
{{ builder }}.{{ v.local_name }}(&self.path.{{ v.local_name }});
{%- endif %}
{%- endfor %}
{%- endmacro %}
{%- macro sdk_build_url(url, params, prefix = "", from_attr = False) %}
{%- if params.values()|selectattr("location", "equalto", "path")|list|length > 0 -%}
format!(
"{{ prefix }}{{ url }}",
{%- for k, v in params.items() %}
{%- if v.location == "path" %}
{{ k }} = {{ ('"' + k + '"') if not from_attr else ('self.' + v.local_name + '.as_ref()') }},
{%- endif %}
{%- endfor %}
)
{%- else %}
"{{ prefix }}{{ url }}".to_string()
{%- endif %}
{%- endmacro %}