Skip to content

Conversation

@nanoandrew4
Copy link
Contributor

@nanoandrew4 nanoandrew4 commented Jan 15, 2026

Partially addresses #225

Other lints were considered but rejected for now in favor of enforcing the guidelines, especially around documentation, with macros at compile time.

Summary by CodeRabbit

  • New Features

    • Added api_dto macro to generate REST DTO serialization, schema and request/response wiring.
    • New lints: enforce snake_case for API JSON names; forbid api_dto in contract layer; require api_dto for API DTOs.
  • Improvements

    • Converted many REST DTOs and tests to macro-driven DTOs for consistent serialization and OpenAPI output.
    • Operation builders and APIs now require DTO marker traits for stricter request/response typing.
  • Documentation

    • Expanded docs and READMEs for the new macro and lint guidance.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 15, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: adding a new lint to enforce snake-case usage with the new api_dto macro.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from b3d16ce to 84540b5 Compare January 15, 2026 10:14
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml`:
- Around line 21-84: Add the missing example entry for the UI test file by
creating an example target named "fail_pascal_case" that points to
"ui/fail_pascal_case.rs" so every file in the ui/ directory has a corresponding
[[example]] entry; update the Cargo.toml examples list to include this new
[[example]] with name fail_pascal_case and path ui/fail_pascal_case.rs.
🧹 Nitpick comments (3)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/README.md (1)

1-9: Well-documented lint rule.

The README clearly explains what the lint does, why it matters, and provides helpful examples.

Minor markdown nit: Consider using ## (h2) instead of ### (h3) for section headings to follow proper heading hierarchy after the h1 title.

📝 Optional heading level fix
 # de0803_api_snake_case

-### What it does
+## What it does
 Checks that DTOs (structs and enums) defined in the `api/rest` directory use `snake_case` for serde renaming configurations. Specifically:
 1. `#[serde(rename_all = "...")]` on structs/enums must be "snake_case".
 2. `#[serde(rename = "...")]` on fields must be in snake_case.

-### Why is this bad?
+## Why is this bad?
 The API standard requires all JSON properties to be in `snake_case`. Using other casing styles (like `camelCase`, `PascalCase`, etc.) leads to inconsistent API responses and violations of the project's API guidelines.

-### Example
+## Example
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml (1)

1-19: Good crate structure following conventions.

The crate correctly uses workspace dependencies for clippy_utils, dylint_linting, lint_utils, dylint_testing, and serde as per coding guidelines.

Consider updating edition to "2024" for consistency.

Other lint crates in this PR (e.g., de0110_no_schema_for_on_gts_structs, de0902_no_schema_for_on_gts_structs) use edition = "2024". This crate uses edition = "2021", which creates inconsistency across the workspace.

Suggested fix
 [package]
 name = "de0803_api_snake_case"
 version = "0.1.0"
 authors = ["Hypernetix"]
 description = "DTOs must not use CamelCase or PascalCase in serde rename_all, only snake_case is allowed"
-edition = "2021"
+edition = "2024"
 publish = false
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs (1)

5-6: Minor: Comment description doesn't match the violation type.

The comment says "DTO fields must not use non-snake_case" but this test validates a type-level rename_all attribute, not a field-level rename. Consider updating to:

-// Should trigger DE0803 - DTO fields must not use non-snake_case in serde rename/rename_all
+// Should trigger DE0803 - DTOs must not use non-snake_case in serde rename_all

This would more accurately describe the specific violation being tested and align with the actual lint message.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2478dc5 and 84540b5.

⛔ Files ignored due to path filters (1)
  • dylint_lints/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (45)
  • dylint_lints/Cargo.toml
  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/README.md
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/rust-toolchain
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.stderr
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
🧰 Additional context used
📓 Path-based instructions (2)
dylint_lints/**/Cargo.toml

📄 CodeRabbit inference engine (dylint_lints/AGENTS.md)

dylint_lints/**/Cargo.toml: In Cargo.toml for lint crates, reference common workspace dependencies (clippy_utils, dylint_linting, lint_utils) using .workspace = true to avoid duplication
Define example targets in lint crate Cargo.toml with [[example]] sections for each UI test case
Register new lint crates by adding them to the members list in the workspace root Cargo.toml

Files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
dylint_lints/**/src/lib.rs

📄 CodeRabbit inference engine (dylint_lints/AGENTS.md)

dylint_lints/**/src/lib.rs: Use declare_pre_expansion_lint! for lints that check derive attributes before macro expansion (e.g., detecting serde/utoipa derives)
Use EarlyLintPass with rustc_ast for pre-expansion lints that need to see #[derive(...)] attributes directly before macro expansion
Use declare_early_lint! for checking syntax and structure after macro expansion but before type resolution without type information
Use declare_late_lint! with LateLintPass and rustc_hir when type information or semantic analysis is required
Use ui_test_examples when tests need external dependencies (serde, utoipa) or have logically distinct scenarios with explicit test organization
Use ui_test when all tests have no external dependencies and you have many small, similar test cases for simpler configuration
Every lint crate MUST include a test_comment_annotations_match_stderr test using lint_utils::test_comment_annotations_match_stderr to validate comment/stderr alignment
Use is_in_contract_module_ast() helper from lint_utils crate to check if an AST item is in a contract/ directory
Document lint behavior in doc comments within the lint implementation
Select pre-expansion lint pass for detecting serde/utoipa derives that must be checked before proc macros expand

