utoipauto

repository·main·Indexed 41 days ago

https://github.com/probablyclem/utoipauto

A Rust macro crate that automates the addition of OpenAPI paths and schemas to the utoipa crate. It scans source files for specific attributes—such as #[utoipa::path(...)], ToSchema, and ToResponse—to eliminate the need to manually list every endpoint and DTO in the #[openapi] macro.

Tokens
4.6K
Snippets
20
Records
22
Agent score
51%

What's inside utoipauto

  1. Understand the purpose of the acceptance tests workspace

    main
    The acceptance workspace contains multiple crates designed to test various aspects of the utoipauto macro. These tests serve as a reference for how end-users are expected to interact with and use the library in real-world scenarios.
  2. How to use the utoipauto macro

    main

    The #[utoipauto] macro automates the discovery of #[utoipa::path(...)] methods, ToSchema structs, and ToResponse implementations to reduce verbosity in your OpenApi definition.

    Critical Requirement: You must place the #[utoipauto] macro before the #[derive(OpenApi)] and #[openapi] macros.

    use utoipauto::utoipauto;
    
    #[utoipauto]
    #[derive(OpenApi)]
    #[openapi(tags(...))]
    pub struct ApiDoc;
    use utoipauto::utoipauto;
    
    #[utoipauto]
    #[derive(OpenApi)]
    #[openapi(
        tags(
            (name = "todo", description = "Todo management endpoints.")
        ),
        modifiers(&SecurityAddon)
    )]
    pub struct ApiDoc;
  3. Exclude items from automatic scanning

    main

    You can prevent specific items or files from being picked up by the macro.

    Exclude a method or struct

    Use the #[utoipa_ignore] macro on a function or a struct to skip it during discovery.

    #[utoipa_ignore]
    #[utoipa::path(...)]
    async fn ignored_function() { ... }
    
    #[utoipa_ignore]
    #[derive(ToSchema)]
    struct IgnoredModel { ... }

    Exclude files and directories using glob patterns

    Use the exclude attribute with glob patterns (supporting *, **, ?, and brace alternation) to skip specific files or directories.

    Note: The default exclusion is ["**/._*"] (macOS sidecar files). If you provide your own exclude list, you must include **/._* manually if you want to keep skipping them.

    • Custom exclusion: #[utoipauto(paths = "./src", exclude = ["**/generated/**", "**/*_test.rs"])]
    • Include default exclusions + custom: #[utoipauto(exclude = ["**/._*", "**/generated/**"])]
    • Disable all exclusions: #[utoipauto(exclude = [])]
    #[utoipauto(paths = "./src", exclude = ["**/generated/**", "**/*_test.rs"])]
    #[derive(OpenApi)]
    #[openapi(tags())]
    pub struct ApiDoc;
  4. Configure paths for automatic scanning

    main

    You can control which files or modules the macro scans using the paths argument. The paths argument accepts a comma-separated string of file or directory paths.

    Import from src folder (Default)

    If you omit the paths argument, the macro automatically scans the src folder.

    Import from a specific module or directory

    Specify a directory to scan all methods and structs within it:

    #[utoipauto(paths = "./src/rest")]

    Import from specific files

    Provide a comma-separated list of specific .rs files:

    #[utoipauto(paths = "./src/rest/test_controller.rs,./src/rest/test2_controller.rs ")]

    Usage in Workspaces

    When working in a workspace, you must include the crate name in the path. To specify that paths belong to another crate, use the from keyword:

    • Standard workspace path: #[utoipauto(paths = "./utoipauto/src")]
    • Cross-crate path: #[utoipauto(paths = "./utoipauto/src from utoipauto")]
    • Sub-folder with crate mapping: #[utoipauto(paths = "(./src/lib/rest from crate::rest)")]
    #[utoipauto(paths = "./src/rest/test_controller.rs,./src/rest/test2_controller.rs ")]
    #[derive(OpenApi)]
    #[openapi(
        paths(
            crate::rest::other_controller::get_users,
        ),
        components(
            schemas(TestDTO)
        ),
        tags(
            (name = "todo", description = "Todo management endpoints.")
        ),
        modifiers(&SecurityAddon)
    )]
    pub struct ApiDoc;
  5. Exclude items from discovery using `utoipa_ignore`

    main

    To prevent specific functions, structs, or enums from being automatically discovered and included in your OpenAPI documentation, apply the #[utoipa_ignore] attribute to the item.

    This attribute is respected by both the function scanner and the attribute-based scanner for structs and enums.

    #[utoipa_ignore]
    pub fn hidden_route() {}
    
    #[utoipa_ignore]
    #[derive(ToSchema)]
    pub struct HiddenModel {}
  6. Correct placement of the utoipauto macro

    main

    The utoipauto macro must be placed before the #[derive] and #[openapi] attributes on a struct. If the macro is placed after these attributes, the compiler will panic.

    Correct Pattern:

    #[utoipauto(...)]
    #[derive(ToSchema)]
    #[openapi(...)]
    struct MyStruct;
    #[utoipauto()]
    #[derive(ToSchema)]
    #[openapi()]
    struct MyStruct;
  7. Customize attribute detection for paths, schemas, and responses

    main

    By default, utoipauto looks for #[utoipa::path(...)], ToSchema, and ToResponse. You can override these by specifying custom attribute names in the #[utoipauto] macro.

    Custom function attribute

    Use function_attribute_name to specify which attribute identifies a path handler:

    #[utoipauto(function_attribute_name = "handler")]

    Custom model and response attributes

    Use schema_attribute_name and response_attribute_name to specify custom derive macros:

    #[utoipauto(schema_attribute_name = "Schema", response_attribute_name = "Response")]
    #[handler]
    pub fn custom_router() { /* ... */ }
    
    #[utoipauto(function_attribute_name = "handler")]
    #[derive(OpenApi)]
    #[openapi(tags())]
    pub struct ApiDoc;
  8. Configure utoipauto macro attributes

    main

    When using the utoipauto macro, you can pass several configuration attributes to customize how it scans your project and which attributes it looks for. These attributes are parsed from the macro invocation.

    Available Attributes

    AttributeTypeDefault ValueDescription
    pathsString./srcThe directory path to scan for source files.
    function_attribute_nameStringutoipaThe name of the attribute used to identify handler functions.
    schema_attribute_nameStringToSchemaThe name of the attribute used to identify schema definitions.
    response_attribute_nameStringToResponseThe name of the attribute used to identify response definitions.
    excludeList of Strings["**/._*"]A list of glob patterns to exclude from scanning. Note: Providing an explicit list replaces the defaults entirely. To scan everything, provide an empty list [].

    Usage Example

    // Example of a macro invocation with custom configuration
    utoipauto!(
        paths = "./my_api",
        function_attribute_name = "my_handler",
        exclude = ["**/generated/**", "**/*_test.rs"]
    )
    utoipauto!(
        paths = "./my_api",
        function_attribute_name = "my_handler",
        exclude = ["**/generated/**", "**/*_test.rs"]
    )
  9. Parse multiple Rust files with exclusions

    main

    Use parse_files to recursively scan a directory or a single file for Rust (.rs) files. It returns a Vec<(String, syn::File)> where each tuple contains the file path as a String and the parsed syn::File AST.

    To prevent scanning specific files or directories (like target/ or generated files), provide a GlobSet created via build_exclude_set.

    use utoipauto_core::file_utils::{parse_files, build_exclude_set};
    
    let excludes = build_exclude_set(&["**/generated/**", "target/"]);
    let files = parse_files("./src", &excludes)?;
  10. Extract file paths from attributes using `extract_paths`

    main

    The extract_paths function parses attribute strings to retrieve a list of file paths. It supports two distinct syntaxes:

    1. Legacy Syntax (Arrow Syntax): Uses a mapping of MODULE_TREE_PATH => MODULE_SRC_PATH separated by semicolons.
      • Example: (path_a => file_a.rs) ; (path_b => file_b.rs)
    2. New Syntax (Comma Syntax): A simple comma-separated list of file paths.
      • Example: file_a.rs, file_b.rs

    Note: If the input contains a pipe character (|), the function will panic. It is expected that users follow the paths="(MODULE_TREE_PATH => MODULE_SRC_PATH) ;" pattern for legacy mapping or a simple comma-separated list for new syntax.

    use utoipauto_core::string_utils::extract_paths;
    
    let paths = extract_paths(
       "(utoipa_auto_macro::tests::controllers::controller1 => ./utoipa-auto-macro/tests/controllers/controller1.rs) ; (utoipa_auto_macro::tests::controllers::controller2 => ./utoipa-auto-macro/tests/controllers/controller2.rs)"
    );
    
    assert_eq!(
      paths,
      vec![
        "./utoipa-auto-macro/tests/controllers/controller1.rs".to_string(),
        "./utoipa-auto-macro/tests/controllers/controller2.rs".to_string(),
      ]
    );