feat(c4): add @c4 code level (struct/enum/trait/fn) + code diagram - #9
Open
nightscape wants to merge 2 commits into
Open
nightscape wants to merge 2 commits into
nightscape wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for curated code-level (@c4 code) elements to the architecture IR and emits a dedicated PlantUML “code diagram” for them.
Changes:
- Introduces
CodeElementin module docs and IR; overlays extracted elements into the directory tree and merge logic. - Extends the Rust adapter to parse
@c4 codeelements (struct/enum/trait/fn) from item docs, including@c4 usesrelationships. - Adds
generate_codeto emitc4-code.pumland wires it into the CLI render flow.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| core/archidoc-types/src/module_doc.rs | Adds module-doc CodeElement and exposes it on ModuleDoc. |
| core/archidoc-types/src/lib.rs | Re-exports CodeElement at the crate root. |
| core/archidoc-types/src/ir.rs | Adds IR-level code_elements on DirNode and defines an IR CodeElement. |
| core/archidoc-engine/src/tree.rs | Initializes code_elements when building the dir tree. |
| core/archidoc-engine/src/plantuml.rs | Implements generate_code to emit c4-code.puml. |
| core/archidoc-engine/src/merge.rs | Merges code_elements alongside other dir fields. |
| core/archidoc-engine/src/ir_builder.rs | Overlays ModuleDoc.code_elements into IR DirNode.code_elements. |
| core/archidoc-cli/src/main.rs | Calls generate_code during PlantUML rendering. |
| adapters/archidoc-rust/src/walker.rs | Extracts code elements across sources (special-casing entry modules). |
| adapters/archidoc-rust/src/parser.rs | Parses @c4 code elements using syn and adds unit test coverage. |
| adapters/archidoc-rust/src/cargo_modules.rs | Updates test fixtures to include code_elements. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub use ir::{ArchitectureIR, C4Level, DirNode, FileNode}; | ||
| pub use scaffold_ir::{ScaffoldIR, ScaffoldNode, ScaffoldPostHook, ScaffoldTemplate, ScaffoldVariable}; | ||
| pub use module_doc::{FileEntry, ModuleDoc, Relationship}; | ||
| pub use module_doc::{CodeElement, FileEntry, ModuleDoc, Relationship}; |
Comment on lines
+145
to
+156
| // Map a code element's bare name to its qualified puml id so that | ||
| // intra-component `@c4 uses StorageEntity` arrows land on the defined node | ||
| // instead of auto-creating a bare one. | ||
| let mut by_name: BTreeMap<&str, String> = BTreeMap::new(); | ||
| for owner in &owners { | ||
| for el in &owner.code_elements { | ||
| by_name.insert( | ||
| el.name.as_str(), | ||
| to_puml_id(&format!("{}__{}", owner.path, el.name)), | ||
| ); | ||
| } | ||
| } |
Comment on lines
+169
to
+182
| boundary_defs.push_str(&format!( | ||
| " Component({}, \"{}\", \"{}\", \"{}\")\n", | ||
| id, el.name, el.kind, desc | ||
| )); | ||
| for rel in &el.relationships { | ||
| let to_id = by_name | ||
| .get(rel.target.as_str()) | ||
| .cloned() | ||
| .unwrap_or_else(|| to_puml_id(&rel.target)); | ||
| rel_defs.push_str(&format!( | ||
| "Rel({}, {}, \"{}\", \"{}\")\n", | ||
| id, to_id, rel.label, rel.protocol | ||
| )); | ||
| } |
Comment on lines
203
to
205
| fn to_puml_id(s: &str) -> String { | ||
| s.replace('.', "_").replace('/', "_").replace('-', "_") | ||
| } |
Comment on lines
+79
to
+87
| let code_elements = if is_standard_entry { | ||
| let dir = path.parent().unwrap_or(path); | ||
| read_rs_sources(dir) | ||
| .iter() | ||
| .flat_map(|(_, src)| parser::extract_code_elements(src)) | ||
| .collect() | ||
| } else { | ||
| parser::extract_code_elements(&fs::read_to_string(path).unwrap_or_default()) | ||
| }; |
Comment on lines
+153
to
+157
| description: if c.description.is_empty() || c.description == "*No description*" { | ||
| None | ||
| } else { | ||
| Some(c.description.clone()) | ||
| }, |
Completes the C4 model downward. Item-level `@c4 code` doc markers turn a curated set of load-bearing types into code-level nodes under their component — the rust adapter previously parsed only module `//!` docs. - archidoc-types: CodeElement on ModuleDoc + ir::CodeElement on DirNode (serde-default; existing IR JSON stays valid). - archidoc-rust/parser: extract_code_elements() uses syn to find struct/enum/trait/fn items whose doc contains `@c4 code`; captures kind, description, and `@c4 uses` relationships. Unmarked items are ignored (curated, not a dump). - archidoc-rust/walker: aggregates a module's code elements from its directory's source files onto the component's ModuleDoc. - archidoc-engine: ir_builder propagates code_elements; plantuml generate_code() renders c4-code.puml (Component per element, kind as the tech tag, intra-component `@c4 uses` resolved to qualified ids). Emitted only when a code element exists. - Tests: extract_code_elements curation + typing + relationships. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
nightscape
force-pushed
the
feat/c4-code-level
branch
from
June 14, 2026 12:02
2c24e9c to
3d903a4
Compare
…iagram
Auto-detects `impl Trait for Type` blocks and renders an "implements" arrow
from the type to the trait in the `@c4 code` diagram — so the diagram shows
not just which types and traits exist, but which concrete types realize
which traits.
To keep the diagram a curated set, an arrow is drawn only when BOTH ends are
`@c4 code` elements. The walker already filters collected impls to those
whose implementing type is a declared code element (so the IR never carries
Debug/Clone/Serialize noise), and the renderer draws the arrow only when the
trait is a code element too.
- archidoc-types: `TraitImpl { type_name, trait_name }` + `trait_impls` on
ModuleDoc and DirNode (serde-default, skip-if-empty).
- archidoc-rust/parser: `extract_trait_impls()` via syn (last path segment of
trait + self type; inherent impls skipped).
- archidoc-rust/walker: collects impls, keeps only those of @C4 code types.
- archidoc-engine: ir_builder + merge propagate the field; plantuml::generate_code
emits `Rel(type, trait, "implements", "trait")` when both are @C4 code.
- Tests: parser realization extraction + both-are-code render gating.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds the bottom of the C4 model. Item-level
@c4 codedoc markers onstruct/enum/trait/fnbecome curated code-level nodes under their component. The rust adapter previously parsed only module//!docs.archidoc-types:CodeElementonModuleDoc+ir::CodeElementonDirNode(bothserde-default, so existing IR JSON stays valid).archidoc-rust/parser:extract_code_elements()usessynto find items whose doc contains@c4 code; captures kind, description, and@c4 usesrelationships. Unmarked items are ignored — the diagram is a curated set of load-bearing types, not a dump of every symbol.archidoc-rust/walker: aggregates a module's code elements from its directory's source files onto the component'sModuleDoc.archidoc-engine:ir_builderpropagatescode_elements;plantuml::generate_code()rendersc4-code.puml(oneComponent(...)per element, kind as the tech tag, intra-component@c4 usesresolved to qualified ids). Emitted only when a code element exists.Example
→
c4-code.pumlwithFileFormatAdapter+StorageEntityas components and an arrow between them;InternalHelperis omitted.Notes
@c4 codeitems are collected per component directory (the walker aggregates a module's source files). Items in deeper un-annotated subdirectories are not yet picked up — a reasonable follow-up. Test added covering curation, typing, and relationships.🤖 Generated with Claude Code