Skip to content

Commit

Permalink
Feature custom ToSchema rename (#354)
Browse files Browse the repository at this point in the history
Add support for custom `rename` and `rename_all` attribute on `ToSchema`
derived types. These attributes work like the serde counter parts can be used
instead of serde ones. But if both are defined then serde `rename` and `rename_all`
will take prededence.

Add support for `title` attribute for `ToSchema` derived types. Currently there
is one limitation with `title` and it cannot be put on complex enum because of 
limitations in types used to generate OpenAPI spec. in `utoipa`.

Update docs and add more examples regarding rename and title on `ToSchema`.
Add tests for possible configuration options.

Refactor `ToSchema`  enum `ToTokens` functionality once more to simplify it.
  • Loading branch information
juhaku authored Nov 19, 2022
1 parent a84e41c commit c99ddfe
Show file tree
Hide file tree
Showing 11 changed files with 1,078 additions and 455 deletions.
10 changes: 2 additions & 8 deletions utoipa-gen/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,16 +310,10 @@ trait Rename {
/// * `container_rule` which is used to rename containers with _`rename_all`_ property.
fn rename<'r, R: Rename>(
value: &'r str,
to: Option<&'r str>,
to: Option<Cow<'r, str>>,
container_rule: Option<&'r RenameRule>,
) -> Option<Cow<'r, str>> {
let rename = to.as_ref().and_then(|to| {
if !to.is_empty() {
Some(Cow::Borrowed(*to))
} else {
None
}
});
let rename = to.and_then(|to| if !to.is_empty() { Some(to) } else { None });

rename.or_else(|| {
container_rule
Expand Down
33 changes: 25 additions & 8 deletions utoipa-gen/src/component/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,13 @@ impl ToTokensExt for Vec<Feature> {
pub trait FeaturesExt {
fn pop_by(&mut self, op: impl FnMut(&Feature) -> bool) -> Option<Feature>;

fn find_value_type_feature_as_value_type(&mut self) -> Option<super::features::ValueType>;
fn pop_value_type_feature(&mut self) -> Option<super::features::ValueType>;

fn find_rename_feature_as_rename(&mut self) -> Option<Rename>;
/// Pop [`Rename`] feature if exists in [`Vec<Feature>`] list.
fn pop_rename_feature(&mut self) -> Option<Rename>;

/// Pop [`RenameAll`] feature if exists in [`Vec<Feature>`] list.
fn pop_rename_all_feature(&mut self) -> Option<RenameAll>;
}

impl FeaturesExt for Vec<Feature> {
Expand All @@ -570,36 +574,49 @@ impl FeaturesExt for Vec<Feature> {
.map(|index| self.swap_remove(index))
}

fn find_value_type_feature_as_value_type(&mut self) -> Option<super::features::ValueType> {
fn pop_value_type_feature(&mut self) -> Option<super::features::ValueType> {
self.pop_by(|feature| matches!(feature, Feature::ValueType(_)))
.and_then(|feature| match feature {
Feature::ValueType(value_type) => Some(value_type),
_ => None,
})
}

fn find_rename_feature_as_rename(&mut self) -> Option<Rename> {
fn pop_rename_feature(&mut self) -> Option<Rename> {
self.pop_by(|feature| matches!(feature, Feature::Rename(_)))
.and_then(|feature| match feature {
Feature::Rename(rename) => Some(rename),
_ => None,
})
}

fn pop_rename_all_feature(&mut self) -> Option<RenameAll> {
self.pop_by(|feature| matches!(feature, Feature::RenameAll(_)))
.and_then(|feature| match feature {
Feature::RenameAll(rename_all) => Some(rename_all),
_ => None,
})
}
}

impl FeaturesExt for Option<Vec<Feature>> {
fn pop_by(&mut self, op: impl FnMut(&Feature) -> bool) -> Option<Feature> {
self.as_mut().and_then(|features| features.pop_by(op))
}

fn find_value_type_feature_as_value_type(&mut self) -> Option<super::features::ValueType> {
fn pop_value_type_feature(&mut self) -> Option<super::features::ValueType> {
self.as_mut()
.and_then(|features| features.pop_value_type_feature())
}

fn pop_rename_feature(&mut self) -> Option<Rename> {
self.as_mut()
.and_then(|features| features.find_value_type_feature_as_value_type())
.and_then(|features| features.pop_rename_feature())
}

fn find_rename_feature_as_rename(&mut self) -> Option<Rename> {
fn pop_rename_all_feature(&mut self) -> Option<RenameAll> {
self.as_mut()
.and_then(|features| features.find_rename_feature_as_rename())
.and_then(|features| features.pop_rename_all_feature())
}
}

Expand Down
18 changes: 6 additions & 12 deletions utoipa-gen/src/component/into_params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,22 +257,16 @@ impl ToTokens for Param<'_> {
field_features.push(style.clone()); // could try to use cow to avoid cloning
};
}
let value_type = field_features.find_value_type_feature_as_value_type();
let value_type = field_features.pop_value_type_feature();
let is_inline = field_features
.pop_by(|feature| matches!(feature, Feature::Inline(_)))
.is_some();
let rename = field_features
.find_rename_feature_as_rename()
.pop_rename_feature()
.map(|rename| rename.into_value());
let rename = field_param_serde
let rename_to = field_param_serde
.as_ref()
.and_then(|field_param_serde| {
if !field_param_serde.rename.is_empty() {
Some(Cow::Borrowed(field_param_serde.rename.as_str()))
} else {
None
}
})
.and_then(|field_param_serde| field_param_serde.rename.as_deref().map(Cow::Borrowed))
.or_else(|| rename.map(Cow::Owned));
let rename_all = self
.serde_container
Expand All @@ -283,8 +277,8 @@ impl ToTokens for Param<'_> {
.rename_all
.map(|rename_all| rename_all.as_rename_rule())
});
let name = super::rename::<FieldRename>(name, rename.as_deref(), rename_all)
.unwrap_or(Cow::Borrowed(name));
let name =
super::rename::<FieldRename>(name, rename_to, rename_all).unwrap_or(Cow::Borrowed(name));
let type_tree = TypeTree::from_type(&field.ty);

tokens.extend(quote! { utoipa::openapi::path::ParameterBuilder::new()
Expand Down
Loading

0 comments on commit c99ddfe

Please sign in to comment.