Skip to content

Commit

Permalink
Feature generic_full_path:
Browse files Browse the repository at this point in the history
- stabilize it
- add support for partial imports

Closes: #20
  • Loading branch information
DenuxPlays committed Jul 28, 2024
1 parent ef500ab commit a0d729c
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 42 deletions.
21 changes: 4 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
<div align="center">
<div style="text-align: center">
<h1>Utoipauto</h1>
<p>
<strong>Rust Macros to automate the addition of Paths/Schemas to Utoipa crate, simulating Reflection during the compilation phase</strong>
</p>
<p>
</p>
</div>

# Crate presentation
Expand Down Expand Up @@ -111,24 +109,13 @@ If you want to use generics, you have three ways to do it.
use path::to::schema;
```

3. use experimental `generic_full_path` feature
3. use `generic_full_path` feature

Please keep in mind that this is an experimental feature and causes more build-time overhead.
<br>
Please keep in mind that this is an experimental feature and causes more build-time overhead.
Higher RAM usage, longer compile times and excessive disk usage (especially on larger projects) are the consequences.
<br>
Also we currently do not support "partial" imports. The following is **NOT** supported:

```rust
use path::to::generic;

#[aliases(GenericSchema = generic::Schema)]
```

Please use the full path instead or the `as` keyword to rename the imported schemas.

```toml
utoipauto = { version = "0.2.0", feature = ["generic_full_path"] }
utoipauto = { version = "*", feature = ["generic_full_path"] }
```

## Usage with workspaces
Expand Down
3 changes: 3 additions & 0 deletions tests/generic_full_path/even_more_schemas.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub struct EvenMoreSchema;

pub struct EvenMoreSchema2;
2 changes: 2 additions & 0 deletions tests/generic_full_path/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
mod even_more_schemas;
mod more_schemas;
mod partial_imports;
mod schemas;
mod test;
22 changes: 22 additions & 0 deletions tests/generic_full_path/partial_imports.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use utoipa::ToSchema;

use crate::generic_full_path::even_more_schemas as schemas;
use crate::generic_full_path::more_schemas;

#[derive(ToSchema)]
#[aliases(
PartialImportGenericSchemaMoreSchema = PartialImportGenericSchema < more_schemas::MoreSchema >,
PartialImportGenericSchemaMoreSchema2 = PartialImportGenericSchema < more_schemas::MoreSchema2 >
)]
pub struct PartialImportGenericSchema<T> {
_data: T,
}

#[derive(ToSchema)]
#[aliases(
PartialImportEvenMoreGenericSchemaAsImport = PartialImportGenericSchema < schemas::EvenMoreSchema >,
PartialImportEvenMoreGenericSchema2AsImport = PartialImportGenericSchema < schemas::EvenMoreSchema2 >
)]
pub struct PartialImportGenericSchemaAsImport<T> {
_data: T,
}
71 changes: 54 additions & 17 deletions utoipauto-core/src/discover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,10 @@ fn parse_from_attr(
.expect("Failed to parse derive attribute");
for nested_meta in nested {
if nested_meta.path().segments.len() == 2 {
if nested_meta.path().segments[0].ident.to_string() == "utoipa" {
if nested_meta.path().segments[1].ident.to_string() == "ToSchema"
&& !is_generic
{
if nested_meta.path().segments[0].ident == "utoipa" {
if nested_meta.path().segments[1].ident == "ToSchema" && !is_generic {
out.push(DiscoverType::Model(name.to_string()));
} else if nested_meta.path().segments[1].ident.to_string() == "ToResponse"
} else if nested_meta.path().segments[1].ident == "ToResponse"
&& !is_generic
{
out.push(DiscoverType::Response(name.to_string()));
Expand Down Expand Up @@ -197,9 +195,8 @@ fn parse_generic_schema(meta: ParseNestedMeta, name: &str, _imports: Vec<String>
}

let generics = merge_nested_generics(nested_generics);
let generic_type = name.to_string() + &generics;

generic_type
name.to_string() + &generics
}

#[cfg(feature = "generic_full_path")]
Expand Down Expand Up @@ -408,21 +405,17 @@ fn extract_use_statements(file_path: &str, crate_name: &str) -> Vec<String> {
#[cfg(feature = "generic_full_path")]
fn find_import(imports: Vec<String>, current_module: &str, name: &str) -> String {
let name = name.trim();
for import in imports {
let current_module = current_module.trim();
for import in imports.iter() {
if import.contains(name) {
let import = import
.split(" as ")
.next()
.unwrap_or(&import)
.trim()
.to_string();
return import;
let full_path = import_to_full_path(import);
return full_path;
}
}

// If the name contains `::` it means it's already a full path
// If the name contains `::` it means that it's a partial import or a full path
if name.contains("::") {
return name.to_string();
return handle_partial_import(imports, name).unwrap_or_else(|| name.to_string());
}

// Only append the module path if the name does not already contain it
Expand All @@ -433,6 +426,35 @@ fn find_import(imports: Vec<String>, current_module: &str, name: &str) -> String
name.to_string()
}

#[cfg(feature = "generic_full_path")]
fn handle_partial_import(imports: Vec<String>, name: &str) -> Option<String> {
name.split("::").next().and_then(|first| {
let first = first.trim();

for import in imports {
let import = import.trim();

if import.ends_with(first) {
let full_path = import_to_full_path(&import);

let usable_import = format!("{}{}", full_path.trim(), name[first.len()..].trim());
return Some(usable_import);
}
}
None
})
}

#[cfg(feature = "generic_full_path")]
fn import_to_full_path(import: &str) -> String {
import
.split(" as ")
.next()
.unwrap_or(&import)
.trim()
.to_string()
}

#[cfg(feature = "generic_full_path")]
fn get_current_module_from_name(name: &str) -> String {
let parts: Vec<&str> = name.split("::").collect();
Expand All @@ -443,6 +465,9 @@ fn get_current_module_from_name(name: &str) -> String {
mod test {
use quote::quote;

#[cfg(feature = "generic_full_path")]
use crate::discover::{find_import, get_current_module_from_name, process_one_generic};

#[test]
fn test_parse_function() {
let quoted = quote! {
Expand Down Expand Up @@ -475,6 +500,18 @@ mod test {
assert_eq!(result, expected);
}

#[test]
#[cfg(feature = "generic_full_path")]
fn test_process_one_generic_partial_import() {
let part = "PartialImportGenericSchema<more_schemas::MoreSchema>";
let name = "crate";
let imports = vec!["crate::generic_full_path::more_schemas".to_string()];
let expected =
"PartialImportGenericSchema<crate::generic_full_path::more_schemas::MoreSchema>";
let result = process_one_generic(part, name, imports);
assert_eq!(result, expected);
}

#[test]
#[cfg(feature = "generic_full_path")]
fn test_process_one_generic_no_nested_generics() {
Expand Down
13 changes: 7 additions & 6 deletions utoipauto-core/src/file_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,14 @@ pub fn parse_files<T: Into<PathBuf>>(path: T) -> Result<Vec<(String, syn::File)>
}

fn is_rust_file(path: &Path) -> bool {
path.is_file() && match path.extension() {
Some(ext) => match ext.to_str() {
Some(ext) => ext.eq("rs"),
path.is_file()
&& match path.extension() {
Some(ext) => match ext.to_str() {
Some(ext) => ext.eq("rs"),
None => false,
},
None => false,
},
None => false,
}
}
}

/// Extract the module name from the file path
Expand Down
4 changes: 2 additions & 2 deletions utoipauto-core/src/string_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,12 @@ pub fn trim_parentheses(str: &str) -> String {
/// paths,
/// vec![
/// "./utoipa-auto-macro/tests/controllers/controller1.rs".to_string(),
/// "./utoipa-auto-macro/tests/controllers/controller2.rs".to_string()
/// "./utoipa-auto-macro/tests/controllers/controller2.rs".to_string(),
/// ]
/// );
/// ```
pub fn extract_paths(attributes: &str) -> Vec<String> {
let attributes = trim_parentheses(&attributes);
let attributes = trim_parentheses(attributes);

if attributes.contains('|') {
panic!("Please use the new syntax ! paths=\"(MODULE_TREE_PATH => MODULE_SRC_PATH) ;\"")
Expand Down

0 comments on commit a0d729c

Please sign in to comment.