Files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
🧠 Learnings (22)
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/*.rs : Include at least one test case with no violations (empty or near-empty `.stderr` file) in UI tests

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Every lint crate MUST include a `test_comment_annotations_match_stderr` test using `lint_utils::test_comment_annotations_match_stderr` to validate comment/stderr alignment

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Select pre-expansion lint pass for detecting serde/utoipa derives that must be checked before proc macros expand

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs
  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/*.rs : For struct/enum/function multiline spans in UI tests, place error comments on the first line of the item declaration (e.g., `pub struct Name`), not on the `#[derive(...)]` attribute line

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Organize Rust lint crates with structure: `src/lib.rs` for implementation, `ui/` directory for UI test files with corresponding `.stderr` files

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `ui_test_examples` when tests need external dependencies (serde, utoipa) or have logically distinct scenarios with explicit test organization

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/*.stderr : Generate `.stderr` files by running tests (`cargo test --lib ui_examples`), copying normalized stderr output, and using `$DIR/` placeholder for file paths with exact line number matching

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/*.rs : Place test comment annotations (`// Should trigger DEXXX - description` or `// Should not trigger DEXXX - description`) on the line immediately before where the error is reported, not on attribute lines

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Document lint behavior in doc comments within the lint implementation

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/README.md
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/Cargo.toml : In `Cargo.toml` for lint crates, reference common workspace dependencies (`clippy_utils`, `dylint_linting`, `lint_utils`) using `.workspace = true` to avoid duplication

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/Cargo.toml : Register new lint crates by adding them to the `members` list in the workspace root `Cargo.toml`

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de09_gts_layer/de0901_gts_string_pattern/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `is_in_contract_module_ast()` helper from `lint_utils` crate to check if an AST item is in a contract/ directory

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `ui_test` when all tests have no external dependencies and you have many small, similar test cases for simpler configuration

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Use `cargo dylint new <lint_name>` in `dylint_lints/` directory to initialize new lint crates with proper scaffolding

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
  • dylint_lints/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `declare_pre_expansion_lint!` for lints that check derive attributes before macro expansion (e.g., detecting serde/utoipa derives)

Applied to files:

  • dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/Cargo.toml
  • dylint_lints/de02_api_layer/de0203_dtos_must_have_serde_derives/Cargo.toml
  • dylint_lints/de01_contract_layer/de0110_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de09_gts_layer/de0902_no_schema_for_on_gts_structs/Cargo.toml
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
  • dylint_lints/de02_api_layer/de0204_dtos_must_have_toschema_derive/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/main.rs : If generated `main.rs` and `main.stderr` are empty, remove them from the UI test directory

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.stderr
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/Cargo.toml : Define example targets in lint crate `Cargo.toml` with `[[example]]` sections for each UI test case

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.stderr
  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `EarlyLintPass` with `rustc_ast` for pre-expansion lints that need to see `#[derive(...)]` attributes directly before macro expansion

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `declare_early_lint!` for checking syntax and structure after macro expansion but before type resolution without type information

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/**/src/lib.rs : Use `declare_late_lint!` with `LateLintPass` and `rustc_hir` when type information or semantic analysis is required

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Verify tests pass with explicit `-p` flag to ensure correct package tests are running: `cargo test --lib -p <lint_name>`

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/Cargo.toml
📚 Learning: 2026-01-14T17:18:31.902Z
Learnt from: CR
Repo: hypernetix/hyperspot PR: 0
File: dylint_lints/AGENTS.md:0-0
Timestamp: 2026-01-14T17:18:31.902Z
Learning: Applies to dylint_lints/ui/*.stderr : Ensure `.stderr` files have comment annotations describing what triggers the lint and include `help:` and `note:` sections

Applied to files:

  • dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr
🧬 Code graph analysis (6)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs (16)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs (16)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs (1)
  • main (15-15)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs (16)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs (1)
  • main (15-15)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs (1)
dylint_lints/lint_utils/src/lib.rs (1)
  • is_in_api_rest_folder (24-26)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs (2)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.rs (16)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_camel_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_pascal_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_kebab_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_uppercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass.rs (1)
  • main (15-15)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_lowercase.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_snake_case.rs (1)
  • main (11-11)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_outside_api.rs (1)
  • main (11-11)
🪛 markdownlint-cli2 (0.18.1)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/README.md

3-3: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: UI macro tests (trybuild)
  • GitHub Check: Test Suite (ubuntu-latest, rust=stable)
  • GitHub Check: Test Suite (windows-latest, rust=stable)
  • GitHub Check: Test Suite (macos-latest, rust=stable)
  • GitHub Check: Dylint Lints Tests
  • GitHub Check: Code Coverage (cargo-llvm-cov)
  • GitHub Check: Run Dylint Lints

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@codecov-commenter
Copy link

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

… snake case

Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs`:
- Around line 122-134: check_type_rename_all currently only accepts the literal
"snake_case" (rejecting SCREAMING_SNAKE_CASE and UPPERCASE) whereas field-level
logic uses is_valid_case(); update check_type_rename_all to call
is_valid_case(value) instead of comparing value == "snake_case" so the
type-level rename_all validation accepts the same variants (snake_case,
SCREAMING_SNAKE_CASE, uppercase, numeric, and underscores) as is_valid_case;
ensure any error messages/reference still indicate invalid case when
is_valid_case returns false.

In
`@examples/plugin-modules/tenant_resolver/tenant_resolver-gw/src/api/rest/dto.rs`:
- Around line 208-209: The attribute on GetParentsResponseDto incorrectly
includes request; update the modkit_macros::api_dto invocation for the
GetParentsResponseDto struct from api_dto(request, response) to
api_dto(response) so it is only generated as a response DTO (matching
GetChildrenResponseDto) and prevents request-side usage at compile time.

In `@libs/modkit-macros/src/api_dto.rs`:
- Around line 10-20: Update the misleading comment and swap the trait-impl
variables so names match their implementations: change the comment on the
default branch to "Default: neither if neither specified" (or adjust logic
consistently) and swap the implementations so req_trait_impl produces an impl
::modkit::api::api_dto::RequestApiDto for `#name` and resp_trait_impl produces an
impl ::modkit::api::api_dto::ResponseApiDto for `#name`; keep the
serialize/deserialize variable usage (serialize -> ser/Response impl,
deserialize -> de/Request impl) consistent with those names.
🧹 Nitpick comments (4)
libs/modkit/src/api/mod.rs (1)

15-15: LGTM!

The new api_dto module correctly expands the public API surface to support macro-driven DTO generation. The module is properly placed alongside other API-related submodules.

Consider whether key types from api_dto (e.g., RequestApiDto, ResponseApiDto traits) should be re-exported in this file or in the prelude module for convenience, depending on how frequently downstream code needs to reference them directly.

modules/system/api_gateway/src/module.rs (1)

694-699: Consider adding Debug derive for consistency.

The previous implementation included Debug which was removed during the migration. While not strictly required for the current tests, adding Debug improves debuggability and maintains consistency with other test DTOs in this PR (e.g., TestData, LargePayload).

🔧 Suggested fix
-    #[derive(Clone)]
+    #[derive(Debug, Clone)]
     #[modkit_macros::api_dto(request, response)]
     struct UserEvent {
libs/modkit-macros/src/api_dto.rs (1)

21-27: serde attribute added unconditionally.

When neither request nor response is specified, #[serde(rename_all = "snake_case")] is still added but no serde derives are generated, making the attribute dead code.

Consider conditionalizing the serde attribute:

Proposed fix
+    let serde_attr = if serialize || deserialize {
+        quote! { #[serde(rename_all = "snake_case")] }
+    } else {
+        quote! {}
+    };
     quote! {
         #[derive(`#ser` `#de` utoipa::ToSchema)]
-        #[serde(rename_all = "snake_case")]
+        `#serde_attr`
         `#input`
         `#req_trait_impl`
         `#resp_trait_impl`
     }
modules/system/nodes_registry/src/api/rest/dto.rs (1)

36-37: Minor: Inconsistent attribute ordering on OsInfoDto.

OsInfoDto has the macro attribute before #[derive(...)], while all other DTOs in this file have #[derive(...)] first. This likely doesn't affect functionality, but consider reordering for consistency:

-#[modkit_macros::api_dto(request, response)]
 #[derive(Debug, Clone)]
+#[modkit_macros::api_dto(request, response)]
 pub struct OsInfoDto {

@mergify
Copy link

mergify bot commented Jan 21, 2026

🧪 CI Insights

Here's what we observed from your CI run for bef92d4.

🟢 All jobs passed!

But CI Insights is watching 👀

Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@dylint_lints/de02_api_layer/de0203_dtos_must_use_api_dto/README.md`:
- Around line 3-5: The heading level jumps from H1 to H3 for the section titled
"What it does"; change the "### What it does" heading to "## What it does" so it
is a proper H2 under the document's H1 and satisfies MD001 (update the heading
token for the "What it does" section).

In
`@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_screaming_snake_case.stderr`:
- Around line 1-2: The expected stderr span for the DTO serde-rename error is
off-by-one because a preceding comment shifted the serde attribute; update the
test expectation for pass_field_screaming_snake_case to point to the actual
serde attribute location (or simply re-bless the fixture) so the DE0803 message
matches the fixture's attribute span, ensuring the stderr UI file is updated
accordingly.

In
`@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_uppercase.stderr`:
- Around line 1-10: The test output file pass_field_uppercase.stderr currently
contains a DE0803 violation (serde rename uses non-snake_case) which makes this
a failing case; either remove the error output so the pass case truly has an
empty .stderr or move/rename the test to a fail_* case and update the test suite
listing accordingly; locate the DTO test that produced DE0803 (referenced by
pass_field_uppercase.stderr and the serde rename attribute) and either correct
the serde rename to snake_case or convert the test into a failing scenario and
adjust the UI test index so there remains at least one no-violation pass case
with an empty .stderr.
♻️ Duplicate comments (3)
examples/plugin-modules/tenant_resolver/tenant_resolver-gw/src/api/rest/dto.rs (1)

209-210: Consider response-only scope for GetParentsResponseDto.

If this type is never deserialized from requests, narrowing it to response-only keeps the surface tighter. Please verify usage before changing.

To verify usage:

#!/bin/bash
# Inspect all usages of GetParentsResponseDto to confirm it is only used as a response type.
rg -n --type=rust -C3 '\bGetParentsResponseDto\b'
modules/file_parser/src/api/rest/dto.rs (2)

81-99: Same casing verification as above for enum variants.


124-161: Same casing verification as above for enum variants.

🧹 Nitpick comments (4)
apps/gts-docs-validator/src/scanner.rs (1)

148-151: Optional: precompile false-positive regexes once.

Regex::new inside is_false_positive recompiles every call. Consider a static precompiled set (e.g., RegexSet + OnceLock or existing lazy helper) to avoid repeated compilation in large scans.

♻️ Example (adjust to your MSRV/deps)
+use std::sync::OnceLock;
+use regex::RegexSet;
@@
+static FALSE_POSITIVE_SET: OnceLock<RegexSet> = OnceLock::new();
+
 fn is_false_positive(gts_id: &str) -> bool {
-    for pattern in FALSE_POSITIVE_PATTERNS {
-        if let Ok(re) = Regex::new(pattern)
-            && re.is_match(gts_id) {
-                return true;
-            }
-    }
-    false
+    let set = FALSE_POSITIVE_SET.get_or_init(|| {
+        RegexSet::new(FALSE_POSITIVE_PATTERNS).expect("Invalid false-positive regex set")
+    });
+    set.is_match(gts_id)
 }
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/pass_field_screaming_snake_case.rs (1)

1-1: Consider renaming this fixture to fail_* for clarity.

The file name uses pass_ but the fixture is intended to trigger the lint (paired .stderr). Renaming to fail_field_screaming_snake_case.rs (and the .stderr file) would better match UI-test conventions and reduce confusion.

libs/modkit-macros/src/lib.rs (1)

1066-1071: Validate api_dto args to avoid silent typos.

Right now any ident is accepted, so a typo (e.g., reponse) quietly disables derives. Consider rejecting unknown args with a compile error.

💡 Suggested guard
 #[proc_macro_attribute]
 pub fn api_dto(attr: TokenStream, item: TokenStream) -> TokenStream {
     let attrs = parse_macro_input!(attr with Punctuated::<Ident, Token![,]>::parse_terminated);
+    for id in &attrs {
+        if id != "request" && id != "response" {
+            return syn::Error::new_spanned(
+                id,
+                "expected `request` and/or `response`",
+            )
+            .to_compile_error()
+            .into();
+        }
+    }
     let input = parse_macro_input!(item as DeriveInput);
     TokenStream::from(api_dto::expand_api_dto(&attrs, &input))
 }
dylint_lints/lint_utils/src/lib.rs (1)

90-150: Tighten api_dto path matching to avoid false positives.

Currently any attribute ending in api_dto suppresses lints, even from other crates. Consider requiring bare api_dto or a path containing modkit_macros, and reuse a small helper to avoid duplication in both functions.

♻️ Possible refactor
+fn is_api_dto_path(path: &rustc_ast::Path) -> bool {
+    let segments: Vec<&str> = path.segments.iter().map(|s| s.ident.name.as_str()).collect();
+    matches!(segments.as_slice(), ["api_dto"] | [.., "modkit_macros", "api_dto"])
+}
+
 pub fn has_api_dto_attribute(item: &rustc_ast::Item) -> bool {
     for attr in &item.attrs {
         // Check for modkit_macros::api_dto or just api_dto
         if let rustc_ast::AttrKind::Normal(attr_item) = &attr.kind {
-            let path = &attr_item.item.path;
-            let segments: Vec<&str> = path.segments.iter().map(|s| s.ident.name.as_str()).collect();
-
-            // Match: api_dto, modkit_macros::api_dto
-            if segments.last() == Some(&"api_dto") {
+            if is_api_dto_path(&attr_item.item.path) {
                 return true;
             }
         }
     }
     false
 }
@@
-        if let rustc_ast::AttrKind::Normal(attr_item) = &attr.kind {
-            let path = &attr_item.item.path;
-            let segments: Vec<&str> = path.segments.iter().map(|s| s.ident.name.as_str()).collect();
-
-            if segments.last() != Some(&"api_dto") {
+        if let rustc_ast::AttrKind::Normal(attr_item) = &attr.kind {
+            if !is_api_dto_path(&attr_item.item.path) {
                 continue;
             }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@libs/modkit-macros/src/api_dto.rs`:
- Around line 36-42: The macro emit unconditionally includes #[serde(rename_all
= "snake_case")] which breaks when no serde derives are emitted; update the
api_dto macro so the serde attribute is only inserted into the quote! output
when at least one of the serde trait implementations (req_trait_impl or
resp_trait_impl) is present/ non-empty — i.e., conditionally append the serde
attribute into the generated token stream alongside
req_trait_impl/resp_trait_impl (or guard around the entire attribute insertion)
so it only appears when Serialize or Deserialize is actually derived.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs`:
- Around line 92-95: Update the diagnostic texts to match the UI fixtures by
replacing occurrences of "invalid case" with "non-snake_case" in the diagnostic
messages emitted by this lint: change the string passed to
diag.primary_message(...) and adjust the accompanying diag.help(...) text where
it references the case wording; ensure you update all occurrences (including the
other block that sets primary_message and help around the second diagnostic and
any test helper references mentioned) so the emitted `.stderr` matches the
fixtures.
- Around line 38-42: The enum branch (ItemKind::Enum handling) currently calls
check_type_rename_all and then iterates variants calling
check_fields(&variant.data) but never validates variant-level serde renames; add
a call to validate each variant's attributes (e.g., invoke a helper like
check_variant_attrs or reuse the existing attribute-checking routine) before
calling check_fields so you run the snake_case check on &variant.attrs for each
variant in enum_def.variants, keeping the calls in the same loop and using the
existing cx context.
- Around line 52-78: find_serde_attribute_value currently only reads direct
value_str() forms and skips nested list forms like rename_all(serialize = "...",
deserialize = "...") and rename(serialize = "..."); update this function to
inspect nested meta_item_list entries for meta items that themselves contain a
list (e.g., rename_all(...) or rename(...)) and iterate those inner nested items
to extract their value_str() values (the serialize/deserialize entries) as well
as the outer direct value_str(); ensure you push (meta_item.span, value) for
each inner string value found so that both rename and the serialize/deserialize
variants are handled by find_serde_attribute_value.

In
`@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_screaming_snake_case.stderr`:
- Around line 1-10: Update the expected stderr output so the referenced source
file name is correct: replace the incorrect occurrence of
pass_field_screaming_snake_case.rs with fail_field_screaming_snake_case.rs in
the fail_field_screaming_snake_case.stderr content so the error header matches
the test case name and the UI test will compare against the correct expected
file reference.

In
`@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_field_uppercase.stderr`:
- Around line 1-10: The stderr snapshot incorrectly references the source file
name "pass_field_uppercase.rs" instead of the actual test source
"fail_field_uppercase.rs"; update the stderr content so the diagnostic header
shows "$DIR/fail_field_uppercase.rs" (the same diagnostic for the #[serde(rename
= "UPPERCASE_FIELD")] field) so the expected output matches the real test file
name.

In
`@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr`:
- Around line 1-4: Update the stderr fixture so the reported path matches the
test file: change the `$DIR/pass_screaming_snake_case.rs` reference to
`$DIR/fail_screaming_snake_case.rs` in
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_screaming_snake_case.stderr;
ensure the error line that references the attribute #[serde(rename_all =
"SCREAMING_SNAKE_CASE")] and the DE0803 lint text exactly match the
wording/format used in src/lib.rs so the UI output remains aligned with the lint
message.

@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from 10be5f1 to 661307e Compare January 21, 2026 14:25
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/src/lib.rs`:
- Around line 82-87: The line using "let Some(inner_meta_item) =
inner_nested.meta_item() && let Some(inner_value) = inner_meta_item.value_str()"
is invalid; replace it with nested conditional pattern matching (e.g., an outer
if let to extract inner_meta_item from inner_nested.meta_item() and an inner if
let to extract inner_value from inner_meta_item.value_str()) so that
results.push((inner_meta_item.span, inner_value.as_str().to_string())) only runs
when both are Some; update the block that iterates inner_list (the code handling
meta_item_list(), inner_nested, inner_meta_item, inner_value and results.push)
accordingly.
🧹 Nitpick comments (1)
dylint_lints/de08_rest_api_conventions/de0803_api_snake_case/ui/fail_enum_variant_rename_camel_case.rs (1)

6-6: Nit: Comment refers to "fields" but these are enum variants.

The comments mention "DTO fields" but the lint is checking enum variants here. Consider updating for consistency:

Suggested comment fix
-    // Should trigger DE0803 - DTO fields must not use non-snake_case in serde rename/rename_all
+    // Should trigger DE0803 - DTO enum variants must not use non-snake_case in serde rename/rename_all
     #[serde(rename = "firstVariant")]
     FirstVariant,
-    // Should trigger DE0803 - DTO fields must not use non-snake_case in serde rename/rename_all
+    // Should trigger DE0803 - DTO enum variants must not use non-snake_case in serde rename/rename_all
     #[serde(rename = "secondVariant")]

Also applies to: 9-9

Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch 3 times, most recently from d544d87 to 86f39b9 Compare January 21, 2026 20:43
@nanoandrew4 nanoandrew4 changed the title Add new lint to enforce snake-case usage with serde Add new lint to enforce snake-case usage with new api_dto macro Jan 21, 2026
@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from 86f39b9 to 308f35d Compare January 22, 2026 09:16
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@libs/modkit-macros/src/lib.rs`:
- Around line 1119-1123: The api_dto macro currently ignores unknown or
duplicate flag idents, so add validation in the api_dto function: parse the
attrs into idents as you already do, then iterate them to check against an
allowed set (e.g., "Serialize", "ResponseApiDto", etc.), detect duplicates, and
if any unknown or duplicate flags are found return a compile error by creating a
syn::Error (e.g., syn::Error::new_spanned) and returning
TokenStream::from(err.to_compile_error()). Update api_dto to perform this
validation before calling api_dto::expand_api_dto and pass validated flags (or
bail early with the compile error) so typos are reported precisely.

@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch 4 times, most recently from 32e5de1 to e2e7b31 Compare January 22, 2026 10:13
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In `@dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/README.md`:
- Around line 1-21: The top-level heading jumps from `#` to `###` in the README;
change the second heading from `### What it does` (and the subsequent `###`
headings if they represent top-level sections) to `## What it does` to follow
proper Markdown heading level order and ensure consistent document structure;
update any related section headings (e.g., `### Why is this bad?`, `### Known
problems`, `### Example`) to `##` only if they are meant to be the same level as
"What it does".

In `@dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/src/lib.rs`:
- Around line 9-60: Replace the use of is_in_contract_path with the AST-based
helper is_in_contract_module_ast in the EarlyLintPass impl for
De0104NoApiDtoInContract: in check_item (and any other places in this file) stop
calling is_in_contract_path(cx.sess().source_map(), item.span) and instead call
is_in_contract_module_ast(cx, item) (or the correct is_in_contract_module_ast
signature that accepts the lint context and the AST Item), so contract detection
uses the AST helper for structs/enums.

In
`@dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/ui/with_api_dto.rs`:
- Around line 4-23: The "Should trigger" annotation comments are currently
placed above the attribute lines for the API DTOs; move each comment so it
immediately precedes the item declaration line: place the "Should trigger DE0104
- api_dto in contract" comment directly above the `pub struct User { ... }`
declaration (not above `#[modkit_macros::api_dto(request, response)]`), directly
above the `pub struct Product { ... }` declaration (not above its attribute),
and directly above the `pub enum OrderStatus { ... }` declaration (not above its
attribute); keep the attribute macros (`#[modkit_macros::api_dto(...)]`) on the
line(s) above the declarations as they are.

In
`@dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/ui/without_api_dto.rs`:
- Around line 4-9: Move the test annotation comment so it immediately precedes
the item declaration instead of the attribute: take the "Should not trigger
DE0104 - api_dto in contract" comment currently above
#[modkit_macros::api_dto(request, response)] and place it directly above the
`pub struct UserDto { ... }` line (the item declaration) so the annotation sits
on the same line group as the item rather than the attribute.

@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from e2e7b31 to 44b6d8b Compare January 22, 2026 10:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dylint_lints/lint_utils/src/lib.rs`:
- Around line 122-186: The adds_snake_case_rename() method in ApiDtoArgs
currently always returns true, which contradicts the macro behavior that only
applies serde(rename_all = "snake_case") when request or response is present;
update adds_snake_case_rename() to return self.has_request || self.has_response;
also check get_api_dto_args() (which can produce ApiDtoArgs { has_request:false,
has_response:false }) and either add a validation step in the lint to treat the
empty/invalid attribute as absent/error or document that a false/false result
represents an invalid/empty attribute so callers know this case must be rejected
similarly to the macro.
🧹 Nitpick comments (1)
dylint_lints/lint_utils/src/lib.rs (1)

97-120: Consider narrowing api_dto attribute matching.
Matching any path ending in api_dto could skip lints for unrelated attributes from other crates. If this should only match unqualified api_dto or modkit_macros::api_dto, you can tighten the check.

♻️ Suggested tweak
-            if segments.last() == Some(&"api_dto") {
+            if segments.last() == Some(&"api_dto")
+                && (segments.len() == 1 || segments.contains(&"modkit_macros"))
+            {
                 return true;
             }

@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from 44b6d8b to 04d391a Compare January 22, 2026 10:40
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/Cargo.toml`:
- Line 4: The Cargo.toml contains a placeholder authors field ("authors =
[\"authors go here\"]"); either replace that placeholder with the real author
list (e.g., authors = ["Name <email>"]) or remove the authors line entirely (the
field is optional in modern Cargo) to resolve the placeholder value; update the
Cargo.toml's authors entry accordingly.
🧹 Nitpick comments (2)
dylint_lints/lint_utils/src/lib.rs (1)

97-208: Align api_dto arg parsing with macro validation.
get_api_dto_args currently ignores unknown/duplicate flags, so is_valid() can be true for attributes the macro would reject (e.g., #[api_dto(request, invalid)]). Consider tracking invalid flags and folding that into is_valid() (or returning None) to keep lint behavior in sync with the proc-macro.

♻️ Suggested adjustment
-use rustc_lint::LintContext;
+use rustc_lint::LintContext;
+use std::collections::HashSet;
 pub fn get_api_dto_args(item: &rustc_ast::Item) -> Option<ApiDtoArgs> {
@@
-            let mut has_request = false;
-            let mut has_response = false;
+            let mut has_request = false;
+            let mut has_response = false;
+            let mut has_invalid = false;
+            let mut seen = HashSet::new();
@@
-                    if let Some(ident) = arg.ident() {
-                        match ident.name.as_str() {
+                    if let Some(ident) = arg.ident() {
+                        let name = ident.name.as_str();
+                        if !seen.insert(name) {
+                            has_invalid = true;
+                        }
+                        match name {
                             "request" => has_request = true,
                             "response" => has_response = true,
-                            _ => {}
+                            _ => has_invalid = true,
                         }
                     }
                 }
             }
 
             return Some(ApiDtoArgs {
                 has_request,
                 has_response,
+                has_invalid,
             });
@@
 pub struct ApiDtoArgs {
     pub has_request: bool,
     pub has_response: bool,
+    pub has_invalid: bool,
 }
@@
     pub fn is_valid(&self) -> bool {
-        self.has_request || self.has_response
+        (self.has_request || self.has_response) && !self.has_invalid
     }
 }
dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/Cargo.toml (1)

22-22: Consider using workspace dependency for utoipa.

The utoipa dependency is defined in the workspace root Cargo.toml (version 5.4 with macros feature), but this lint crate has a hardcoded version instead of referencing the workspace dependency. Use utoipa.workspace = true for consistency and easier version management, or utoipa = { workspace = true, features = ["macros"] } if only the macros feature is needed.

@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from 04d391a to 89c8b5b Compare January 22, 2026 10:50
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@dylint_lints/lint_utils/src/lib.rs`:
- Around line 97-105: Update the doc comment for the
#[modkit_macros::api_dto(...)] attribute in lib.rs to correct the behavior:
change the line that currently states "`#[serde(rename_all = \"snake_case\")]
(always)`" to indicate that serde(rename_all = "snake_case") is applied only
when `request` or `response` is specified; ensure the comment mentions the
conditional nature and references the `api_dto` attribute and `serde(rename_all
= "snake_case")` so linters and readers understand it is not always applied.

In `@libs/modkit-macros/src/api_dto.rs`:
- Around line 53-72: The generated trait impls (resp_trait_impl and
req_trait_impl) omit the type generics and where-clauses for generic DTOs,
causing compilation failures for types like struct Foo<T>; update the impl
generation to include the input's generics and where-clause (use input.generics
and input.generics.where_clause) so the impls become impl#generics
::modkit::api::api_dto::ResponseApiDto for `#name`#generics `#where_clause` and
similarly for RequestApiDto, ensuring you interpolate the same generics when
referring to the type (`#name`#generics) and attach the where-clause if present.
🧹 Nitpick comments (3)
dylint_lints/de02_api_layer/de0203_dtos_must_use_api_dto/Cargo.toml (2)

5-5: Placeholder description not updated.

The description field still contains "description goes here". Consider updating it to describe what this lint enforces (e.g., "Lint to enforce api_dto macro usage on DTOs in the API layer").

Suggested fix
-description = "description goes here"
+description = "Lint to enforce api_dto macro usage on DTOs in the API layer"

12-14: Add an example for a passing test case.

Currently only the missing_api_dto example is defined, which tests the violation case (missing api_dto macro). Include a test case with correct usage of the api_dto macro to verify the lint produces no false positives on valid code, with a corresponding empty or near-empty .stderr file and an additional [[example]] entry in Cargo.toml.

dylint_lints/de01_contract_layer/de0104_no_api_dto_in_contract/ui/with_api_dto.stderr (1)

1-5: Prefer pointing the diagnostic span at the item declaration (not the attribute).

This would align UI tests with the guideline that annotations live on the item line, not the #[derive]/#[...] line. Update the lint span accordingly and refresh the .stderr expectations.

Based on learnings, ...

Also applies to: 10-15, 18-23

Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from 89c8b5b to 561dfd9 Compare January 22, 2026 11:10
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@dylint_lints/de01_contract_layer/de0101_no_serde_in_contract/.cargo/config.toml`:
- Around line 1-6: The config.toml currently places the linker key at top-level
and comments a [target.'cfg(all())'] block which is invalid; move the linker =
"dylint-link" entry into a literal target table such as
[target.x86_64-unknown-linux-gnu] (or whatever target triple you build for)
instead of top-level or [target.'cfg(all())'], ensuring the linker key lives
under that literal target table in config.toml so Cargo recognizes it.

In
`@dylint_lints/de01_contract_layer/de0102_no_toschema_in_contract/.cargo/config.toml`:
- Around line 1-6: The linker = "dylint-link" entry is at the root level but
must live under a target table; move that key into the [target.'cfg(all())']
section so the file contains a target.'cfg(all())' table with linker =
"dylint-link" (i.e., restore/enable the commented [target.'cfg(all())'] block
and place the linker setting inside it), referencing the linker key and the
[target.'cfg(all())'] table to locate where to change.

In
`@dylint_lints/de02_api_layer/de0202_dtos_not_referenced_outside_api/.cargo/config.toml`:
- Line 6: The top-level linker key is invalid; move the linker setting into a
[build] section so Cargo recognizes it (i.e., create a [build] header and place
linker = "dylint-link" under it), or alternatively use a [target.'cfg(...)']
section for conditional linkers; update the existing standalone linker entry
accordingly so the config uses [build] and the linker symbol is defined there.

In `@dylint_lints/de09_gts_layer/de0901_gts_string_pattern/.cargo/config.toml`:
- Around line 1-6: The top-level setting linker = "dylint-link" is invalid in
Cargo config and will be ignored; move this key into a proper section header
(for example add a [build] section or place it inside an existing target table
like [target.'cfg(all())']) so the linker directive is recognized; update the
config so the unique symbol linker = "dylint-link" is nested under a section
header (e.g., [build] or [target.'cfg(all())']) rather than at top level.
♻️ Duplicate comments (2)
libs/modkit-macros/src/api_dto.rs (1)

60-74: Handle generics in generated trait impls.

For generic DTOs (e.g., struct Foo<T>), the generated impl RequestApiDto for Foo {} / impl ResponseApiDto for Foo {} will not compile because generics and where-clauses are omitted.

Proposed fix
 pub fn expand_api_dto(args: &Punctuated<Ident, Token![,]>, input: &DeriveInput) -> TokenStream {
     if let Err(err) = validate_flags(args) {
         return err;
     }

     let has_request = args.iter().any(|id| id == "request");
     let has_response = args.iter().any(|id| id == "response");

     if !has_request && !has_response {
         return quote! {
             compile_error!("api_dto macro requires at least one of 'request' or 'response' arguments");
         };
     }

     let (serialize, deserialize) = (has_response, has_request);
     let name = &input.ident;
+    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
     let ser = if serialize {
         quote! { ::serde::Serialize, }
     } else {
         quote! {}
     };
     let resp_trait_impl = if serialize {
-        quote! { impl ::modkit::api::api_dto::ResponseApiDto for `#name` {} }
+        quote! { impl `#impl_generics` ::modkit::api::api_dto::ResponseApiDto for `#name` `#ty_generics` `#where_clause` {} }
     } else {
         quote! {}
     };
     let de = if deserialize {
         quote! { ::serde::Deserialize, }
     } else {
         quote! {}
     };
     let req_trait_impl = if deserialize {
-        quote! { impl ::modkit::api::api_dto::RequestApiDto for `#name` {} }
+        quote! { impl `#impl_generics` ::modkit::api::api_dto::RequestApiDto for `#name` `#ty_generics` `#where_clause` {} }
     } else {
         quote! {}
     };
dylint_lints/de01_contract_layer/de0103_no_http_types_in_contract/.cargo/config.toml (1)

1-6: Same config pattern as other lint crates.

This follows the same global linker configuration pattern. The same verification note about potentially needing a [build] section header applies here.

🧹 Nitpick comments (2)
modules/system/nodes_registry/nodes_registry/src/api/rest/dto.rs (1)

1-1: Minor: Inconsistent Uuid usage.

Line 1 imports Uuid, but lines 23 and 85 use the fully qualified uuid::Uuid. Consider using the imported Uuid consistently throughout for cleaner code.

Suggested fix
-    pub node_id: uuid::Uuid,
+    pub node_id: Uuid,

Also applies to: 6-6, 23-23, 85-85

libs/modkit-macros/src/api_dto.rs (1)

83-89: Use fully qualified path for utoipa::ToSchema for consistency.

The serde traits use fully qualified paths (::serde::Serialize, ::serde::Deserialize), but utoipa::ToSchema does not. For consistency and to avoid potential shadowing issues in user code, consider using ::utoipa::ToSchema.

Proposed fix
     quote! {
-        #[derive(`#ser` `#de` utoipa::ToSchema)]
+        #[derive(`#ser` `#de` ::utoipa::ToSchema)]
         `#serde_attr`
         `#input`
         `#req_trait_impl`
         `#resp_trait_impl`
     }

@nanoandrew4 nanoandrew4 requested a review from Bechma January 23, 2026 08:39
Signed-off-by: nanoandrew4 <andressmithdev@pm.me>
@nanoandrew4 nanoandrew4 force-pushed the snake-case-enforcement branch from a29e99b to ca12fa2 Compare January 23, 2026 10:49
}

// Must not have consecutive underscores
if s.contains("__") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like edge cases, but why are we denying __ and _hello_?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never seen __ (double spacing) used in a request/response, if it shows up it is likely a typo IMO. A field that starts with a leading underscore is usually private/internal, and should probably not be part of a DTO. They just enforce a more predictable convention, but if we do have valid use cases for variables starting with an underscore, I can do away with that validation

@nanoandrew4 nanoandrew4 requested a review from Bechma January 23, 2026 14:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants