diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 378df646b..13bd0d8ef 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -16,3 +16,59 @@ This repository is built on alloy which use JSX to define components. This is NO - DO NOT destructure props in the component definition Do not update changelogs, these are managed by `npx chronus`. + +## TypeScript/Symbol Patterns - Known Gotchas + +- For exported components that attach subcomponents (for example, `Component.Call = ...`), avoid private type aliases in public exported const annotations; API Extractor can fail. Prefer `export function Component(...) { ... }` and attach subcomponents as properties after declaration. + **Static memberSpaces in Symbol subclasses:** When subclassing symbols with custom `memberSpaces`, declare static `memberSpaces` as `readonly string[]` in both base and subclass. TypeScript tuple widening/narrowing on the static side causes incompatibility errors without explicit readonly string[] typing. Always validate symbol changes with: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` (`pnpm --filter @alloy-js/rust build` runs `alloy build --with-dev` and `generate-docs`, so it also checks API surface generation). + **For SourceFile/CrateDirectory loop changes:** run `pnpm --filter @alloy-js/rust exec vitest run test/source-file-crate-directory.test.tsx` first for fast debugging, then run full `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test`. + **For TypeAlias/ConstDeclaration loops:** run `pnpm --filter @alloy-js/rust exec vitest run test/type-alias-const.test.tsx` first, then run `pnpm --filter @alloy-js/rust test`. + **For import/reference integration loops:** run `pnpm --filter @alloy-js/rust exec vitest run test/imports.test.tsx test/reference.test.tsx` before full-suite validation to iterate faster. + **For Cargo.toml generation loops:** keep `CrateDirectory` Cargo.toml emission opt-in (`includeCargoToml`) so existing single-file `toRenderTo()` tests keep stable output. +- For `CargoTomlFile` targets, emit explicit sections (`[lib] path = "lib.rs"` for libs, `[[bin]]` with `name`/`path` for bins) before `[dependencies]`; expect snapshot updates in cargo-toml/golden/stc tests. + **Validation workflow tip:** use `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` as the Rust package correctness gate; also run `pnpm --filter rust-example build` when touching sample crate exports, and fix TS2742 by replacing inferred exported `createCrate(...)` values with explicit descriptor constants (`as const`) plus exported type aliases. +- In components using `scope.enclosingModule`, narrow to `RustModuleScope` (for example, `instanceof RustModuleScope`) before accessing `.types` or `.values`; `enclosingModule` is typed as `RustScopeBase`. +- Avoid whitespace-only `code` template literals (for example, ``code` ` ``); they can crash core code rendering. Use plain string literals like `" "` for standalone spaces. +- For `ModuleDirectory`, derive the module name from the last `path` segment before calling `addChildModule`, so nested paths register the correct child module. +- For exported Rust APIs, avoid exposing private helper types through symbol-keyed fields on exported interfaces; API Extractor treats them as public and fails build with `ae-forgotten-export`. +- For public builtins exports (for example `export const std = createCrate(...)` in `packages/rust/src/builtins/std.ts`), avoid relying on inferred/private descriptor shapes; this can trigger TS2742/API Extractor portability errors. Prefer explicit exported type aliases/interfaces for the export surface, then validate with `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test`. +- In `packages/rust/src/components/stc/index.ts`, prefer explicit named component imports (alias if needed) and `stc(ComponentAlias)` for exported wrappers; avoid `import * as base` + `stc(base.X)`, which can trigger API Extractor `ae-forgotten-export` private type leakage. +- `Parameters` rendering requires `RustFunctionScope`; when testing `Parameters`/`Stc.Parameters`, render within a `FunctionDeclaration` instead of at module/root scope to avoid `Can't create parameter symbol outside of a function scope.` +- For enum/struct sibling rendering, always insert explicit `` separators between repeated children (variants/fields) and between doc comments and declaration attributes; debug quickly with `pnpm --filter @alloy-js/rust exec vitest run test/enum.test.tsx test/struct.test.tsx`, then confirm with full `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test`. +- For braced expression components that use `` (for example `StructExpression`), do not add a leading `` right after `" {"`; it introduces an extra blank line before the first indented entry. +- In `TraitDeclaration`, a self-closing `FunctionDeclaration` is an abstract signature and must render with `;` (not `{}`); a trait method with children is a default implementation and renders with braces. For fast regression checks, run `pnpm --filter @alloy-js/rust exec vitest run test/function.test.tsx test/reference.test.tsx`. +- In `SourceFile`/module registration flows, standalone non-root files should self-register with parent module declarations, but module-root files (`lib.rs`, `main.rs`, `mod.rs`) must never self-register. +- In `ImplBlock`/`TraitDeclaration`, `FunctionDeclaration` defaults receiver to `&self`; set `receiver="none"` for associated functions like `new()`. +- For `ModDeclarations` render-order regressions, first run `pnpm --filter @alloy-js/rust exec vitest run test/t045-render-order.test.tsx test/mod-declarations.test.tsx`, then run full rust validation. +- For Rust generic rendering, keep lifetime parameters and type parameters in the same `TypeParameterProp[]`, but always render lifetimes first (`<'a, 'b, T>`), and quickly verify with `pnpm --filter @alloy-js/rust exec vitest run test/type-parameters.test.tsx test/lifetime-parameters.test.tsx`. +- For `ImplBlock` generic forwarding, ensure `StructDeclaration`/`EnumDeclaration` register named type parameter symbols on `NamedTypeSymbol.typeParameters` whenever `typeParameters` are provided. +- For trait-import tests that depend on `Reference` auto-use behavior, avoid Rust prelude traits (for example `Clone`) because prelude symbols intentionally skip `use` generation; assert imports with non-prelude traits like `std::fmt::Display`. +- `LetBinding` intentionally does **not** register symbols in scope (T049 exclusions); treat it as syntax-only statement rendering with optional type/initializer and a trailing `;`. +- In `FunctionCallExpression`-style components using `` + `Wrap` (`when={args.length > 1}`), expect stable multiline output for multi-arg calls, and turbofish type-arg lists may also line-break under the same group; snapshot tests should assert multiline form. +- For components using ``, line breaks occur only after print-width overflow; include at least one long argument in multi-arg tests when asserting multiline wrapping. +- Ensure `DocComment`/`ModuleDocComment` end with a trailing line break before declarations; otherwise generated output can concatenate comment text and the next declaration. + +Critical rules: + +1. Do not invent architecture. Ground every important claim in actual repository code, file structure, symbols, or tests. +2. Prefer concrete evidence over broad summaries. +3. Distinguish clearly between: + - observed facts from the repo + - inferences from patterns + - proposals for the new language package +4. When something is unclear, say so explicitly under "Open Questions" or "Ambiguities". +5. Do not start implementing code unless explicitly asked. This phase is documentation and planning only. +6. Write output directly to the requested markdown file. +7. Use clear headings and bullet points where useful. +8. When referencing repository files, include repository-relative paths. +9. Focus only on the parts of the repo relevant to Alloy core and existing language packages. +10. Optimize for future AI coding agents that will consume these documents. +11. For symbol-model changes (like Rust T003), always validate with `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` before moving to dependent tasks. + +Quality bar: + +- Precise +- Evidence-based +- Dependency-aware +- Implementation-oriented +- Not generic diff --git a/.github/prompts/001-discovery.md b/.github/prompts/001-discovery.md new file mode 100644 index 000000000..940902b1c --- /dev/null +++ b/.github/prompts/001-discovery.md @@ -0,0 +1,64 @@ +You are working inside the Alloy monorepo. + +Your goal is to identify the minimum set of repository files required to understand: + +1. Alloy core +2. How language packages extend Alloy +3. The current implementations for TypeScript, Java, Python, and C# +4. The testing patterns used by existing language packages +5. Any contributor docs, architecture docs, package READMEs, or examples relevant to adding a new language package + +Do not write a full architecture summary yet. +Do not propose a design for a target language yet. + +Instead, produce a discovery document at: + +docs/00-relevant-files.md + +The document must contain: + +# Purpose + +A short explanation of why these files matter for planning a new language package. + +# Core Files + +A list of the key files/directories needed to understand Alloy core. +For each item include: + +- path +- why it matters +- what concept it likely covers + +# Existing Language Package Files + +For TypeScript, Java, Python, and C#, list the most important files/directories. +For each item include: + +- path +- package/language +- why it matters +- likely role in the package + +# Tests and Examples + +List the most relevant test/example files and why they matter. + +# Documentation + +List repo docs that appear important. + +# Recommended Reading Order + +Provide a practical reading order for the next analysis passes. + +# Exclusions + +List files/directories that appear large but likely irrelevant for this task. + +Constraints: + +- Be selective. Prefer quality over exhaustiveness. +- Ground everything in the repo. +- Do not guess beyond what the repo supports. +- Keep the document concise but useful. diff --git a/.github/prompts/002-core-understanding.md b/.github/prompts/002-core-understanding.md new file mode 100644 index 000000000..3c3fb6095 --- /dev/null +++ b/.github/prompts/002-core-understanding.md @@ -0,0 +1,99 @@ +You are working inside the Alloy monorepo. + +Your task is to understand Alloy core deeply enough that a later agent can design a new language package without misplacing responsibilities between core and language-specific libraries. + +Write the output to: + + + +Do not design yet. +Do not describe implementation tasks yet. +This document must be primarily descriptive, not prescriptive. + +Study the repository and produce a markdown document with the following exact sections: + +# 1. Objective + +Explain the purpose of this document and how it will be used later. + +# 2. What Alloy Appears to Be + +Describe Alloy's role as a code generation framework based on repository evidence. +Summarize the apparent generation model in plain language. + +# 3. Core Architectural Model + +Explain the core architecture of Alloy. +Cover the major abstractions and how they appear to work together. + +At minimum analyze: + +- rendering model +- component/composition model +- context/providers/hooks if present +- document/file generation model +- symbol/reference model +- scope and name resolution model +- formatting/printing model +- extension/plugin surfaces for language packages + +# 4. Key Core Concepts + +Create a glossary of the most important concepts in Alloy core. +For each concept include: + +- name +- what it means +- why it matters +- key files where it is defined or exercised + +# 5. Core-to-Language Extension Surface + +Describe exactly how language packages appear to build on core. +Identify the likely contracts, APIs, primitives, or patterns that language packages rely on. + +Separate: + +- clearly observed extension points +- inferred extension patterns + +# 6. Source File Inventory + +List the most important core files and directories. +For each include: + +- path +- purpose +- notable exported types/functions/components/classes +- why it is relevant to new language support + +# 7. Invariants and Constraints + +Document the important invariants or architectural constraints that later work must respect. +Examples: + +- what core appears to own +- what language packages appear to own +- assumptions about rendering +- assumptions about symbol handling +- assumptions about output structure + +# 8. Testing Patterns in Core + +Describe how core behavior is tested and what that implies for future language packages. + +# 9. Ambiguities / Open Questions + +List anything that is unclear, inconsistent, or underdocumented in the repo. + +# 10. Key Takeaways for Future Language Package Design + +Summarize the most important lessons from core that a planner must not miss. + +Requirements: + +- Ground important claims in repository-relative file paths. +- Be concrete. +- Separate fact from inference. +- Do not propose new abstractions unless necessary to explain an observed gap. +- Optimize the document for later AI agents that will design and implement . diff --git a/.github/prompts/003-compare-language-packages.md b/.github/prompts/003-compare-language-packages.md new file mode 100644 index 000000000..be8570bb8 --- /dev/null +++ b/.github/prompts/003-compare-language-packages.md @@ -0,0 +1,105 @@ +You are working inside the Alloy monorepo. + +Your task is to compare the existing Alloy language packages so that a future agent can identify the stable patterns for implementing a new package. + +Study the repository and write the output to: + + + +Use the previously generated core understanding document as context: + + +The purpose of this document is not to restate core, but to extract reusable patterns and package-shape expectations from the existing language packages. + +Analyze the existing language packages for: + +- TypeScript +- Java +- Python +- C# + +Produce a markdown document with the following exact sections: + +# 1. Objective + +Explain how this comparison will inform the design of support. + +# 2. Packages Compared + +List the package paths examined for TypeScript, Java, Python, and C#. + +# 3. Common Package Anatomy + +Describe the recurring structure shared by the existing language packages. +Examples may include: + +- package layout +- public API surface +- component organization +- symbol/reference helpers +- file/module abstractions +- declaration and expression modeling +- formatting utilities +- tests and examples + +# 4. Cross-Language Concept Matrix + +Create a detailed comparison matrix covering at least: + +- source file / module representation +- imports / usings / package declarations / namespaces +- identifiers and symbol references +- literals +- expressions +- statements +- declarations +- type references +- functions / methods +- classes / interfaces / structs / equivalents +- visibility/modifiers/annotations/decorators where applicable +- comments / doc comments +- formatting / whitespace / delimiters +- test strategy + +For each concept, explain: + +- how TypeScript handles it +- how Java handles it +- how Python handles it +- how C# handles it +- what seems to be the shared principle +- what varies by language + +# 5. Reusable Patterns + +List the architectural and implementation patterns that appear reusable for any new language package. + +# 6. Divergent or Language-Specific Patterns + +List the parts that vary significantly and should not be over-generalized. + +# 7. Implied Minimum Viable Surface for a New Language Package + +Based on the existing packages, infer the likely minimum set of capabilities a new language package should provide. + +This section should be an inferred checklist, not yet a target-language proposal. + +# 8. Testing Patterns Across Language Packages + +Describe how the existing language packages test themselves and what a new package should likely emulate. + +# 9. Gaps, Inconsistencies, and Risks + +Identify inconsistencies between existing packages or any uncertainty that could affect planning for . + +# 10. Takeaways for Designing + +Summarize the most important reusable lessons. + +Requirements: + +- Ground all major claims in actual repository evidence. +- Use repository-relative paths. +- Distinguish observed package behavior from inferred patterns. +- Do not design yet except where explicitly asked in section 10 to state takeaways. +- Optimize for future planning and implementation agents. diff --git a/.github/prompts/004-target-language-design-notes.md b/.github/prompts/004-target-language-design-notes.md new file mode 100644 index 000000000..1ae544fe1 --- /dev/null +++ b/.github/prompts/004-target-language-design-notes.md @@ -0,0 +1,114 @@ +You are working inside the Alloy monorepo. + +Your task is to design the implementation approach for a new Alloy language package for , using: + +1. repository evidence from Alloy core +2. patterns from the existing language packages +3. the real constraints and idioms of + +Write the output to: + + + +Use these existing planning documents as required context: + +- +- + +This document is a design brief, not a backlog and not implementation code. +It must define a realistic and scoped plan for support. + +Produce a markdown document with the following exact sections: + +# 1. Objective + +State the purpose of the design notes and how they will feed the PRD. + +# 2. Target Language Constraints + +Describe the core code-organization and syntax constraints of that matter for Alloy package design. + +Cover the language features that are relevant to code generation, such as: + +- file/module/package/namespace model +- import/include/use semantics +- declaration forms +- expression and statement model +- type system concerns +- comments/doc comments +- formatting conventions +- visibility/modifier rules +- special constraints or idioms + +# 3. Mapping Alloy Concepts to + +Map the Alloy concepts from core and the cross-language patterns to the constructs of . + +For each major concept, explain: + +- whether it maps cleanly +- whether it needs adaptation +- whether it suggests a package-specific abstraction +- any risks or limitations + +# 4. Proposed Package Shape + +Propose the likely package architecture for alloy- within the monorepo. + +Include: + +- likely directories/modules +- likely public API shape +- major internal areas of responsibility +- relationship to Alloy core +- any helper layers needed + +# 5. MVP Scope + +Define the recommended MVP for support. + +This should be explicit and limited. +Identify the minimum supported capabilities needed for a useful first version. + +Be concrete about what is in scope, for example: + +- file/module generation +- imports/includes +- identifiers and references +- primitive types +- basic declarations +- functions +- classes/structs/interfaces/enums as relevant +- basic expressions/statements +- comments +- formatting +- tests + +# 6. Deferred / Out-of-Scope Features + +List advanced features that should be deferred beyond MVP. + +# 7. Golden Scenarios + +Define a small set of example outputs that the package should be able to generate by the end of MVP. +These should be realistic and useful for testing. + +# 8. Risks and Open Design Questions + +List the biggest design uncertainties, repo ambiguities, or target-language-specific risks. + +# 9. Recommendation + +Provide a concise recommendation for how Alloy should support in MVP form. + +Requirements: + +- Ground the design in the repo and existing package patterns. +- Make explicit where you are inferring or proposing. +- Do not create a task backlog yet. +- Do not hand-wave target-language semantics; be precise. +- Optimize the result so a later planning agent can directly convert it into a PRD. + +``` + +``` diff --git a/.github/prompts/005-prd-generation.md b/.github/prompts/005-prd-generation.md new file mode 100644 index 000000000..f64381edb --- /dev/null +++ b/.github/prompts/005-prd-generation.md @@ -0,0 +1,125 @@ +You are working inside the Alloy monorepo. + +Your task is to produce a detailed, execution-ready PRD for adding language support to Alloy. + +Write the output to: + + + +Use the following planning inputs as required context: + +- +- +- + +This PRD will be consumed by AI coding agents and human reviewers. +It must be implementation-oriented, precise, scoped, and testable. + +Produce a markdown document with the following exact sections: + +# 1. Title + +A clear title for the initiative. + +# 2. Objective + +State the goal of adding support to Alloy. + +# 3. Background and Context + +Summarize the relevant context from Alloy core and existing language packages. +Do not repeat all details; include only what materially affects this initiative. + +# 4. Problem Statement + +Explain what problem this new package solves and why the work matters. + +# 5. Goals + +List the concrete goals for the initiative. + +# 6. Non-Goals + +List what is explicitly out of scope for this effort. + +# 7. Users / Stakeholders + +Identify the likely users and maintainers of the package. + +# 8. Scope of MVP + +Define the MVP scope in precise implementation terms. + +# 9. Functional Requirements + +Provide detailed functional requirements for the new language package. + +These should cover, where applicable: + +- package scaffolding and public API +- file/module/package/namespace rendering +- import/include/use handling +- identifiers and symbol/reference rendering +- primitive and basic type representation +- declarations +- functions/methods +- classes/structs/interfaces/enums or relevant equivalents +- basic expressions/statements +- comments/doc comments +- formatting behavior +- examples and developer ergonomics +- tests and golden outputs + +Each requirement should be specific and testable. + +# 10. Non-Functional Requirements + +Define relevant quality requirements, such as: + +- consistency with Alloy architecture +- maintainability +- clarity of public API +- testability +- incremental extensibility +- predictable rendering behavior + +# 11. Proposed Package/Module Architecture + +Describe the recommended package structure in the monorepo and the major modules/components to implement. + +# 12. Milestones / Phases + +Break the work into phases from foundation to MVP completion. + +# 13. Acceptance Criteria + +Define clear criteria that must be true for MVP to be considered complete. + +# 14. Test Strategy + +Describe how this package should be tested, including: + +- unit tests +- golden/file-output tests +- scenario coverage +- regression coverage + +# 15. Risks and Mitigations + +List the main implementation risks and how to mitigate them. + +# 16. Open Questions + +List unresolved questions that should be answered before or during implementation. + +# 17. Recommended Next Step + +State the immediate next action after this PRD is approved. + +Requirements: + +- Optimize for execution by autonomous coding agents. +- Be concrete, not aspirational. +- Avoid vague requirements. +- Keep dependencies and implementation order in mind. +- Ground the PRD in the prior analysis docs. diff --git a/.github/prompts/006-backlog-generation.md b/.github/prompts/006-backlog-generation.md new file mode 100644 index 000000000..1b5b180af --- /dev/null +++ b/.github/prompts/006-backlog-generation.md @@ -0,0 +1,345 @@ +You are a senior technical product planner and software architect. + +You are working inside the Alloy monorepo. + +Your task is to generate a complete, execution-ready project backlog from the Rust language package PRD and documentation @docs/language-packages/rust + +The backlog will be consumed primarily by AI coding agents, so it must be: + +- precise +- implementation-oriented +- dependency-aware +- incrementally executable +- testable +- safe for autonomous execution + +Write the output to: + + + +Use the following planning inputs as required context: + +- +- +- +- + +These documents may include: + +- PRDs +- architecture documents +- non-functional requirements +- data model notes +- API design notes +- state machine docs +- operational constraints +- implementation notes + +If some files are clearly obsolete, contradictory, or overlapping: + +- call that out explicitly +- make the most reasonable synthesis possible +- do not silently ignore meaningful conflicts + +# Objective + +Produce a backlog that translates the product and architecture into a practical development plan from foundation to production-ready system. + +The output must be suitable for: + +- humans who need a complete view of scope, sequencing, and risks +- AI coding agents that execute one task at a time with limited context + +--- + +## What you must do + +1. Read and synthesize the documents under `docs/language-packages/rust`. +2. Identify the full scope of work needed to build the system. +3. Break the work into: + - epics + - features/capabilities + - implementation tasks +4. Order the backlog by dependencies and implementation readiness. +5. Separate true product work from enabling/foundation work. +6. Explicitly account for: + - infrastructure/platform setup + - testing + - documentation +7. Include tasks needed for integration between subsystems, not just isolated components. +8. Identify assumptions, missing information, or decisions that block implementation. +9. Prefer vertical slices where possible, but include foundational platform work where necessary. +10. Optimize for AI coding agents that will execute one task at a time with limited context. + +## Backlog design rules + +Each task must be: + +- small enough for one coding agent to complete in one focused work session +- large enough to produce meaningful progress +- independently understandable +- explicitly scoped +- verifiable by tests, review, or observable outcome + +Avoid vague tasks like: + +- "build auth" +- "create frontend" +- "implement backend" + +Prefer concrete tasks like: + +- "Add JWT validation middleware for API requests" +- "Create database migration for tenant, user, and role tables" +- "Implement POST /customers endpoint with validation and persistence" +- "Build customer list page with pagination and empty/loading states" + +Avoid Placeholder tasks: + +- Do not create placeholder, umbrella, or non-executable tasks unless they are true spikes or decision records. +- Each implementation task should correspond to a concrete change that can be completed and validated. + +Each task document must be executable in isolation by an AI coding agent. That means it must contain enough context, scope boundaries, dependencies, acceptance criteria, and validation guidance that the agent can implement it without needing to infer the intended work from the PRD alone. + +--- + +## Backlog artifact organization + +Generate the backlog as repository documentation using this structure: + +- `docs/backlog/index.md` — master backlog index +- `docs/backlog/epics/` — epic-level documents +- `docs/backlog/tasks/` — executable task documents +- `docs/backlog/phases/` — implementation phase documents if useful +- `docs/backlog/agents/` — AI-agent execution guidance if useful + +Use stable IDs and deterministic sortable filenames. + +Examples: + +- `E001-platform-foundation.md` +- `T001-repo-bootstrap.md` +- `P01-foundation.md` + +Do not put all detail into the index. +The index is for navigation, summary, sequencing, and readiness. +Detailed implementation instructions belong in epic and task documents. + +--- + +## Backlog index requirements + +Create `docs/backlog/index.md` as the single entry point for the backlog. + +It must include: + +- purpose of the backlog +- how to use the backlog +- backlog file organization +- executive summary +- major workstreams +- recommended implementation order +- key risks +- assumptions / gaps / open questions +- epic overview table +- recommended delivery phases +- task summary table +- dependency highlights +- ready-now tasks +- blocked tasks +- high-priority tasks +- links to all detailed backlog documents + +The index must be concise enough to scan quickly while still giving a complete map of the backlog. + +--- + +## Epic document requirements + +Create one file per epic under `docs/backlog/epics/`. + +For each epic include: + +- ID +- Title +- Summary +- Why this epic exists +- Goals +- In scope +- Out of scope +- Dependencies +- What it enables +- Risks / notes +- Task list with links +- Sequencing notes +- Completion criteria + +--- + +## Task document requirements + +Create one file per executable task under `docs/backlog/tasks/`. + +For each task include: + +- ID +- Title +- Epic +- Type (foundation, feature, bug, spike, infra, test, docs, security, ops) +- Status +- Priority +- Owner role +- Whether autonomous AI execution is appropriate +- Whether human review is required +- Dependencies +- What this task blocks, if known +- Description +- Goal / why this task exists +- Scope included +- Explicit out-of-scope +- Recommended context files/docs the implementation agent should read first +- Implementation guidance +- Acceptance criteria +- Definition of done +- Validation approach +- Suggested validation commands if they can be reasonably inferred +- Risks or notes +- Follow-on tasks if applicable + +Make each task independently understandable and safe for small-context execution. + +--- + +## Phase document requirements + +If phases materially improve execution, create phase docs under `docs/backlog/phases/`. + +Each phase should include: + +- ID +- Title +- Goal +- Why the phase exists +- Included epics +- Included tasks +- Exit criteria +- Risks + +Suggested examples: + +- Phase 0: repo/platform foundation +- Phase 1: core domain skeleton +- Phase 2: first end-to-end vertical slice +- Phase 3: hardening and operational readiness + +--- + +## AI-agent execution guidance + +If useful, create `docs/backlog/agents/execution-rules.md`. + +This should explain: + +- how AI agents should navigate the backlog +- that they should execute one task at a time +- how to determine whether a task is ready +- when to stop and escalate ambiguity +- which tasks can run in parallel +- which tasks must remain sequential +- which require human checkpoints +- which require integration validation before proceeding + +--- + +## For each backlog item include + +At minimum, every backlog item must capture: + +- ID +- Title +- Epic +- Type +- Description +- Goal / why this task exists +- Scope included +- Explicit out-of-scope +- Dependencies +- Suggested priority +- Suggested owner role +- Acceptance criteria +- Definition of done +- Risks or notes +- Whether the task is suitable for autonomous AI execution +- Recommended context files/docs the implementation agent should read first + +--- + +## Required planning output + +Your generated backlog must contain, either in the index or supporting documents: + +### 1. Executive summary + +- major workstreams +- recommended implementation order +- key risks +- assumptions/gaps discovered + +### 2. Epic list + +A concise list of epics with purpose and dependency order. + +### 3. Detailed backlog + +The full backlog in structured repository documentation. + +### 4. Dependency graph + +Show task/epic dependency relationships in a simple readable format. + +### 5. Recommended delivery phases + +Show logical implementation groupings and rollout order. + +### 6. AI-agent execution guidance + +Explain how autonomous coding agents should safely consume the backlog. + +--- + +## Additional instructions + +- Be exhaustive but practical. +- Do not merely restate the PRD. +- Infer missing implementation tasks that are obviously required. +- Surface ambiguities instead of silently guessing when they materially affect execution. +- Prefer dependency-correct sequencing over feature popularity. +- Optimize for a repo where AI agents will pick one task at a time from the backlog. +- Assume maintainability, testability, and observability matter from the start. +- Separate strategic planning from executable implementation work. +- Prefer one file per epic and one file per task. +- Optimize for deterministic discovery and safe parallel work. + +--- + +## Output expectations + +Create the backlog artifacts in the repository, not just as a single response. + +At minimum, generate: + +- `docs/backlog/index.md` +- epic docs +- task docs + +If the backlog is large, still ensure the index links to everything and that all task docs are scoped for isolated execution. + +Before finalizing the backlog, review it for: + +- oversized tasks that should be split +- missing dependency edges +- duplicate or redundant tasks +- tasks that are too vague for autonomous execution +- missing cross-cutting work such as testing, observability, security, docs, and operations + +Now analyze the documents under `docs/prd/*.md` and generate the backlog. diff --git a/.github/prompts/007-critique-review.md b/.github/prompts/007-critique-review.md new file mode 100644 index 000000000..9fcf56bab --- /dev/null +++ b/.github/prompts/007-critique-review.md @@ -0,0 +1,67 @@ +You are reviewing a planning set for adding language support to Alloy. + +Review the following documents: + +- +- +- +- +- docs/backlog/\* + +Your task is to act as a skeptical senior architect and planning reviewer. + +Write your review to: + +/06-review.md + +Produce a markdown document with the following exact sections: + +# 1. Overall Assessment + +Give a concise assessment of the planning set. + +# 2. Strengths + +List what is strong and well-grounded. + +# 3. Gaps + +Identify missing details, missing requirements, missing tasks, or weak areas. + +# 4. Ambiguities + +Identify unclear wording, assumptions, or decisions that could confuse an implementation agent. + +# 5. Architectural Risks + +Identify places where the plan may violate Alloy's apparent architecture or overfit to an existing language package. + +# 6. Scope Risks + +Identify where MVP may be too large, too small, or poorly bounded. + +# 7. Testing Risks + +Identify weak spots in the testing plan. + +# 8. Backlog Risks + +Identify tasks that are too large, incorrectly ordered, insufficiently testable, or unsafe for autonomous execution. + +# 9. Recommended Corrections + +Give concrete recommendations for improving the docs. + +# 10. Verdict + +Choose one: + +- Ready for implementation +- Ready with minor revisions +- Needs substantial revision + +Requirements: + +- Be specific. +- Ground criticisms in the documents and repo reality where possible. +- Prefer actionable feedback over general commentary. diff --git a/.github/prompts/008-revise.md b/.github/prompts/008-revise.md new file mode 100644 index 000000000..59b80067b --- /dev/null +++ b/.github/prompts/008-revise.md @@ -0,0 +1,38 @@ +You are working inside the Alloy monorepo. + +You previously produced planning documents for adding support to Alloy. +A review has now been completed. + +Your task is to revise the planning set based on: + +- +- +- +- +- docs/backlog +- /06-review.md + +Update the following files in place as needed: + +- +- +- docs/backlog + +Goals: + +1. Resolve ambiguities where possible. +2. Tighten MVP scope if needed. +3. Improve requirement clarity and task ordering. +4. Make acceptance criteria and tests more concrete. +5. Preserve good prior content; revise only where necessary. + +At the end, create: + +/07-revision-summary.md + +That file must contain: + +- what changed +- why it changed +- remaining unresolved questions +- whether the plan is now ready for implementation diff --git a/docs/00-relevant-files.md b/docs/00-relevant-files.md new file mode 100644 index 000000000..4b00d4d82 --- /dev/null +++ b/docs/00-relevant-files.md @@ -0,0 +1,213 @@ +# Discovery: Relevant Files for a New Language Package + +## Purpose + +This document identifies the minimum set of repository files needed to understand Alloy's core architecture, how existing language packages extend it, and what patterns to follow when adding a new language package (Rust). It is grounded entirely in the repository structure and intended as a practical index for subsequent analysis passes. + +--- + +## Core Files + +These files define the framework primitives that every language package builds on. + +| Path | Why It Matters | Concept | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `packages/core/src/index.ts` | Main barrel export — shows every public API surface at a glance | Public API surface | +| `packages/core/src/render.ts` | Core rendering engine that transforms JSX component trees into output text via Prettier | Rendering pipeline | +| `packages/core/src/reactivity.ts` | Custom wrapper over `@vue/reactivity`; drives reactive updates in generated code | Reactivity system | +| `packages/core/src/components/` | 26 built-in JSX components (`Output`, `SourceFile`, `Declaration`, `Scope`, `For`, `Show`, `Block`, `Indent`, etc.) | Component primitives | +| `packages/core/src/symbols/` | 11 files: `basic-symbol.ts`, `output-symbol.ts`, `symbol-table.ts`, `basic-scope.ts`, `output-scope.ts`, `output-space.ts`, `symbol-flow.ts`, `symbol-slot.tsx`, `decl.ts` | Symbol/scope resolution | +| `packages/core/src/context/` | 11 files: scope, binder, declaration, member-declaration, member-scope, source-file, source-directory, format-options, assignment, name-policy contexts | Execution contexts | +| `packages/core/src/binder.ts` | Symbol binding logic — wires declarations to scopes | Symbol binding | +| `packages/core/src/name-policy.ts` | Name policy infrastructure (casing, conflict resolution) | Naming conventions | +| `packages/core/src/refkey.ts` | Reference key management — cross-component symbol linking | Cross-reference keys | +| `packages/core/src/code.ts` | `code` tagged template literal for raw string content in components | Code construction | +| `packages/core/src/stc.ts` | "Symbol Table Component" wrapper — type-safe component factories | STC pattern | +| `packages/core/src/runtime/component.ts` | Component function/creator definition | Component runtime | +| `packages/core/src/content-slot.tsx` | Slot component and context for content projection | Content slots | +| `packages/core/package.json` | Exports map, dependency on `@vue/reactivity`, dual build (prod/dev/browser) | Package structure | + +--- + +## Existing Language Package Files + +### TypeScript (`packages/typescript/`) — most mature, 48 components + +| Path | Why It Matters | Role | +| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| `packages/typescript/src/index.ts` | Barrel export showing the full public surface | API surface | +| `packages/typescript/src/symbols/ts-output-symbol.ts` | Language-specific output symbol with member spaces `["static", "instance", "private-static", "private-instance"]` | Custom symbol type | +| `packages/typescript/src/symbols/ts-lexical-scope.ts` | Lexical scope specialization for TS | Scope hierarchy | +| `packages/typescript/src/symbols/ts-member-scope.ts` | Member scope (class/interface bodies) | Member scoping | +| `packages/typescript/src/symbols/ts-module-scope.ts` | Module-level scope (file-level imports/exports) | Module scoping | +| `packages/typescript/src/symbols/ts-package-scope.ts` | Package-level scope (cross-file resolution) | Package scoping | +| `packages/typescript/src/symbols/reference.tsx` | Reference rendering — emits the correct identifier or import for a symbol | Reference resolution | +| `packages/typescript/src/name-policy.ts` | TS-specific naming rules (camelCase functions, PascalCase types, etc.) | Naming policy | +| `packages/typescript/src/name-conflict-resolver.ts` | Handles naming collisions via suffixes | Conflict resolution | +| `packages/typescript/src/components/` | 48 components: declarations (Class, Function, Interface, Enum), expressions (Array, Object, Arrow, Call), statements (Import, Export, If, Switch, Try), docs (JSDoc) | Language constructs | +| `packages/typescript/src/components/stc/index.ts` | STC wrappers for all 48 components | Type-safe factories | +| `packages/typescript/src/create-package.ts` | Factory for creating a full TS package output | Package scaffolding | +| `packages/typescript/src/context/package-metadata.ts` | Package.json metadata context | Package metadata | +| `packages/typescript/src/builtins/node.ts` | Built-in Node.js type declarations | Standard library | + +### Java (`packages/java/`) — mature, 28 components + +| Path | Why It Matters | Role | +| ------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | -------------------- | +| `packages/java/src/index.ts` | Barrel export | API surface | +| `packages/java/src/symbols/java-output-symbol.ts` | Java-specific output symbol | Custom symbol type | +| `packages/java/src/symbols/java-lexical-scope.ts` | Java lexical scoping | Scope hierarchy | +| `packages/java/src/symbols/java-package-scope.ts` | Package-level scope | Package scoping | +| `packages/java/src/symbols/java-project-scope.ts` | Project-level scope (multi-package) | Project scoping | +| `packages/java/src/symbols/reference.ts` | Java import/reference rendering | Reference resolution | +| `packages/java/src/name-policy.ts` | Java naming conventions | Naming policy | +| `packages/java/src/components/` | 28 components: Class, Interface, Enum, Annotation, Method, Constructor, MavenProject, ImportStatement, etc. | Language constructs | +| `packages/java/src/create-library.ts` | Factory for Java library output | Library scaffolding | + +### Python (`packages/python/`) — early (v0.3.0), 34 components + +| Path | Why It Matters | Role | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | -------------------- | +| `packages/python/src/index.ts` | Barrel export | API surface | +| `packages/python/src/symbols/python-output-symbol.ts` | Python-specific output symbol | Custom symbol type | +| `packages/python/src/symbols/python-lexical-scope.ts` | Python lexical scoping | Scope hierarchy | +| `packages/python/src/symbols/python-member-scope.ts` | Class body scope | Member scoping | +| `packages/python/src/symbols/python-module-scope.ts` | Module-level scope | Module scoping | +| `packages/python/src/symbols/factories.ts` | Symbol factory functions | Symbol creation | +| `packages/python/src/symbols/reference.tsx` | Python import/reference rendering | Reference resolution | +| `packages/python/src/name-policy.ts` | Python naming conventions (snake_case, etc.) | Naming policy | +| `packages/python/src/name-conflict-resolver.ts` | Handles naming collisions | Conflict resolution | +| `packages/python/src/components/` | 34 components: ClassDeclaration, FunctionDeclaration, EnumDeclaration, DataclassDeclaration, various methods, PyDoc, etc. | Language constructs | +| `packages/python/src/create-module.ts` | Factory for Python module output | Module scaffolding | + +### C# (`packages/csharp/`) — mature, 75 components, largest package + +| Path | Why It Matters | Role | +| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | +| `packages/csharp/src/index.ts` | Barrel export | API surface | +| `packages/csharp/src/symbols/csharp.ts` | C#-specific output symbol | Custom symbol type | +| `packages/csharp/src/scopes/` | 10 files: separate scopes directory with `csharp.ts`, `class.ts`, `method.ts`, `namespace.ts`, `source-file.ts`, `named-type.ts`, `lexical.ts`, `factories.ts`, `contexts.ts` | Scope hierarchy (newer pattern) | +| `packages/csharp/src/symbols/reference.tsx` | C# using/reference rendering | Reference resolution | +| `packages/csharp/src/name-policy.ts` | C# naming conventions (PascalCase, etc.) | Naming policy | +| `packages/csharp/src/components/` | 75 components with co-located tests: class, struct, enum, interface, record, method, field, property, attributes, doc, namespace, etc. | Language constructs | +| `packages/csharp/src/builtins/` | 174 files covering the .NET standard library (System._, Microsoft._) | Extensive builtins | +| `packages/csharp/src/create-library.ts` | Factory for C# library output | Library scaffolding | +| `packages/csharp/src/contexts/` | 4 context files: format-options, global-namespace, namespace, reference-context | Language contexts | + +### Go (`packages/go/`) — early (v0.2.0), 23 components, closest reference for Rust + +| Path | Why It Matters | Role | +| ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | +| `packages/go/src/index.ts` | Barrel export | API surface | +| `packages/go/src/symbols/go.ts` | Go-specific output symbol | Custom symbol type | +| `packages/go/src/symbols/function.ts` | Function symbol specialization | Function symbols | +| `packages/go/src/symbols/named-type.ts` | Named type symbol | Type symbols | +| `packages/go/src/symbols/package.ts` | Package-level symbol | Package symbols | +| `packages/go/src/symbols/type-parameter.ts` | Generic type parameter symbol | Generics | +| `packages/go/src/symbols/factories.ts` | Symbol factory functions | Symbol creation | +| `packages/go/src/symbols/reference.ts` | Go import/reference rendering | Reference resolution | +| `packages/go/src/scopes/` | 10 files: separate scopes directory (newer pattern, like C#) — `go.ts`, `function.ts`, `lexical.ts`, `module.ts`, `named-type.ts`, `package.ts`, `source-file.ts`, `factories.ts`, `contexts.ts` | Scope hierarchy | +| `packages/go/src/name-policy.ts` | Go naming conventions (exported = PascalCase, unexported = camelCase) | Naming policy | +| `packages/go/src/components/` | 23 components: Function, Interface, Struct, Type, Variable, SourceFile, ImportStatement, Pointer, TypeParameters, Comment | Language constructs | +| `packages/go/src/components/stc/index.ts` | STC wrappers (27 entries) | Type-safe factories | +| `packages/go/src/create-module.ts` | Factory for Go module output | Module scaffolding | +| `packages/go/src/builtins/` | 5 standard library packages: `fmt`, `io`, `net`, `time` | Standard library | +| `packages/go/src/context/package.ts` | Package context | Package metadata | + +--- + +## Tests and Examples + +### Test Utilities (per-package pattern) + +| Path | Why It Matters | +| ------------------------------------ | ------------------------------------------------------------------------------------------ | +| `packages/typescript/test/utils.tsx` | Defines `toSourceText()`, `findFile()`, `testRender()` — the canonical test helper pattern | +| `packages/java/test/utils.tsx` | Same pattern adapted for Java | +| `packages/python/test/utils.tsx` | Same pattern adapted for Python | +| `packages/csharp/testing/` | C# test utilities (exported via package.json) | +| `packages/go/test/utils.tsx` | Same pattern adapted for Go | +| `packages/core/src/testing.ts` | Core testing utilities: `d` (dedent), `.toRenderTo()` matcher | + +### Key Test Files (representative patterns) + +| Path | Why It Matters | +| -------------------------------------------------------- | ------------------------------------- | +| `packages/core/test/rendering/basic.test.tsx` | Core rendering fundamentals | +| `packages/core/test/symbols/symbol-table.test.ts` | Symbol table mechanics | +| `packages/core/test/symbols/resolution.test.ts` | Cross-reference resolution | +| `packages/typescript/test/class.test.tsx` | Class declaration generation pattern | +| `packages/typescript/test/imports.test.tsx` | Import resolution and rendering | +| `packages/typescript/test/function-declaration.test.tsx` | Function output testing | +| `packages/java/test/class.test.tsx` | Java class generation | +| `packages/python/test/class-declaration.test.tsx` | Python class generation | +| `packages/go/src/components/function/function.test.tsx` | Go function test (co-located pattern) | +| `packages/go/src/components/struct/declaration.test.tsx` | Go struct test | + +### Sample Projects + +| Path | Why It Matters | +| ----------------------------- | ---------------------------------------------------------------- | +| `samples/basic-project/` | Minimal Alloy project with TS + Java generation examples | +| `samples/go-example/` | Go-specific generation example — closest model for a Rust sample | +| `samples/python-example/` | Python generation with TypeSpec integration | +| `samples/client-emitter/` | Practical API client generation pattern | +| `samples/scaffold-generator/` | Template-based scaffolding | + +--- + +## Documentation + +| Path | Why It Matters | +| ------------------------------------------------------------ | ----------------------------------------------------------- | +| `readme.md` | Repo overview, JSX/STC syntax examples, supported languages | +| `packages/docs/src/content/docs/guides/basic-concepts.md` | Components, context, rendering concepts | +| `packages/docs/src/content/docs/guides/diagnosing-issues.md` | Troubleshooting guide | +| `.github/copilot-instructions.md` | Alloy JSX conventions and component patterns | +| `.github/prompts/000.md` | Prompt series placeholders and conventions | +| `.github/prompts/001-discovery.md` – `008*.md` | Sequential workflow prompts for language package creation | +| `test/performance/README.md` | Benchmark runner documentation | + +--- + +## Recommended Reading Order + +1. **`readme.md`** — High-level understanding of Alloy's purpose and syntax +2. **`packages/core/src/index.ts`** — Scan the full public API surface +3. **`packages/core/src/components/`** — Understand built-in primitives (`Output`, `SourceFile`, `Declaration`, `Scope`, `Block`, `Indent`) +4. **`packages/core/src/symbols/`** — Understand symbol tables, scopes, binding, and resolution +5. **`packages/core/src/context/`** — Understand execution contexts (scope, binder, declaration, name-policy) +6. **`packages/core/src/render.ts`** — Understand the rendering pipeline +7. **`packages/go/src/`** — Study the Go package end-to-end as the closest structural model for Rust (newer `scopes/` + `symbols/` pattern, systems language, similar constructs) +8. **`packages/typescript/src/`** — Study as the most mature and comprehensive reference +9. **`packages/go/test/` and `packages/go/src/components/**/\*.test.tsx`\*\* — Testing patterns for a newer language package +10. **`samples/go-example/`** — Practical usage patterns +11. **`packages/csharp/src/scopes/`** — Additional reference for the newer scopes architecture pattern (shared with Go) + +--- + +## Exclusions + +These files/directories appear large but are likely irrelevant for planning a Rust language package: + +| Path | Reason to Skip | +| -------------------------------------------- | ------------------------------------------------------------------------------- | +| `packages/csharp/src/builtins/` | 174 files of .NET standard library definitions — C#-specific, not generalizable | +| `packages/devtools/` | React-based debugging UI — unrelated to language package creation | +| `packages/babel-plugin-jsx-dom-expressions/` | Internal JSX transformation plumbing — excluded from vitest workspace | +| `packages/babel-plugin-alloy/` | Babel plugin internals | +| `packages/babel-preset-alloy/` | Babel preset internals | +| `packages/rollup-plugin/` | Build tooling internals | +| `packages/trace-cli/` | Debug trace CLI tool | +| `packages/cli/` | CLI orchestration — relevant only after implementation | +| `packages/create/` | Project scaffolding tool | +| `packages/msbuild/` | MSBuild XML generation — C#-specific | +| `packages/docs/` (build infra) | Astro/Starlight site scaffolding — docs content is listed above | +| `packages/markdown/` | Markdown generation — different domain | +| `packages/json/` | JSON generation — different domain | +| `pnpm-lock.yaml` | Dependency lock file | +| `patches/` | pnpm patches for upstream dependencies | +| `eng/` | Release automation scripts | +| `packages/core/src/debug/` | 19 files of debug/trace infrastructure — not needed for language design | +| `packages/core/src/devtools/` | DevTools protocol and server | +| `packages/core/src/host/` | Host environment abstraction (Node vs browser) | diff --git a/docs/backlog/agents/execution-rules.md b/docs/backlog/agents/execution-rules.md new file mode 100644 index 000000000..28dae9b04 --- /dev/null +++ b/docs/backlog/agents/execution-rules.md @@ -0,0 +1,88 @@ +# AI Agent Execution Rules + +## How to Navigate the Backlog + +1. **Start with the index**: Read `docs/backlog/index.md` for the full backlog overview. +2. **Check phases**: Read the current phase document (e.g., `phases/P01-foundation.md`) for context. +3. **Pick a ready task**: A task is "ready" when all its dependencies have status `done`. +4. **Read the task document fully** before starting implementation. +5. **Read all listed context files** before writing code. + +## Execution Rules + +### One Task at a Time + +- Execute exactly one task per session. +- Complete the task fully (including tests) before moving on. + +### How to Determine if a Task is Ready + +A task is ready when: + +- All tasks listed in its `Dependencies` field have been completed. +- The task status is `pending` (not `in_progress`, `done`, or `blocked`). + +### Ready-Now Tasks (Phase 1) + +These tasks have no dependencies and can start immediately: + +- **T001**: Package scaffold +- **T007**: Name policy (depends only on T001) +- **T008**: Parameter descriptor (depends only on T001) +- **T016**: DocComment (depends only on T001) +- **T017**: TypeParameters (depends only on T001) +- **T018**: Value component (depends only on T001) + +### Sequential Chains + +These tasks must be done in order: + +- T001 → T002 → T003 → T004 → T005 → T006 (foundation chain) +- T001 → T003 → T004 → T005 → T005b → T006 (foundation chain) +- T009 → T010 → T011/T012/T013/T014 (component chain) +- T022 → T023 → T024 → T025 → T026 → T027 (module system chain) +- T028 → T029 → T031 → T030 (external deps chain) + +### Parallel-Safe Tasks + +These groups can be done in parallel (after their dependencies are met): + +- T007, T007b, T008, T016, T017, T018 (independent foundations) +- T011, T012, T013, T014, T015 (independent components after T010) +- T019, T020 (traits and impl after E003) +- T032, T033, T034, T035 (polish tasks after prior phases) + +### When to Stop and Escalate + +Stop and ask for human guidance when: + +- A task's acceptance criteria are ambiguous. +- You discover a design conflict between the task and existing code. +- A dependency is listed as `done` but the expected code doesn't exist. +- You need to modify `@alloy-js/core` to complete a task (this is not allowed). +- Tests fail in ways that suggest an architectural issue, not a bug. + +### Human Review Checkpoints + +These tasks require human review before proceeding: + +- **T001**: Initial package setup (structure must be correct). +- **T005**: Scope hierarchy (architecture review). +- **T022**: Reference resolution (most complex task). +- **T034**: Golden scenarios (validates full MVP). + +### Quality Rules + +- Every task that adds a component must include tests. +- Tests use `toRenderTo()` matcher with `d` template tag for expected output. +- Always import `"@alloy-js/core/testing"` in test files. +- Follow Alloy conventions: `props.x` access, `code` template tag, kebab-case files. +- Do not modify `@alloy-js/core` or any other package. +- Do not update changelogs. + +### After Completing a Task + +1. Run `pnpm --filter @alloy-js/rust build` to verify compilation. +2. Run `pnpm --filter @alloy-js/rust test` to verify all tests pass. +3. If both pass, the task is done. +4. If either fails, fix the issues before marking done. diff --git a/docs/backlog/epics/E001-package-foundation.md b/docs/backlog/epics/E001-package-foundation.md new file mode 100644 index 000000000..5c629330e --- /dev/null +++ b/docs/backlog/epics/E001-package-foundation.md @@ -0,0 +1,57 @@ +# E001: Package Foundation + +## Summary + +Bootstrap the `@alloy-js/rust` package within the Alloy monorepo with build configuration, test infrastructure, and barrel exports. + +## Why This Epic Exists + +Every language package requires a package scaffold before any code can be written. This is the prerequisite for all subsequent work. + +## Goals + +- Create `packages/rust/` with `package.json`, `tsconfig.json`, `vitest.config.ts`. +- Configure dependency on `@alloy-js/core`. +- Create `src/index.ts` barrel export. +- Create `test/utils.tsx` with `toSourceText()` helper. +- Create `test/vitest.setup.ts` importing `@alloy-js/core/testing`. +- Verify build and test commands work. + +## In Scope + +- Package scaffold files. +- Test infrastructure. +- Barrel export structure (empty initially). + +## Out of Scope + +- Any Rust language components or symbols. +- Actual language functionality. + +## Dependencies + +- None (first epic). + +## What It Enables + +- All subsequent epics (E002–E006). + +## Risks / Notes + +- Must follow the exact same package.json/tsconfig patterns as existing language packages (e.g., `packages/typescript/`, `packages/go/`). +- The `pnpm-workspace.yaml` must include the new package. + +## Task List + +- [T001: Package scaffold](../tasks/T001-package-scaffold.md) +- [T002: Test infrastructure](../tasks/T002-test-infrastructure.md) + +## Sequencing Notes + +Must be completed first. All other epics depend on this. + +## Completion Criteria + +- `pnpm build` succeeds for the `@alloy-js/rust` package. +- `pnpm test` runs (even with no tests yet) without errors. +- `import * as rust from "@alloy-js/rust"` compiles without errors. diff --git a/docs/backlog/epics/E002-symbol-system.md b/docs/backlog/epics/E002-symbol-system.md new file mode 100644 index 000000000..b5e4f2832 --- /dev/null +++ b/docs/backlog/epics/E002-symbol-system.md @@ -0,0 +1,73 @@ +# E002: Symbol System + +## Summary + +Implement the Rust symbol classes, scope hierarchy, factory functions, and name policy that form the foundation of the language package's type system. + +## Why This Epic Exists + +Alloy's code generation model is built on symbols and scopes. Every declaration, reference, and import depends on the symbol system. This must be in place before any components can be built. + +## Goals + +- Implement `RustOutputSymbol` extending core's `OutputSymbol`. +- Implement `NamedTypeSymbol` and `FunctionSymbol` subclasses. +- Implement all 6 scope classes: `RustCrateScope`, `RustModuleScope`, `RustFunctionScope`, `RustLexicalScope`, `RustImplScope`, `RustTraitScope`. +- Implement symbol factory functions. +- Implement `createRustNamePolicy()` with all Rust element types and reserved words. +- Implement scope utility hooks (`useRustScope()`, etc.). +- Implement `ParameterDescriptor` interface. + +## In Scope + +- Symbol classes with Rust-specific properties (visibility, symbolKind, isAsync, isUnsafe, isConst). +- Scope classes with correct declaration spaces. +- Factory functions for creating symbols in scopes. +- Name policy with `snake_case`, `PascalCase`, `SCREAMING_SNAKE_CASE`, and `r#` reserved word handling. +- `ParameterDescriptor` interface. + +## Out of Scope + +- Reference resolution logic (E004). +- External crate descriptors (E005). +- Any JSX components. + +## Dependencies + +- E001 (package scaffold must exist). + +## What It Enables + +- E003 (core components need symbols and scopes). +- E004 (module system needs scope import tracking). +- E005 (external deps need symbol creation). + +## Risks / Notes + +- The scope hierarchy design (declaration spaces, member spaces) is critical and hard to change later. Study Go (`packages/go/src/symbols/`) and C# (`packages/csharp/src/symbols/`, `packages/csharp/src/scopes/`) patterns closely. +- `RustModuleScope` needs `use` import tracking from the start, even though the UseStatement component comes in E004. +- `RustCrateScope` needs child module and dependency tracking from the start. + +## Task List + +- [T003: RustOutputSymbol base class](../tasks/T003-rust-output-symbol.md) +- [T004: NamedTypeSymbol and FunctionSymbol](../tasks/T004-symbol-subclasses.md) +- [T005: Scope hierarchy Part 1 (crate/module)](../tasks/T005-scope-hierarchy.md) +- [T005b: Scope hierarchy Part 2 (function/lexical/member)](../tasks/T005b-scope-hierarchy-part2.md) +- [T006: Symbol factory functions](../tasks/T006-symbol-factories.md) +- [T007: Name policy](../tasks/T007-name-policy.md) +- [T007b: Name conflict resolver](../tasks/T007b-name-conflict-resolver.md) +- [T008: Parameter descriptor and scope hooks](../tasks/T008-parameter-descriptor.md) + +## Sequencing Notes + +T003 → T004 → T005 → T005b → T006 (sequential dependency chain). T007, T007b, and T008 can be done in parallel with T005/T006. + +## Completion Criteria + +- All symbol classes instantiate correctly with expected properties. +- All scope classes have correct declaration spaces. +- Factory functions create symbols in the right spaces. +- Name policy transforms names correctly for all Rust element types. +- Reserved words are handled with `r#` prefix. +- `name-policy.test.tsx` passes. diff --git a/docs/backlog/epics/E003-core-components.md b/docs/backlog/epics/E003-core-components.md new file mode 100644 index 000000000..bcadfd360 --- /dev/null +++ b/docs/backlog/epics/E003-core-components.md @@ -0,0 +1,74 @@ +# E003: Core Declaration Components + +## Summary + +Implement the fundamental Rust declaration components: struct, enum, function, type alias, const, plus formatting components (attributes, doc comments, type parameters, values). + +## Why This Epic Exists + +Declaration components are the primary user-facing API. Users compose these to generate Rust source code. This epic delivers the first renderable output. + +## Goals + +- Implement structural components: `SourceFile`, `CrateDirectory`, `Declaration`, `Reference`. +- Implement declaration components: `StructDeclaration`, `Field`, `EnumDeclaration`, `EnumVariant`, `FunctionDeclaration`, `TypeAlias`, `ConstDeclaration`. +- Implement formatting components: `Attribute`, `DeriveAttribute`, `DocComment`, `ModuleDocComment`, `Parameters`, `TypeParameters`, `WhereClause`, `Value`. +- All components render correct single-file Rust syntax. +- Tests for each component. + +## In Scope + +- All listed components with props interfaces. +- Single-file rendering (no cross-module imports yet). +- Visibility modifiers (`pub`, `pub(crate)`). +- Derive attributes. +- Doc comments. +- Type parameters with bounds. + +## Out of Scope + +- Cross-module references and imports (E004). +- Trait declarations and impl blocks (separate in E003b). +- External crate descriptors (E005). +- Cargo.toml generation (E005). + +## Dependencies + +- E001 (package scaffold). +- E002 (symbol system — symbols, scopes, factories, name policy). + +## What It Enables + +- E003b (traits/impl use FunctionDeclaration). +- E004 (module system uses SourceFile, Reference). +- E005 (build file uses CrateDirectory). +- Users can generate single-file Rust code. + +## Risks / Notes + +- `SourceFile` is complex — it wraps core's SourceFile, creates RustModuleScope, and will eventually render `use` statements. For this phase, stub the import rendering (empty). +- `CrateDirectory` creates the crate scope. Cargo.toml is deferred to E005. +- `Reference` initially just renders symbol names without import tracking. + +## Task List + +- [T009: SourceFile and CrateDirectory](../tasks/T009-source-file-crate-directory.md) +- [T010: Declaration and Reference basics](../tasks/T010-declaration-reference.md) +- [T011: StructDeclaration and Field](../tasks/T011-struct-declaration.md) +- [T012: EnumDeclaration and EnumVariant](../tasks/T012-enum-declaration.md) +- [T013: FunctionDeclaration and Parameters](../tasks/T013-function-declaration.md) +- [T014: TypeAlias and ConstDeclaration](../tasks/T014-type-alias-const.md) +- [T015: Attribute and DeriveAttribute](../tasks/T015-attributes.md) +- [T016: DocComment and ModuleDocComment](../tasks/T016-doc-comments.md) +- [T017: TypeParameters and WhereClause](../tasks/T017-type-parameters.md) +- [T018: Value component](../tasks/T018-value-component.md) + +## Sequencing Notes + +T009 → T010 must come first (SourceFile/Declaration are prerequisites). Then T011–T018 can be done in any order (they are independent components). + +## Completion Criteria + +- `struct.test.tsx`, `enum.test.tsx`, `function.test.tsx`, `attributes.test.tsx` pass. +- Single-file Rust output is syntactically correct. +- `pub`, derives, doc comments, type parameters render correctly. diff --git a/docs/backlog/epics/E004-traits-and-impl.md b/docs/backlog/epics/E004-traits-and-impl.md new file mode 100644 index 000000000..2e3c7d021 --- /dev/null +++ b/docs/backlog/epics/E004-traits-and-impl.md @@ -0,0 +1,62 @@ +# E004: Traits and Impl Blocks + +## Summary + +Implement trait declarations and impl blocks — the Rust-specific constructs that have no direct analog in existing Alloy language packages. + +## Why This Epic Exists + +Traits and impl blocks are central to Rust's type system. Without them, generated code cannot define behavior for types. Impl blocks are architecturally novel for Alloy since methods are declared separately from types. + +## Goals + +- Implement `TraitDeclaration` with method signatures, default implementations, and supertraits. +- Implement `ImplBlock` for both inherent and trait implementations. +- Support `self` receiver auto-injection in methods inside impl blocks. +- Methods in impl blocks are added to the target type's member space. + +## In Scope + +- `TraitDeclaration` component and `RustTraitScope`. +- `ImplBlock` component and `RustImplScope`. +- `FunctionDeclaration` `receiver` prop for `&self`, `&mut self`, `self`. +- Member symbol creation for impl methods. + +## Out of Scope + +- Associated types (render inline for now). +- Default trait implementations via `impl Default for Type`. +- Trait objects (`dyn Trait`). + +## Dependencies + +- E002 (RustImplScope, RustTraitScope already defined). +- E003 (FunctionDeclaration, TypeParameters, WhereClause). + +## What It Enables + +- Complete type system for Rust code generation. +- E005 (external crate traits can be referenced in derives/impls). + +## Risks / Notes + +- Impl blocks can add members to a type defined elsewhere. The member scope must correctly append to the type's existing member space without conflicts. +- Multiple impl blocks for the same type must coexist. +- Self receiver handling needs clear design — recommend `receiver` prop with default `"&self"` inside impl blocks. + +## Task List + +- [T019: TraitDeclaration](../tasks/T019-trait-declaration.md) +- [T020: ImplBlock](../tasks/T020-impl-block.md) +- [T021: Self receiver in FunctionDeclaration](../tasks/T021-self-receiver.md) + +## Sequencing Notes + +T019 and T020 can be done in parallel. T021 depends on T020 (needs ImplBlock context). + +## Completion Criteria + +- `trait.test.tsx` and `impl.test.tsx` pass. +- Trait with methods and default impls renders correctly. +- Inherent impl and trait impl render correctly. +- Methods inside impl blocks auto-get `&self` receiver. diff --git a/docs/backlog/epics/E005-module-system-imports.md b/docs/backlog/epics/E005-module-system-imports.md new file mode 100644 index 000000000..fde048c67 --- /dev/null +++ b/docs/backlog/epics/E005-module-system-imports.md @@ -0,0 +1,71 @@ +# E005: Module System and Imports + +## Summary + +Implement Rust's module system with automatic `use` statement generation, `mod` declarations, and multi-module crate support. + +## Why This Epic Exists + +Rust requires explicit `mod` declarations and `use` imports. Without this, generated code cannot span multiple files. This is the most architecturally complex part of the language package. + +## Goals + +- Implement `RustModuleScope.addUse()` for import tracking. +- Implement `Reference` component with full resolution and import triggering. +- Implement `UseStatement` / `UseStatements` with path grouping (`use path::{A, B};`). +- Implement `ModuleDirectory` for submodule directories. +- Implement auto-generation of `mod` declarations in parent modules. +- Import sorting: `std::` → external → `crate::`. +- Prelude type handling (no `use` for `Option`, `Result`, `Vec`, `String`, etc.). + +## In Scope + +- `use crate::path::Symbol;` for same-crate cross-module references. +- `use ::Symbol;` for external crate references. +- `use path::{A, B};` tree grouping. +- `mod name;` auto-generation. +- `ModuleDirectory` component. +- Import sorting and grouping with blank lines. + +## Out of Scope + +- `pub use` re-exports. +- `use path::*` glob imports. +- `self::` and `super::` path shortcuts (use `crate::` paths). + +## Dependencies + +- E002 (RustModuleScope, RustCrateScope with tracking methods). +- E003 (SourceFile, Reference, CrateDirectory). + +## What It Enables + +- Multi-module crate generation. +- E006 (external crate references trigger use + dependency tracking). + +## Risks / Notes + +- This is the hardest part. Study Go's implementation (`packages/go/src/symbols/reference.ts`, `packages/go/src/components/ImportStatement.tsx`) closely. +- `use` path construction from `ResolutionResult` requires walking `pathDown` to build `crate::module::submodule::Symbol`. +- Prelude types must be skipped — maintain a prelude list. +- `mod` declarations must respect visibility (`pub mod` vs `mod`). + +## Task List + +- [T022: Reference resolution with use tracking](../tasks/T022-reference-resolution.md) +- [T023: UseStatement and UseStatements components](../tasks/T023-use-statements.md) +- [T024: ModuleDirectory component](../tasks/T024-module-directory.md) +- [T025: Auto mod declaration generation](../tasks/T025-mod-declarations.md) +- [T026: Cross-module import integration tests](../tasks/T026-import-integration-tests.md) +- [T027: Module structure integration tests](../tasks/T027-module-structure-tests.md) + +## Sequencing Notes + +T022 → T023 → T024 → T025 → T026 → T027 (mostly sequential — each builds on the previous). + +## Completion Criteria + +- `imports.test.tsx`, `reference.test.tsx`, `module-structure.test.tsx` pass. +- Cross-module references auto-generate correct `use` statements. +- `mod` declarations auto-generated in crate root and parent modules. +- Import sorting follows Rust convention. diff --git a/docs/backlog/epics/E006-external-deps-build-polish.md b/docs/backlog/epics/E006-external-deps-build-polish.md new file mode 100644 index 000000000..6c4088dd3 --- /dev/null +++ b/docs/backlog/epics/E006-external-deps-build-polish.md @@ -0,0 +1,74 @@ +# E006: External Dependencies, Build File, and Polish + +## Summary + +Implement external crate descriptors, `Cargo.toml` generation, `std` builtins, STC wrappers, and final integration tests. + +## Why This Epic Exists + +External crate support and build file generation are essential for real-world use. Polish ensures the package is production-ready and consistent with other Alloy language packages. + +## Goals + +- Implement `createCrate()` factory for external crate descriptors. +- Implement `std` builtin crate descriptor. +- Implement `CargoTomlFile` component. +- Implement dependency tracking on `RustCrateScope`. +- Implement STC wrappers for all key components. +- Verify all barrel exports are complete. +- Pass all golden scenario tests. + +## In Scope + +- `createCrate()` factory with descriptor pattern. +- `std` builtins (Option, Result, Vec, String, HashMap, fmt::Display, etc.). +- `CargoTomlFile` with [package] and [dependencies] sections. +- Dependency auto-tracking when external crate symbols are referenced. +- STC wrappers in `src/components/stc/index.ts`. +- Golden scenario tests matching PRD section 7. +- Edge case tests (empty structs, empty enums, etc.). + +## Out of Scope + +- Cargo workspace support. +- Feature flags in Cargo.toml. +- Dev dependencies section. + +## Dependencies + +- E001–E005 (all prior epics). + +## What It Enables + +- Complete MVP delivery. +- Real-world usage for code generation targeting Rust. + +## Risks / Notes + +- `createCrate()` must follow the SymbolCreator protocol used by existing packages. Study `packages/go/src/create-module.ts` and `packages/csharp/src/create-library.ts`. +- TOML formatting is string-based (no TOML library needed for simple cases). +- Prelude handling: std prelude types should not generate `use` statements. + +## Task List + +- [T028: createCrate() factory](../tasks/T028-create-crate.md) +- [T029: std builtin descriptors](../tasks/T029-std-builtins.md) +- [T030: CargoTomlFile component](../tasks/T030-cargo-toml.md) +- [T031: External crate dependency tracking](../tasks/T031-dependency-tracking.md) +- [T032: STC wrappers](../tasks/T032-stc-wrappers.md) +- [T033: Barrel exports verification](../tasks/T033-barrel-exports.md) +- [T034: Golden scenario tests](../tasks/T034-golden-scenarios.md) +- [T035: Edge case tests](../tasks/T035-edge-cases.md) + +## Sequencing Notes + +T028 → T029 → T031 (dependency chain). T030 depends on T031. T032–T035 can be done in parallel after T028–T031. + +## Completion Criteria + +- `cargo-toml.test.tsx` passes. +- External crate references generate `use` + `Cargo.toml` dependency. +- All golden scenarios from PRD section 7 pass. +- STC wrappers exist for all key components. +- All barrel exports verified complete. +- Full test suite green. diff --git a/docs/backlog/epics/E007-bug-fixes.md b/docs/backlog/epics/E007-bug-fixes.md new file mode 100644 index 000000000..ce5e64bec --- /dev/null +++ b/docs/backlog/epics/E007-bug-fixes.md @@ -0,0 +1,85 @@ +# E007: Bug Fixes and Rendering Corrections + +## Summary + +Fix bugs and rendering issues discovered during integration testing with `samples/rust-example/`. These are defects in existing components that produce incorrect Rust output. + +## Why This Epic Exists + +The initial MVP implementation (E001–E006) produced working components, but integration testing revealed rendering defects that produce invalid Rust code. These must be fixed before adding new features. + +## Goals + +- Fix Reference component to work in all scope positions (fields, parameters, return types). +- Fix missing newlines between sibling items (enum variants, struct fields, doc comments). +- Fix trait abstract method rendering (`;` instead of `{}`). +- Fix enum tuple variant syntax (`(Type)` instead of `{ Type }`). +- Auto-register standalone source files as modules. +- Improve FunctionDeclaration default receiver ergonomics. +- Fix ModDeclarations reactivity for JSX ordering independence. + +## In Scope + +- T039: Reference scope traversal +- T040: Missing newlines between sibling items +- T041: Trait abstract method signatures +- T042: Enum tuple variant support +- T043: Standalone SourceFile module registration +- T044: FunctionDeclaration default receiver +- T045: ModDeclarations render order dependency +- T069: ImplBlock generic parameter forwarding +- T070: ImplBlock trait name missing use import +- T071: Prelude type shadowing causes missing imports +- T072: CrateDirectory Cargo.toml missing [lib] path +- T073: Reference inline rendering inserts unexpected line breaks +- T074: Cargo.toml renders empty [dependencies] section + +## Out of Scope + +- New components or features (those are in E008/E009). +- Changes to the core Alloy rendering engine. + +## Dependencies + +- E001–E006 (existing implementation must be complete). + +## What It Enables + +- Correct Rust output from all existing components. +- Unblocks expression components (E008) and language feature gaps (E009). +- Unblocks the sample project update (T053). + +## Risks / Notes + +- T039 (Reference scope traversal) is the highest-impact fix — it enables type-safe references everywhere. +- T040 (newlines) affects all generated output — likely requires changes to multiple components. +- Some fixes may require test updates for changed output formatting. + +## Task List + +- [T039: Reference scope traversal](../tasks/T039-reference-scope-traversal.md) — P0 +- [T040: Missing newlines between sibling items](../tasks/T040-missing-newlines-between-items.md) — P0 +- [T041: Trait abstract method signatures](../tasks/T041-trait-abstract-methods.md) — P1 +- [T042: Enum tuple variant support](../tasks/T042-enum-tuple-variants.md) — P1 +- [T043: Standalone SourceFile module registration](../tasks/T043-standalone-sourcefile-module-registration.md) — P1 +- [T044: FunctionDeclaration default receiver](../tasks/T044-function-default-receiver.md) — P2 +- [T045: ModDeclarations render order dependency](../tasks/T045-mod-declarations-render-order.md) — P2 +- [T069: ImplBlock generic parameter forwarding](../tasks/T069-implblock-generic-forwarding.md) — P0 +- [T070: ImplBlock trait name missing use import](../tasks/T070-implblock-trait-use-import.md) — P0 +- [T071: Prelude type shadowing causes missing imports](../tasks/T071-prelude-type-shadowing.md) — P1 +- [T072: CrateDirectory Cargo.toml missing [lib] path](../tasks/T072-cargo-toml-lib-path.md) — P1 +- [T073: Reference inline rendering inserts unexpected line breaks](../tasks/T073-reference-inline-line-breaks.md) — P2 +- [T074: Cargo.toml renders empty [dependencies] section](../tasks/T074-empty-dependencies-section.md) — P3 + +## Sequencing Notes + +T039 and T040 are P0 and can be worked in parallel. T041–T043 are P1 and independent. T044–T045 are P2 improvements. + +## Completion Criteria + +- All 7 bug fix tasks completed and tested. +- Generated Rust output has correct formatting (newlines, indentation). +- Reference component works in all scope positions. +- Trait methods render correct abstract signatures. +- Enum tuple variants use correct syntax. +- `samples/rust-example/` can be updated to use Reference in all type positions. diff --git a/docs/backlog/epics/E008-expression-components.md b/docs/backlog/epics/E008-expression-components.md new file mode 100644 index 000000000..d6268dd39 --- /dev/null +++ b/docs/backlog/epics/E008-expression-components.md @@ -0,0 +1,99 @@ +# E008: Expression and Statement Components + +## Summary + +Add expression-level and statement-level components for generating Rust function bodies, control flow, and common expression patterns. Moves the package beyond declaration-only generation. + +## Why This Epic Exists + +The initial MVP only provides declaration-level components (structs, functions, traits, etc.). Function bodies must be written as raw `code` template literals. Analysis of `samples/rust-example/` found 24 raw code instances that could be replaced by dedicated components. TypeScript and C# Alloy packages already provide expression/statement components. + +## Goals + +- Provide struct literal expression components (covers the most common raw code pattern). +- Provide control flow components (match, if, for, while, loop). +- Provide expression components (function calls, closures, method chains). +- Provide utility expression components (return, break, continue, macros, try, await). +- Update the rust-example sample to demonstrate the new components. + +## In Scope + +- T046: StructExpression + FieldInit +- T047: MatchExpression + MatchArm +- T048: IfExpression + ElseIfClause + ElseClause +- T049: LetBinding +- T050: FunctionCallExpression +- T051: ClosureExpression +- T052: ReturnExpression + MacroCall +- T053: Update rust-example sample +- T055: ForExpression +- T056: WhileExpression + LoopExpression +- T057: BreakExpression + ContinueExpression +- T060: AwaitExpression +- T061: MethodChainExpression +- T064: TryExpression (? operator) +- T065: UnsafeBlock +- T067: BlockExpression + +## Out of Scope + +- Pattern matching DSL (destructuring patterns remain as raw strings). +- Async blocks. +- Full iterator adapter library. + +## Dependencies + +- E007 (bug fixes) — T039 (Reference scope traversal) unblocks using Reference in expressions. + +## What It Enables + +- Code generators can produce complete Rust function bodies with structured components. +- Composable expression trees instead of raw string templates. +- The sample project becomes a full showcase of the package's capabilities. + +## Risks / Notes + +- Expression components are pure rendering components — no symbol/scope integration initially. +- MatchExpression and IfExpression are the highest-value additions. +- StructExpression covers the most raw code instances (8 of 24). +- STC wrappers should be created for each new component. + +## Task List + +### Tier 1 — High Impact + +- [T046: StructExpression + FieldInit](../tasks/T046-struct-expression.md) — P1 +- [T047: MatchExpression + MatchArm](../tasks/T047-match-expression.md) — P1 +- [T048: IfExpression + ElseIfClause + ElseClause](../tasks/T048-if-expression.md) — P1 +- [T055: ForExpression](../tasks/T055-for-expression.md) — P1 + +### Tier 2 — Medium Impact + +- [T049: LetBinding](../tasks/T049-let-binding.md) — P2 +- [T050: FunctionCallExpression](../tasks/T050-function-call-expression.md) — P2 +- [T051: ClosureExpression](../tasks/T051-closure-expression.md) — P2 +- [T056: WhileExpression + LoopExpression](../tasks/T056-while-loop-expression.md) — P2 +- [T057: BreakExpression + ContinueExpression](../tasks/T057-break-continue.md) — P2 +- [T060: AwaitExpression](../tasks/T060-await-expression.md) — P2 +- [T061: MethodChainExpression](../tasks/T061-method-chain-expression.md) — P2 +- [T064: TryExpression](../tasks/T064-try-expression.md) — P2 + +### Tier 3 — Nice to Have + +- [T052: ReturnExpression + MacroCall](../tasks/T052-return-macro.md) — P3 +- [T065: UnsafeBlock](../tasks/T065-unsafe-block.md) — P3 +- [T067: BlockExpression](../tasks/T067-block-expression.md) — P3 + +### Integration + +- [T053: Update rust-example sample](../tasks/T053-update-rust-example.md) — P2 + +## Sequencing Notes + +Tier 1 tasks are independent and can be parallelized. Tier 2 tasks can also be parallelized. T057 depends on T055/T056. T061 depends on T050. T053 depends on all other tasks plus E007 bug fixes. + +## Completion Criteria + +- All expression/statement components implemented with tests. +- STC wrappers exported for each new component. +- `samples/rust-example/` updated to use new components with minimal raw code remaining. diff --git a/docs/backlog/epics/E009-language-feature-gaps.md b/docs/backlog/epics/E009-language-feature-gaps.md new file mode 100644 index 000000000..7aba9534f --- /dev/null +++ b/docs/backlog/epics/E009-language-feature-gaps.md @@ -0,0 +1,72 @@ +# E009: Language Feature Gaps + +## Summary + +Add declaration and type system components for Rust features not covered in the original MVP — lifetimes, tuple structs, statics, visibility modifiers, associated types, and inner attributes. + +## Why This Epic Exists + +A comprehensive audit of Rust language features against the existing component inventory revealed gaps in the type system and declaration components. These are not expression-level features (covered by E008) but structural language features needed for complete Rust code generation. + +## Goals + +- Add lifetime parameter support to generics. +- Support tuple struct and unit struct syntax. +- Add static item declarations. +- Complete visibility modifier support (`pub(super)`). +- Add associated type support in traits and impl blocks. +- Add inner attribute support (`#![...]`). + +## In Scope + +- T054: Lifetime parameter support +- T058: Tuple struct declaration +- T059: StaticDeclaration +- T062: pub(super) visibility +- T063: AssociatedType in traits +- T066: InnerAttribute (#![...]) + +## Out of Scope + +- `impl Trait` / `dyn Trait` syntax (can use raw strings). +- Destructuring patterns (requires pattern DSL infrastructure). +- `macro_rules!` definitions (too complex for code gen). +- Cargo workspace support. + +## Dependencies + +- E001–E006 (existing implementation). + +## What It Enables + +- Code generators can target lifetime-annotated APIs (common in Rust libraries). +- Newtype patterns via tuple structs. +- Complete visibility control. +- Traits with associated types (Iterator, Deref, etc.). + +## Risks / Notes + +- T054 (lifetimes) requires extending the TypeParameterProp interface — must be backward compatible. +- T058 (tuple structs) may extend StructDeclaration or create a new component — design decision needed. +- T062 (pub(super)) could be a simple prop addition or a visibility refactor. + +## Task List + +- [T054: Lifetime parameter support](../tasks/T054-lifetime-parameters.md) — P1 +- [T058: Tuple struct declaration](../tasks/T058-tuple-struct.md) — P1 +- [T059: StaticDeclaration](../tasks/T059-static-declaration.md) — P2 +- [T062: pub(super) visibility](../tasks/T062-pub-super-visibility.md) — P2 +- [T063: AssociatedType in traits](../tasks/T063-associated-type.md) — P2 +- [T066: InnerAttribute](../tasks/T066-inner-attribute.md) — P3 + +## Sequencing Notes + +T054 and T058 are P1 and independent — can be parallelized. T059, T062, T063 are P2 and independent. T066 is P3. + +## Completion Criteria + +- Lifetime parameters render correctly in all generic positions. +- Tuple structs and unit structs render with correct syntax. +- All Rust visibility modifiers supported. +- Associated types work in both trait declarations and impl blocks. +- Inner attributes render `#![...]` syntax. diff --git a/docs/backlog/index.md b/docs/backlog/index.md new file mode 100644 index 000000000..cf6ced092 --- /dev/null +++ b/docs/backlog/index.md @@ -0,0 +1,368 @@ +# @alloy-js/rust — Project Backlog + +## Purpose + +This backlog defines all work needed to implement `@alloy-js/rust`, a new Alloy language package for generating Rust source code. It is derived from the planning documents in [`docs/language-packages/rust/`](../language-packages/rust/). + +## How to Use This Backlog + +1. **Humans:** Read this index for a complete overview. Drill into epic and task docs for details. +2. **AI agents:** Read [`agents/execution-rules.md`](agents/execution-rules.md) first, then pick the next ready task from the task table below. +3. **Phases** show the recommended execution order. Complete one phase before starting the next. + +## Backlog File Organization + +``` +docs/backlog/ +├── index.md ← You are here +├── epics/ ← Epic-level documents (9 epics) +├── tasks/ ← Executable task documents (102 tasks) +├── phases/ ← Implementation phase documents (8 phases) +└── agents/ ← AI agent execution guidance +``` + +--- + +## Executive Summary + +### Major Workstreams + +| # | Workstream | Epic | Tasks | Phase | +| --- | ------------------------------------------------------- | ---- | ----------------------- | ------- | +| 1 | Package scaffold & test infra | E001 | T001–T002 | P01 | +| 2 | Symbol system (symbols, scopes, factories, name policy) | E002 | T003–T008 | P01 | +| 3 | Core declaration components | E003 | T009–T018 | P02 | +| 4 | Traits and impl blocks | E004 | T019–T021 | P03 | +| 5 | Module system and imports | E005 | T022–T027 | P04 | +| 6 | External deps, build file, polish | E006 | T028–T038 | P05–P06 | +| 7 | Bug fixes and rendering corrections | E007 | T039–T045 | P07 | +| 9 | Language feature gaps | E009 | T054–T066 (declaration) | P08 | + +### Recommended Implementation Order + +**P01 → P02 → P03 → P04 → P05 → P06** (strictly sequential phases; tasks within a phase may parallelize). + +### Key Risks + +1. **Scope hierarchy design** (T005) is hard to change later — requires human architecture review. +2. **Reference resolution** (T022) is the most complex task — use path construction from `ResolutionResult` is error-prone. +3. **Impl blocks** (T020) are architecturally novel for Alloy — no existing package has an analog. +4. **`mod` auto-generation** (T025) must handle visibility and nesting correctly. +5. **`r#` reserved word handling** (T007) is unique to Rust — other packages use `_` suffix. + +### Assumptions / Gaps / Open Questions + +1. **Prelude handling:** Should `Option`, `Result`, `Vec`, `String` skip `use` generation? _Recommendation: Yes._ Must resolve before T022. +2. **Self receiver default:** Should methods in impl blocks auto-get `&self`? _Recommendation: Yes, with `receiver="none"` opt-out._ Must resolve before T021. +3. **`mod` visibility:** Should `mod` declarations default to `pub mod` or `mod`? _Recommendation: Explicit prop._ Must resolve before T025. +4. **Crate type:** Support both `lib.rs` and `main.rs`? _Recommendation: Yes, via `crateType` prop._ Must resolve before T009. +5. **`use` tree syntax:** Group `use path::{A, B};` or flatten? _Recommendation: Group (more idiomatic)._ Must resolve before T023. + +--- + +## Epic Overview + +| Epic | Title | Tasks | Dependencies | Phase | +| ------------------------------------------------ | ----------------------------------- | ----------------------- | ------------ | ------- | +| [E001](epics/E001-package-foundation.md) | Package Foundation | T001–T002 | None | P01 | +| [E002](epics/E002-symbol-system.md) | Symbol System | T003–T008 | E001 | P01 | +| [E003](epics/E003-core-components.md) | Core Declaration Components | T009–T018 | E001, E002 | P02 | +| [E004](epics/E004-traits-and-impl.md) | Traits and Impl Blocks | T019–T021 | E002, E003 | P03 | +| [E005](epics/E005-module-system-imports.md) | Module System and Imports | T022–T027 | E002, E003 | P04 | +| [E006](epics/E006-external-deps-build-polish.md) | External Deps, Build, Polish | T028–T038 | E001–E005 | P05–P06 | +| [E007](epics/E007-bug-fixes.md) | Bug Fixes and Rendering Corrections | T039–T045 | E001–E006 | P07 | +| [E009](epics/E009-language-feature-gaps.md) | Language Feature Gaps | T054–T066 (declaration) | E001–E006 | P08 | + +--- + +## Recommended Delivery Phases + +| Phase | Title | Epics | Tasks | Goal | +| ---------------------------------------- | --------------------- | -------------- | --------- | ---------------------------------------------------- | +| [P01](phases/P01-foundation.md) | Foundation | E001, E002 | T001–T008 | Package scaffold, symbols, scopes, name policy | +| [P02](phases/P02-core-components.md) | Core Components | E003 | T009–T018 | Single-file Rust declaration rendering | +| [P03](phases/P03-traits-impl.md) | Traits & Impl | E004 | T019–T021 | Traits and impl blocks | +| [P04](phases/P04-module-system.md) | Module System | E005 | T022–T027 | Multi-module crates with `use` and `mod` | +| [P05](phases/P05-external-deps-build.md) | External Deps & Build | E006 (partial) | T028–T031 | External crates and Cargo.toml | +| [P06](phases/P06-polish.md) | Polish | E006 (partial) | T032–T038 | STC wrappers, exports, golden tests | +| P07 | Bug Fixes | E007 | T039–T045 | Fix rendering bugs discovered in integration testing | + +--- + +## Task Summary Table + +| ID | Title | Epic | Type | Priority | Deps | Status | +| --------------------------------------------------------------- | --------------------------------------------------------------- | ---- | ----------- | -------- | ---------------------------- | ------- | +| [T001](tasks/T001-package-scaffold.md) | Package scaffold | E001 | foundation | P0 | — | done | +| [T002](tasks/T002-test-infrastructure.md) | Test infrastructure | E001 | foundation | P0 | T001 | done | +| [T003](tasks/T003-rust-output-symbol.md) | RustOutputSymbol base class | E002 | foundation | P0 | T001 | done | +| [T004](tasks/T004-symbol-subclasses.md) | NamedTypeSymbol + FunctionSymbol | E002 | foundation | P0 | T003 | done | +| [T005](tasks/T005-scope-hierarchy.md) | Scope hierarchy part 1 (module/crate) | E002 | foundation | P0 | T003, T004 | done | +| [T005b](tasks/T005b-scope-hierarchy-part2.md) | Scope hierarchy part 2 (function/lexical/member) | E002 | foundation | P0 | T005 | done | +| [T006](tasks/T006-symbol-factories.md) | Symbol factory functions | E002 | foundation | P0 | T003–T005 | done | +| [T007](tasks/T007-name-policy.md) | Name policy | E002 | feature | P0 | T001 | done | +| [T007b](tasks/T007b-name-conflict-resolver.md) | Name conflict resolver (local declarations win; rename imports) | E002 | feature | P1 | T003 | done | +| [T008](tasks/T008-parameter-descriptor.md) | Parameter descriptor + hooks | E002 | foundation | P1 | T001 | done | +| [T009](tasks/T009-source-file-crate-directory.md) | SourceFile + CrateDirectory | E003 | feature | P0 | T005, T007 | done | +| [T010](tasks/T010-declaration-reference.md) | Declaration + Reference basics | E003 | feature | P0 | T006, T009 | done | +| [T011](tasks/T011-struct-declaration.md) | StructDeclaration + Field | E003 | feature | P0 | T006, T010 | done | +| [T012](tasks/T012-enum-declaration.md) | EnumDeclaration + EnumVariant | E003 | feature | P0 | T006, T010, T015–T017 | done | +| [T013](tasks/T013-function-declaration.md) | FunctionDeclaration + Parameters | E003 | feature | P0 | T006, T008, T010, T017 | done | +| [T014](tasks/T014-type-alias-const.md) | TypeAlias + ConstDeclaration | E003 | feature | P1 | T006, T010 | done | +| [T015](tasks/T015-attributes.md) | Attribute + DeriveAttribute | E003 | feature | P0 | T010 | done | +| [T016](tasks/T016-doc-comments.md) | DocComment + ModuleDocComment | E003 | feature | P1 | T001 | done | +| [T017](tasks/T017-type-parameters.md) | TypeParameters + WhereClause | E003 | feature | P0 | T001 | done | +| [T018](tasks/T018-value-component.md) | Value component | E003 | feature | P2 | T001 | done | +| [T019](tasks/T019-trait-declaration.md) | TraitDeclaration | E004 | feature | P0 | T006, T010, T013, T017 | done | +| [T020](tasks/T020-impl-block.md) | ImplBlock | E004 | feature | P0 | T006, T010, T013, T017, T019 | done | +| [T021](tasks/T021-self-receiver.md) | Self receiver in FunctionDeclaration | E004 | feature | P0 | T013, T020 | done | +| [T022](tasks/T022-reference-resolution.md) | Reference resolution + use tracking | E005 | feature | P0 | T005, T010 | done | +| [T023](tasks/T023-use-statements.md) | UseStatement + UseStatements | E005 | feature | P0 | T022 | done | +| [T024](tasks/T024-module-directory.md) | ModuleDirectory | E005 | feature | P0 | T009, T005 | done | +| [T025](tasks/T025-mod-declarations.md) | Auto mod declarations | E005 | feature | P0 | T009, T024 | done | +| [T026](tasks/T026-import-integration-tests.md) | Import integration tests | E005 | test | P0 | T022, T023, T025 | done | +| [T027](tasks/T027-module-structure-tests.md) | Module structure tests | E005 | test | P0 | T024–T026 | done | +| [T028](tasks/T028-create-crate.md) | createCrate() factory | E006 | feature | P0 | T003, T005, T006 | done | +| [T029](tasks/T029-std-builtins.md) | std builtin descriptors | E006 | feature | P1 | T028 | blocked | +| [T030](tasks/T030-cargo-toml.md) | CargoTomlFile component | E006 | feature | P0 | T009, T031 | done | +| [T031](tasks/T031-dependency-tracking.md) | External crate dependency tracking | E006 | feature | P0 | T005, T022, T028 | done | +| [T032](tasks/T032-stc-wrappers.md) | STC wrappers | E006 | feature | P2 | T011–T021 | done | +| [T033](tasks/T033-barrel-exports.md) | Barrel exports verification | E006 | infra | P1 | T032 | done | +| [T034](tasks/T034-golden-scenarios.md) | Golden scenario tests | E006 | test | P0 | T026–T027, T030–T031 | done | +| [T035](tasks/T035-edge-cases.md) | Edge case tests | E006 | test | P1 | T011–T025 | done | +| [T036](tasks/T036-builtin-crate-support.md) | Builtin crate support in createCrate/ref | E006 | feature | P0 | T028 | done | +| [T037](tasks/T037-complete-stc-wrappers.md) | Complete STC wrappers | E006 | feature | P2 | T032 | done | +| [T038](tasks/T038-crate-type-prop.md) | CrateDirectory crateType prop | E006 | feature | P3 | T009 | done | +| [T039](tasks/T039-reference-scope-traversal.md) | Reference component scope traversal | E007 | bug | P0 | T010, T022 | done | +| [T040](tasks/T040-missing-newlines-between-items.md) | Missing newlines between sibling items | E007 | bug | P0 | T011, T012 | done | +| [T041](tasks/T041-trait-abstract-methods.md) | Trait methods should render as abstract signatures | E007 | bug | P1 | T013, T019 | done | +| [T042](tasks/T042-enum-tuple-variants.md) | Enum tuple variant support | E007 | bug | P1 | T012 | done | +| [T043](tasks/T043-standalone-sourcefile-module-registration.md) | Standalone SourceFile module registration | E007 | bug | P1 | T009, T025 | done | +| [T044](tasks/T044-function-default-receiver.md) | FunctionDeclaration default receiver in impl blocks | E007 | improvement | P2 | T013, T021 | done | +| [T045](tasks/T045-mod-declarations-render-order.md) | ModDeclarations render order dependency | E007 | improvement | P2 | T025, T009 | done | +| [T046](tasks/T046-struct-expression.md) | StructExpression + FieldInit | E008 | feature | P1 | T009 | done | +| [T047](tasks/T047-match-expression.md) | MatchExpression + MatchArm | E008 | feature | P1 | T009 | done | +| [T048](tasks/T048-if-expression.md) | IfExpression + ElseIfClause + ElseClause | E008 | feature | P1 | T009 | done | +| [T049](tasks/T049-let-binding.md) | LetBinding | E008 | feature | P2 | T009 | done | +| [T050](tasks/T050-function-call-expression.md) | FunctionCallExpression | E008 | feature | P2 | T009 | done | +| [T051](tasks/T051-closure-expression.md) | ClosureExpression | E008 | feature | P2 | T009 | done | +| [T052](tasks/T052-return-macro.md) | ReturnExpression + MacroCall | E008 | feature | P3 | T009 | done | +| [T053](tasks/T053-update-rust-example.md) | Update rust-example with expression components | E008 | test | P2 | T039–T052 | done | +| [T054](tasks/T054-lifetime-parameters.md) | Lifetime parameter support | E009 | feature | P1 | T017 | done | +| [T055](tasks/T055-for-expression.md) | ForExpression | E008 | feature | P1 | T009 | done | +| [T056](tasks/T056-while-loop-expression.md) | WhileExpression + LoopExpression | E008 | feature | P2 | T009 | done | +| [T057](tasks/T057-break-continue.md) | BreakExpression + ContinueExpression | E008 | feature | P2 | T055, T056 | done | +| [T058](tasks/T058-tuple-struct.md) | Tuple struct declaration | E009 | feature | P1 | T011 | done | +| [T059](tasks/T059-static-declaration.md) | StaticDeclaration | E009 | feature | P2 | T014 | done | +| [T060](tasks/T060-await-expression.md) | AwaitExpression | E008 | feature | P2 | T009 | done | +| [T061](tasks/T061-method-chain-expression.md) | MethodChainExpression | E008 | feature | P2 | T050 | done | +| [T062](tasks/T062-pub-super-visibility.md) | pub(super) visibility | E009 | feature | P2 | T011 | done | +| [T063](tasks/T063-associated-type.md) | AssociatedType in traits | E009 | feature | P2 | T019 | done | +| [T064](tasks/T064-try-expression.md) | TryExpression (? operator) | E008 | feature | P2 | T009 | done | +| [T065](tasks/T065-unsafe-block.md) | UnsafeBlock | E008 | feature | P3 | T009 | done | +| [T066](tasks/T066-inner-attribute.md) | InnerAttribute (#![...]) | E009 | feature | P3 | T015 | done | +| [T067](tasks/T067-block-expression.md) | BlockExpression | E008 | feature | P3 | T009 | done | +| [T068](tasks/T068-rust-example-externals-ts2742.md) | Fix rust-example externals TS2742 build failure | E007 | bug | P1 | T053 | done | +| [T069](tasks/T069-implblock-generic-forwarding.md) | ImplBlock generic parameter forwarding | E007 | bug | P0 | T020, T017 | done | +| [T070](tasks/T070-implblock-trait-use-import.md) | ImplBlock trait name missing use import | E007 | bug | P0 | T020, T022 | done | +| [T071](tasks/T071-prelude-type-shadowing.md) | Prelude type shadowing causes missing imports | E007 | bug | P1 | T022 | done | +| [T072](tasks/T072-cargo-toml-lib-path.md) | CrateDirectory Cargo.toml missing [lib] path | E007 | bug | P1 | T030, T038 | done | +| [T073](tasks/T073-reference-inline-line-breaks.md) | Reference inline rendering inserts unexpected line breaks | E007 | bug | P2 | T010, T022 | done | +| [T074](tasks/T074-empty-dependencies-section.md) | Cargo.toml renders empty [dependencies] section | E007 | bug | P3 | T030, T031 | done | + +--- + +## Dependency Highlights + +### Critical Path + +``` +T001 → T003 → T004 → T005 → T005b → T006 → T010 → T011 (struct rendering) + ↘ T009 ↗ + +T010 → T022 → T023 → T025 → T026 → T027 (module system) + ↘ T024 ↗ + +T022 + T028 → T031 → T030 → T034 (external deps + golden tests) +``` + +### Parallel Opportunities + +**After T001 completes** (parallel-safe): + +- T003 (RustOutputSymbol) +- T007 (name policy) +- T008 (parameter descriptor) +- T016 (doc comments) +- T017 (type parameters) +- T018 (value component) + +**After T010 completes** (parallel-safe): + +- T011 (struct), T012 (enum), T013 (function), T014 (type alias/const), T015 (attributes) + +**After E003 + E004 complete** (parallel-safe): + +- T032 (STC), T035 (edge cases) + +--- + +## Ready-Now Tasks + +These tasks have **no dependencies** and can start immediately: + +| ID | Title | Phase | +| -------- | ---------------- | ----- | +| **T001** | Package scaffold | P01 | + +These pending tasks depend **only on T001** and are ready once T001 is complete: + +| ID | Title | Phase | +| ---- | -------------------- | ----- | +| T008 | Parameter descriptor | P01 | +| T016 | DocComment | P02 | +| T018 | Value component | P02 | + +--- + +## Blocked Tasks + +| ID | Title | Reason | +| ---- | --------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| T029 | std builtin descriptors | Repeated build validation failures on exported `std` typing portability (TS2742/API Extractor). | +| T071 | Prelude type shadowing causes missing imports | Fixed: `ref()` now checks if prelude-named symbol is same-crate before skipping import. | + +--- + +## Open Bug Fixes (Post-MVP) + +These bugs were discovered during integration testing with `samples/rust-example/`: + +| ID | Title | Priority | Impact | +| -------- | ----------------------------------------- | -------- | ----------------------------------------------------------------------------------------- | +| **T040** | Missing newlines between items | P0 | All generated enum/struct output lacks line breaks | +| **T041** | Trait abstract method signatures | P1 | Trait methods render `{}` instead of `;` | +| **T042** | Enum tuple variant support | P1 | Tuple variants render as struct variants | +| **T043** | Standalone SourceFile module registration | P1 | Single-file modules missing from mod declarations | +| **T044** | FunctionDeclaration default receiver | P2 | ✓ Completed — no code changes required; behavior already implemented and tested | +| **T045** | ModDeclarations render order | P2 | ✓ Completed — `ModDeclarations` now reactively re-renders as child modules are registered | + +--- + +## Open Feature Requests — Expression & Statement Components + +These components were identified by analyzing raw `code` template usage in `samples/rust-example/`. TypeScript and C# packages already provide equivalent components. + +### Tier 1 — High Impact (eliminates ~12/24 raw code instances) + +| ID | Title | Priority | Covers | +| -------- | ---------------------------- | -------- | ----------------------------------------- | +| **T046** | StructExpression + FieldInit | P1 | Self/struct literals (8 instances) | +| **T047** | MatchExpression + MatchArm | P1 | Pattern matching (2 instances, core Rust) | + +### Tier 2 — Medium Impact + +| ID | Title | Priority | Covers | +| -------- | ----------------- | -------- | ----------------------------------- | +| **T049** | LetBinding | P2 | Variable declarations (2 instances) | +| **T051** | ClosureExpression | P2 | Closure expressions (1 instance) | + +### Tier 3 — Nice to Have + +| ID | Title | Priority | Covers | +| -------- | ---------------------------- | -------- | ----------------------------- | +| **T052** | ReturnExpression + MacroCall | P3 | Returns, macros (3 instances) | + +### Integration + +| ID | Title | Priority | Deps | +| -------- | -------------------------- | -------- | ----------------------------------------------------------------- | +| **T053** | Update rust-example sample | P2 | All bug fixes (T039–T045) + all expression components (T046–T052) | + +--- + +## Open Feature Requests — Language Feature Gaps + +Discovered by auditing the full Rust language against existing components. + +### Declaration & Type System + +| ID | Title | Priority | Rust Feature | +| -------- | -------------------------- | -------- | ---------------------------------------------- | +| **T054** | Lifetime parameter support | P1 | `'a` lifetimes in generics — core Rust feature | +| **T058** | Tuple struct declaration | P1 | `struct Point(i32, i32)` — common pattern | +| **T059** | StaticDeclaration | P2 | `static` / `static mut` items | +| **T062** | pub(super) visibility | P2 | Complete visibility modifier support | +| **T063** | AssociatedType in traits | P2 | `type Item;` / `type Item = T;` | + +### Control Flow + +| ID | Title | Priority | Rust Feature | +| -------- | ------------------------------------ | -------- | --------------------------------------- | +| **T055** | ForExpression | P1 | `for x in iter { }` — most common loop | +| **T056** | WhileExpression + LoopExpression | P2 | `while` / `loop` | +| **T057** | BreakExpression + ContinueExpression | P2 | `break` / `continue` with labels/values | + +### Expression Components + +| ID | Title | Priority | Rust Feature | +| -------- | -------------------------- | -------- | -------------------------------------- | +| **T060** | AwaitExpression | P2 | `.await` postfix syntax | +| **T061** | MethodChainExpression | P2 | `.iter().filter().map().collect()` | +| **T064** | TryExpression (? operator) | P2 | Error propagation with `?` | +| **T065** | UnsafeBlock | P3 | `unsafe { }` blocks | +| **T066** | InnerAttribute | P3 | `#![...]` crate/module attributes | +| **T067** | BlockExpression | P3 | Expression blocks `{ let x = ...; x }` | + +--- + +## High-Priority Tasks + +These P0 tasks are on the critical path and should be prioritized: + +| ID | Title | Why Critical | +| ---- | --------------------------- | -------------------------------------------- | +| T001 | Package scaffold | Blocks everything | +| T003 | RustOutputSymbol | Blocks all symbol/scope work | +| T005 | Scope hierarchy | Architecture decision — hard to change later | +| T009 | SourceFile + CrateDirectory | Blocks all component work | +| T010 | Declaration + Reference | Blocks all declaration components | +| T022 | Reference resolution | Blocks all import/module work | +| T034 | Golden scenario tests | Validates complete MVP | + +--- + +## Links to All Documents + +### Epics + +- [E001: Package Foundation](epics/E001-package-foundation.md) +- [E002: Symbol System](epics/E002-symbol-system.md) +- [E003: Core Declaration Components](epics/E003-core-components.md) +- [E004: Traits and Impl Blocks](epics/E004-traits-and-impl.md) +- [E005: Module System and Imports](epics/E005-module-system-imports.md) +- [E006: External Deps, Build File, and Polish](epics/E006-external-deps-build-polish.md) +- [E007: Bug Fixes and Rendering Corrections](epics/E007-bug-fixes.md) +- [E008: Expression and Statement Components](epics/E008-expression-components.md) +- [E009: Language Feature Gaps](epics/E009-language-feature-gaps.md) + +### Phases + +- [P01: Foundation](phases/P01-foundation.md) +- [P02: Core Components](phases/P02-core-components.md) +- [P03: Traits & Impl](phases/P03-traits-impl.md) +- [P04: Module System](phases/P04-module-system.md) +- [P05: External Deps & Build](phases/P05-external-deps-build.md) +- [P06: Polish](phases/P06-polish.md) +- P07: Bug Fixes (T039–T045) + +### Agent Guidance + +- [AI Agent Execution Rules](agents/execution-rules.md) + +### Source Documents + +- [01: Core Understanding](../language-packages/rust/01-core-understanding.md) +- [02: Existing Language Patterns](../language-packages/rust/02-existing-language-patterns.md) +- [03: Rust Design Notes](../language-packages/rust/03-rust-design-notes.md) +- [04: Rust PRD](../language-packages/rust/04-rust-prd.md) diff --git a/docs/backlog/phases/P01-foundation.md b/docs/backlog/phases/P01-foundation.md new file mode 100644 index 000000000..23954a1d2 --- /dev/null +++ b/docs/backlog/phases/P01-foundation.md @@ -0,0 +1,41 @@ +# P01: Foundation + +## Goal + +Establish the package scaffold, symbol system, scope hierarchy, and name policy — the foundation everything else builds on. + +## Why This Phase Exists + +No components can be built without symbols and scopes. No tests can run without test infrastructure. This phase is 100% foundation work. + +## Included Epics + +- [E001: Package Foundation](../epics/E001-package-foundation.md) +- [E002: Symbol System](../epics/E002-symbol-system.md) (all tasks) + +## Included Tasks + +| ID | Title | Type | +| ----- | ------------------------------------------------ | ---------- | +| T001 | Package scaffold | foundation | +| T002 | Test infrastructure | foundation | +| T003 | RustOutputSymbol base class | foundation | +| T004 | NamedTypeSymbol and FunctionSymbol | foundation | +| T005 | Scope hierarchy Part 1 (crate/module) | foundation | +| T005b | Scope hierarchy Part 2 (function/lexical/member) | foundation | +| T006 | Symbol factory functions | foundation | +| T007 | Name policy | feature | +| T007b | Name conflict resolver | feature | +| T008 | Parameter descriptor and scope hooks | foundation | + +## Exit Criteria + +- Package builds and tests run. +- All symbol classes, scope classes, and factories compile. +- Name policy tests pass. +- `import * as rust from "@alloy-js/rust"` works. + +## Risks + +- Scope hierarchy design is hard to change later. Review T005 carefully. +- Name policy reserved word handling (`r#`) is novel — test thoroughly. diff --git a/docs/backlog/phases/P02-core-components.md b/docs/backlog/phases/P02-core-components.md new file mode 100644 index 000000000..e58a5f65f --- /dev/null +++ b/docs/backlog/phases/P02-core-components.md @@ -0,0 +1,38 @@ +# P02: Core Components + +## Goal + +Implement all basic Rust declaration components so single-file Rust code can be generated. + +## Why This Phase Exists + +After foundation, users need renderable components. This phase delivers the first visible output. + +## Included Epics + +- [E003: Core Declaration Components](../epics/E003-core-components.md) (all tasks) + +## Included Tasks + +| ID | Title | Type | +| ---- | ---------------------------------- | ------- | +| T009 | SourceFile and CrateDirectory | feature | +| T010 | Declaration and Reference basics | feature | +| T011 | StructDeclaration and Field | feature | +| T012 | EnumDeclaration and EnumVariant | feature | +| T013 | FunctionDeclaration and Parameters | feature | +| T014 | TypeAlias and ConstDeclaration | feature | +| T015 | Attribute and DeriveAttribute | feature | +| T016 | DocComment and ModuleDocComment | feature | +| T017 | TypeParameters and WhereClause | feature | +| T018 | Value component | feature | + +## Exit Criteria + +- `struct.test.tsx`, `enum.test.tsx`, `function.test.tsx`, `attributes.test.tsx` pass. +- Single-file Rust output is syntactically correct. +- All components render with correct visibility, derives, doc comments, type parameters. + +## Risks + +- SourceFile stub (no imports yet) must be designed to easily add import rendering in P04. diff --git a/docs/backlog/phases/P03-traits-impl.md b/docs/backlog/phases/P03-traits-impl.md new file mode 100644 index 000000000..e898b13ea --- /dev/null +++ b/docs/backlog/phases/P03-traits-impl.md @@ -0,0 +1,32 @@ +# P03: Traits and Impl Blocks + +## Goal + +Add Rust trait declarations and impl blocks — the novel construct with no direct analog in other Alloy packages. + +## Why This Phase Exists + +Traits and impls are core to Rust's type system. They're architecturally novel for Alloy and need focused attention. + +## Included Epics + +- [E004: Traits and Impl Blocks](../epics/E004-traits-and-impl.md) (all tasks) + +## Included Tasks + +| ID | Title | Type | +| ---- | ------------------------------------ | ------- | +| T019 | TraitDeclaration | feature | +| T020 | ImplBlock | feature | +| T021 | Self receiver in FunctionDeclaration | feature | + +## Exit Criteria + +- `trait.test.tsx` and `impl.test.tsx` pass. +- Traits with methods and default impls render correctly. +- Both inherent and trait impl blocks render correctly. +- Methods inside impl blocks auto-get `&self` receiver. + +## Risks + +- Impl blocks adding members to existing types is unprecedented in Alloy. Test carefully for symbol conflicts. diff --git a/docs/backlog/phases/P04-module-system.md b/docs/backlog/phases/P04-module-system.md new file mode 100644 index 000000000..79bcbbdad --- /dev/null +++ b/docs/backlog/phases/P04-module-system.md @@ -0,0 +1,36 @@ +# P04: Module System and Imports + +## Goal + +Enable multi-module Rust crates with automatic `use` statement and `mod` declaration generation. + +## Why This Phase Exists + +This is the most complex phase. Rust's module system with `mod` declarations and `use` paths has no direct analog. This enables multi-file code generation. + +## Included Epics + +- [E005: Module System and Imports](../epics/E005-module-system-imports.md) (all tasks) + +## Included Tasks + +| ID | Title | Type | +| ---- | -------------------------------------- | ------- | +| T022 | Reference resolution with use tracking | feature | +| T023 | UseStatement and UseStatements | feature | +| T024 | ModuleDirectory component | feature | +| T025 | Auto mod declaration generation | feature | +| T026 | Cross-module import integration tests | test | +| T027 | Module structure integration tests | test | + +## Exit Criteria + +- `imports.test.tsx`, `reference.test.tsx`, `module-structure.test.tsx` pass. +- Cross-module references auto-generate `use crate::path::Symbol;`. +- `mod` declarations auto-generated in parent modules. +- Import sorting follows Rust convention (std → external → crate). + +## Risks + +- Use path construction from ResolutionResult is error-prone. Test every scenario. +- Prelude handling could miss edge cases. diff --git a/docs/backlog/phases/P05-external-deps-build.md b/docs/backlog/phases/P05-external-deps-build.md new file mode 100644 index 000000000..0f2eea572 --- /dev/null +++ b/docs/backlog/phases/P05-external-deps-build.md @@ -0,0 +1,32 @@ +# P05: External Dependencies and Build File + +## Goal + +Support external crate references and Cargo.toml generation. + +## Why This Phase Exists + +Real-world Rust code uses external crates (serde, tokio, etc.). Without this, generated code can't reference third-party libraries. + +## Included Epics + +- [E006: External Dependencies, Build File, and Polish](../epics/E006-external-deps-build-polish.md) (T028–T031) + +## Included Tasks + +| ID | Title | Type | +| ---- | ---------------------------------- | ------- | +| T028 | createCrate() factory | feature | +| T029 | std builtin descriptors | feature | +| T030 | CargoTomlFile component | feature | +| T031 | External crate dependency tracking | feature | + +## Exit Criteria + +- `cargo-toml.test.tsx` passes. +- External crate references generate `use` + Cargo.toml dependency. +- std builtin types available. + +## Risks + +- SymbolCreator protocol is complex. Study existing implementations carefully. diff --git a/docs/backlog/phases/P06-polish.md b/docs/backlog/phases/P06-polish.md new file mode 100644 index 000000000..f08406f01 --- /dev/null +++ b/docs/backlog/phases/P06-polish.md @@ -0,0 +1,34 @@ +# P06: Polish and Integration + +## Goal + +Complete STC wrappers, verify exports, pass all golden scenarios, and handle edge cases. + +## Why This Phase Exists + +Final quality pass before MVP is declared complete. + +## Included Epics + +- [E006: External Dependencies, Build File, and Polish](../epics/E006-external-deps-build-polish.md) (T032–T035) + +## Included Tasks + +| ID | Title | Type | +| ---- | --------------------------- | ------- | +| T032 | STC wrappers | feature | +| T033 | Barrel exports verification | infra | +| T034 | Golden scenario tests | test | +| T035 | Edge case tests | test | + +## Exit Criteria + +- All golden scenarios from PRD section 7 pass. +- STC wrappers exist for all key components. +- All barrel exports verified complete. +- Full test suite green. +- All 16 acceptance criteria from PRD section 13 met. + +## Risks + +- Golden scenarios may reveal integration issues missed in earlier phases. diff --git a/docs/backlog/tasks/T001-package-scaffold.md b/docs/backlog/tasks/T001-package-scaffold.md new file mode 100644 index 000000000..47d364ef8 --- /dev/null +++ b/docs/backlog/tasks/T001-package-scaffold.md @@ -0,0 +1,89 @@ +# T001: Package Scaffold + +| Field | Value | +| ------------------------- | --------------------------------------------------------------- | +| **ID** | T001 | +| **Epic** | [E001: Package Foundation](../epics/E001-package-foundation.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes (initial package setup) | +| **Dependencies** | None | +| **Blocks** | T002, all subsequent tasks | + +## Description + +Create the `@alloy-js/rust` package directory structure with build configuration files. + +## Goal + +Establish the package scaffold so that subsequent tasks can add source files and tests. + +## Scope Included + +- Create `packages/rust/` directory. +- Create `packages/rust/package.json` with: + - `name: "@alloy-js/rust"` + - `version: "0.1.0"` + - Dependencies on `@alloy-js/core` + - Scripts: `build`, `test`, `clean` + - Exports configuration matching other language packages +- Create `packages/rust/tsconfig.json` extending `../../tsconfig.base.json`. +- Create `packages/rust/vitest.config.ts` matching other language packages. +- Create `packages/rust/src/index.ts` as empty barrel export file. +- Ensure the package is included in `pnpm-workspace.yaml` (if not already via glob). +- Create `packages/rust/api-extractor.json` extending `../../api-extractor.base.json`. +- Ensure build script uses `alloy build --with-dev && pnpm run generate-docs`. +- Include `prepack` script: `"prepack": "node ../../scripts/strip-dev-exports.js"`. +- Include `#imports` hash pattern in package.json matching Go's pattern. +- Include `generate-docs` script: `"generate-docs": "api-extractor run"`. + +## Out of Scope + +- Test utilities (T002). +- Any source code beyond the empty barrel export. + +## Context Files to Read First + +- `packages/go/package.json` — reference for package.json structure. +- `packages/go/tsconfig.json` — reference for tsconfig. +- `packages/go/vitest.config.ts` — reference for vitest config. +- `packages/go/src/index.ts` — reference for barrel export pattern. +- `pnpm-workspace.yaml` — verify package discovery. +- `tsconfig.base.json` — base config being extended. +- `packages/go/api-extractor.json` — reference for api-extractor config. +- `api-extractor.base.json` — base api-extractor config. + +## Implementation Guidance + +1. Copy `packages/go/package.json` as starting point. Change name to `@alloy-js/rust`, update description. +2. Copy `packages/go/tsconfig.json`. Adjust references if needed. +3. Copy `packages/go/vitest.config.ts`. +4. Create `src/index.ts` with a comment: `// @alloy-js/rust barrel export`. +5. Verify `pnpm-workspace.yaml` includes `packages/*` (it likely already does). +6. Run `pnpm install` to link the new package. + +## Acceptance Criteria + +- `packages/rust/package.json` exists with correct name and dependencies. +- `packages/rust/tsconfig.json` exists and extends base config. +- `packages/rust/vitest.config.ts` exists. +- `packages/rust/src/index.ts` exists. +- `pnpm install` succeeds. +- `pnpm --filter @alloy-js/rust build` succeeds (empty build). + +## Definition of Done + +Package scaffold exists and builds without errors. + +## Validation Approach + +```bash +cd packages/rust && pnpm build && pnpm test +``` + +## Risks / Notes + +- Check that the exports field in package.json matches the pattern used by other packages (especially the `"source"`, `"development"`, and `"import"` conditions). diff --git a/docs/backlog/tasks/T002-test-infrastructure.md b/docs/backlog/tasks/T002-test-infrastructure.md new file mode 100644 index 000000000..b066feb65 --- /dev/null +++ b/docs/backlog/tasks/T002-test-infrastructure.md @@ -0,0 +1,67 @@ +# T002: Test Infrastructure + +| Field | Value | +| ------------------------- | --------------------------------------------------------------- | +| **ID** | T002 | +| **Epic** | [E001: Package Foundation](../epics/E001-package-foundation.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T001 | +| **Blocks** | All test tasks | + +## Description + +Create test utilities and setup for the Rust language package. + +## Goal + +Provide a `toSourceText()` helper and vitest setup so that all subsequent component tests can use them. + +## Scope Included + +- Create `packages/rust/test/vitest.setup.ts` importing `@alloy-js/core/testing`. +- Create `packages/rust/test/utils.tsx` with: + - `toSourceText(children, options?)` — wraps children in `Output` + `SourceFile` and renders to string. + - `toSourceTextMultiple(children)` — multi-file variant returning output directory. + - `findFile(output, path)` — extracts a file from output. + - `assertFileContents(output, expected)` — batch file validation. + +## Out of Scope + +- Actual test files (created in later tasks). + +## Context Files to Read First + +- `packages/go/test/utils.tsx` — reference implementation for Go. +- `packages/typescript/test/utils.tsx` — reference for TypeScript. +- `packages/core/testing/index.ts` — available test utilities. +- `packages/core/testing/extend-expect.ts` — custom vitest matchers. + +## Implementation Guidance + +1. Create `test/vitest.setup.ts`: + ```typescript + import "@alloy-js/core/testing"; + ``` +2. Create `test/utils.tsx` following the Go package pattern. The `toSourceText` function should wrap children in minimal Rust context (Output + a basic SourceFile). Initially, since `SourceFile` and `CrateDirectory` don't exist yet, use core components directly with placeholder values. This file will be updated in T009 when Rust-specific components are available. +3. Update `vitest.config.ts` to reference the setup file. + +## Acceptance Criteria + +- `test/vitest.setup.ts` exists and imports core testing. +- `test/utils.tsx` exports `toSourceText`, `toSourceTextMultiple`, `findFile`, `assertFileContents`. +- Running `pnpm test` in the package succeeds (no tests yet, but setup loads). + +## Definition of Done + +Test infrastructure is in place. Subsequent tasks can write tests using `toSourceText()`. + +## Validation Approach + +```bash +cd packages/rust && pnpm test -- --run +``` diff --git a/docs/backlog/tasks/T003-rust-output-symbol.md b/docs/backlog/tasks/T003-rust-output-symbol.md new file mode 100644 index 000000000..1d9f7347f --- /dev/null +++ b/docs/backlog/tasks/T003-rust-output-symbol.md @@ -0,0 +1,82 @@ +# T003: RustOutputSymbol Base Class + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T003 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T001 | +| **Blocks** | T004, T005, T006 | + +## Description + +Implement the base `RustOutputSymbol` class extending core's `OutputSymbol` with Rust-specific properties. + +## Goal + +Provide the base symbol class that all Rust symbols will inherit from. + +## Scope Included + +- Create `packages/rust/src/symbols/rust-output-symbol.ts`. +- `RustOutputSymbol` extends `OutputSymbol` from `@alloy-js/core`. +- Properties: + - `visibility: RustVisibility` — `"pub" | "pub(crate)" | "pub(super)" | undefined` (undefined = private). Reactive. + - `symbolKind: RustSymbolKind` — string literal union of Rust symbol kinds. Reactive. + - `isAsync: boolean` — default false. Reactive. + - `isUnsafe: boolean` — default false. Reactive. + - `isConst: boolean` — default false. Reactive. +- `static readonly memberSpaces = ["members"]`. +- `copy()` method following the pattern in `BasicSymbol`. +- Getter accessors for member spaces: `get members()`. +- `RustOutputSymbolOptions` interface extending `OutputSymbolOptions`. +- Export `RustVisibility` type and `RustSymbolKind` type. + +## Out of Scope + +- Subclasses (T004). +- Scope classes (T005). +- Factory functions (T006). + +## Context Files to Read First + +- `packages/core/src/symbols/output-symbol.ts` — base class. +- `packages/core/src/symbols/basic-symbol.ts` — reference implementation. +- `packages/go/src/symbols/go.ts` — Go's symbol class. +- `packages/csharp/src/symbols/csharp.ts` — C#'s symbol class (closest model with accessibility). + +## Implementation Guidance + +1. Study `GoSymbol` and `CSharpSymbol` for patterns. +2. Use Vue reactivity (`track`/`trigger` from `@vue/reactivity`) for reactive properties, following the exact pattern in `OutputSymbol`. +3. The `copy()` method should call `createSymbol(RustOutputSymbol, ...)` and then `this.initializeCopy(copy)`. +4. Create `symbols/index.ts` barrel exporting the class and types. + +## Acceptance Criteria + +- [x] `RustOutputSymbol` can be instantiated with `createSymbol()`. +- [x] All properties are reactive (tracked/triggered). +- [x] `copy()` produces a copy with same properties. +- [x] Types (`RustVisibility`, `RustSymbolKind`) are exported. + +## Definition of Done + +Base symbol class exists, compiles, and is exported from `symbols/index.ts`. + +## Validation Approach + +Build succeeds: `pnpm --filter @alloy-js/rust build`. + +Implementation evidence: + +- `packages/rust/src/symbols/rust-output-symbol.ts` defines `RustOutputSymbol`, exported `RustVisibility`/`RustSymbolKind`, `memberSpaces`, and `copy()`. +- `packages/rust/src/symbols/index.ts` exports the symbol module surface. + +Test evidence: + +- `packages/rust/test/rust-output-symbol.test.ts` covers instantiation, reactive property updates, `copy()`, and member-space behavior. diff --git a/docs/backlog/tasks/T004-symbol-subclasses.md b/docs/backlog/tasks/T004-symbol-subclasses.md new file mode 100644 index 000000000..355d67407 --- /dev/null +++ b/docs/backlog/tasks/T004-symbol-subclasses.md @@ -0,0 +1,73 @@ +# T004: NamedTypeSymbol and FunctionSymbol + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T004 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T003 | +| **Blocks** | T006, T011, T012, T013 | + +## Description + +Implement the `NamedTypeSymbol` (struct, enum, trait) and `FunctionSymbol` subclasses. + +## Goal + +Provide specialized symbol classes for Rust's type declarations and functions. + +## Scope Included + +- Create `packages/rust/src/symbols/named-type-symbol.ts`: + - Extends `RustOutputSymbol`. + - `typeKind: "struct" | "enum" | "trait" | "type-alias"`. Reactive. + - `static readonly memberSpaces = ["members", "type-parameters"]`. + - Getters: `members`, `typeParameters`. + - `copy()` method. + +- Create `packages/rust/src/symbols/function-symbol.ts`: + - Extends `RustOutputSymbol`. + - `receiverType?: Children` — for method receiver (`&self`, etc.). + - `static readonly memberSpaces = ["members"]` (inherited). + - `copy()` method. + +- Export both from `symbols/index.ts`. + +## Out of Scope + +- Scope classes (T005). +- Factory functions that create these (T006). + +## Context Files to Read First + +- `packages/go/src/symbols/named-type.ts` — Go's NamedTypeSymbol. +- `packages/go/src/symbols/function.ts` — Go's FunctionSymbol. +- `packages/csharp/src/symbols/named-type.ts` — C#'s NamedTypeSymbol. + +## Implementation Guidance + +1. Follow Go's pattern: `NamedTypeSymbol` has `typeKind` property and member spaces. +2. `FunctionSymbol` tracks the receiver type for methods. +3. Both implement `copy()` following the `createSymbol()` + `initializeCopy()` pattern. + +## Acceptance Criteria + +- `NamedTypeSymbol` instantiable with `typeKind`. +- `FunctionSymbol` instantiable with optional `receiverType`. +- Both exported from `symbols/index.ts`. +- Build succeeds. + +## Definition of Done + +Symbol subclasses exist, compile, and are exported. + +## Completion Notes + +- Added `NamedTypeSymbol` and `FunctionSymbol` in `packages/rust/src/symbols/`. +- Exported both from `packages/rust/src/symbols/index.ts`. +- Added tests for creation, reactive updates, member spaces, and copy behavior in `packages/rust/test/symbol-subclasses.test.ts`. diff --git a/docs/backlog/tasks/T005-scope-hierarchy.md b/docs/backlog/tasks/T005-scope-hierarchy.md new file mode 100644 index 000000000..9acd13932 --- /dev/null +++ b/docs/backlog/tasks/T005-scope-hierarchy.md @@ -0,0 +1,80 @@ +# T005: Scope Hierarchy Part 1: Module and Crate Scopes + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T005 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes (architecture review) | +| **Dependencies** | T003, T004 | +| **Blocks** | T005b, T006, T009, T022 | + +## Description + +Implement the module/crate scopes (RustCrateScope and RustModuleScope) — the scopes with import tracking and module declaration tracking. These are the most architecturally critical scopes. + +## Goal + +Define the complete scope hierarchy with correct declaration spaces, member spaces, and tracking capabilities. + +## Scope Included + +Create these files in `packages/rust/src/scopes/`: + +1. **`rust-crate-scope.ts`** — `RustCrateScope`: + - `declarationSpaces = ["types", "values"]`. + - `childModules: Map`. + - `dependencies: Map`. + - `addChildModule(name, visibility)` and `addDependency(name, dep)` methods. + - Getters: `types`, `values`. + +2. **`rust-module-scope.ts`** — `RustModuleScope`: + - `declarationSpaces = ["types", "values"]`. + - Import tracking: `imports: Map>` (path → symbols). + - `addUse(path, symbol)` method. + - `childModules: Map`. + - `addChildModule(name, visibility)` method. + - Getters: `types`, `values`. + +3. **`index.ts`** — Scope barrel with `RustScope` type alias and hooks: `useRustScope()`, `useRustModuleScope()`, `useRustCrateScope()`. + +## Out of Scope + +- Factory functions (T006). +- Reference resolution logic (T022). + +## Context Files to Read First + +- `packages/core/src/symbols/output-scope.ts` — base class. +- `packages/go/src/scopes/` — Go scope hierarchy (closest analog). +- `packages/csharp/src/scopes/` — C# scope hierarchy. +- `packages/core/src/context/scope.ts` — ScopeContext. + +## Implementation Guidance + +1. All scopes extend `OutputScope` from core. +2. Member scopes (impl, trait) pass `ownerSymbol` to the OutputScope constructor via `OutputScopeOptions`. +3. Use `shallowReactive` for Maps that need reactive tracking. +4. The `addUse()` method on `RustModuleScope` groups imports by path string. +5. Study `GoSourceFileScope` in `packages/go/src/scopes/go-source-file-scope.ts` for import tracking pattern. + +## Acceptance Criteria + +- Both scope classes compile and can be instantiated. +- Declaration spaces match specification. +- `RustModuleScope.addUse()` correctly records imports. +- `RustCrateScope.addChildModule()` and `addDependency()` work. +- Hook functions return correctly typed scopes. +- Barrel exports. + +## Definition of Done + +All scopes exist, compile, and are exported from `scopes/index.ts`. + +## Validation Approach + +Build succeeds. Unit test for scope instantiation (can be added to a general symbols test). diff --git a/docs/backlog/tasks/T005b-scope-hierarchy-part2.md b/docs/backlog/tasks/T005b-scope-hierarchy-part2.md new file mode 100644 index 000000000..e566d8013 --- /dev/null +++ b/docs/backlog/tasks/T005b-scope-hierarchy-part2.md @@ -0,0 +1,67 @@ +# T005b: Scope Hierarchy Part 2: Function, Lexical, and Member Scopes + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T005b | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T005 | +| **Blocks** | T006, T013, T019, T020 | + +## Description + +Implement the remaining scope classes: function, lexical, impl, and trait scopes. + +## Goal + +Complete the scope hierarchy with all non-module scopes. + +## Scope Included + +Create these files in `packages/rust/src/scopes/`: + +1. **`rust-function-scope.ts`** — `RustFunctionScope`: + - `declarationSpaces = ["parameters", "type-parameters", "local-variables"]`. + - Getters: `parameters`, `typeParameters`, `localVariables`. + +2. **`rust-lexical-scope.ts`** — `RustLexicalScope`: + - `declarationSpaces = ["local-variables"]`. + - Getter: `localVariables`. + +3. **`rust-impl-scope.ts`** — `RustImplScope`: + - Member scope (has `ownerSymbol`). + - `declarationSpaces = []` (delegates to ownerSymbol.members). + +4. **`rust-trait-scope.ts`** — `RustTraitScope`: + - Member scope (has `ownerSymbol`). + - `declarationSpaces = []` (delegates to ownerSymbol.members). + +Update `scopes/index.ts` to export all new scopes and update the `RustScope` type alias. + +## Out of Scope + +- Module/crate scopes (done in T005). +- Factory functions (T006). + +## Context Files to Read First + +- `packages/go/src/scopes/function.ts` — Go's function scope. +- `packages/go/src/scopes/named-type.ts` — Go's member scope pattern. +- `packages/core/src/symbols/output-scope.ts` — base class. +- `packages/rust/src/scopes/index.ts` — from T005. + +## Acceptance Criteria + +- All 4 scope classes compile and can be instantiated. +- Declaration spaces match specification. +- Member scopes correctly delegate to ownerSymbol. +- Updated barrel exports. + +## Definition of Done + +All scopes exist, compile, and are exported from `scopes/index.ts`. diff --git a/docs/backlog/tasks/T006-symbol-factories.md b/docs/backlog/tasks/T006-symbol-factories.md new file mode 100644 index 000000000..9ebc76051 --- /dev/null +++ b/docs/backlog/tasks/T006-symbol-factories.md @@ -0,0 +1,68 @@ +# T006: Symbol Factory Functions + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T006 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T003, T004, T005 | +| **Blocks** | T011, T012, T013, T014, T019, T020 | + +## Description + +Implement factory functions for creating Rust symbols in the appropriate scopes. + +## Goal + +Provide the symbol creation API that declaration components will use. + +## Scope Included + +Create `packages/rust/src/symbols/factories.ts` with: + +- `createStructSymbol(name, options?)` — creates `NamedTypeSymbol` with `typeKind: "struct"` in `types` space. +- `createEnumSymbol(name, options?)` — creates `NamedTypeSymbol` with `typeKind: "enum"` in `types` space. +- `createTraitSymbol(name, options?)` — creates `NamedTypeSymbol` with `typeKind: "trait"` in `types` space. +- `createFunctionSymbol(name, options?)` — creates `FunctionSymbol` in `values` space. +- `createMethodSymbol(name, options?)` — creates `FunctionSymbol` in owner's `members` space (for impl/trait contexts). +- `createTypeAliasSymbol(name, options?)` — creates `NamedTypeSymbol` with `typeKind: "type-alias"` in `types` space. +- `createConstSymbol(name, options?)` — creates `RustOutputSymbol` with `symbolKind: "const"` in `values` space. +- `createFieldSymbol(name, options?)` — creates `RustOutputSymbol` with `symbolKind: "field"` in owner's `members` space. +- `createVariantSymbol(name, options?)` — creates `RustOutputSymbol` with `symbolKind: "variant"` in owner's `members` space. +- `createParameterSymbol(name, options?)` — creates `RustOutputSymbol` with `symbolKind: "parameter"` in function scope's `parameters` space. +- `createTypeParameterSymbol(name, options?)` — creates `RustOutputSymbol` with `symbolKind: "type-parameter"`. + +Each factory should: + +1. Get the current scope via `useRustScope()` or a typed variant. +2. Get the current binder via `useBinder()`. +3. Call `createSymbol(Constructor, name, space, options)`. +4. Return the created symbol. + +Export all from `symbols/index.ts`. + +## Out of Scope + +- Components that use these factories. +- Reference resolution. + +## Context Files to Read First + +- `packages/go/src/symbols/factories.ts` — Go's factories. +- `packages/csharp/src/symbols/factories.ts` — C#'s factories. +- `packages/core/src/binder.ts` — `createSymbol()` API. + +## Acceptance Criteria + +- All factory functions compile and are exported. +- Each creates the correct symbol type in the correct space. +- Factory functions validate scope type where appropriate. + +## Definition of Done + +All factory functions exist, compile, and are exported. diff --git a/docs/backlog/tasks/T007-name-policy.md b/docs/backlog/tasks/T007-name-policy.md new file mode 100644 index 000000000..0602e2adc --- /dev/null +++ b/docs/backlog/tasks/T007-name-policy.md @@ -0,0 +1,78 @@ +# T007: Name Policy + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T007 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P0 — critical path | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T001 | +| **Blocks** | T009 (SourceFile needs name policy) | + +## Description + +Implement Rust naming conventions via `createRustNamePolicy()`. + +## Goal + +Ensure generated Rust code follows idiomatic naming conventions. + +## Scope Included + +Create `packages/rust/src/name-policy.ts` with: + +- `RustElements` type: `"function" | "method" | "struct" | "enum" | "enum-variant" | "trait" | "type-alias" | "type-parameter" | "field" | "variable" | "parameter" | "constant" | "module"`. + +- `createRustNamePolicy()`: + - `PascalCase`: struct, enum, enum-variant, trait, type-alias, type-parameter. + - `snake_case`: function, method, field, variable, parameter, module. + - `SCREAMING_SNAKE_CASE`: constant. + +- Reserved word handling: 37 Rust keywords → prefix with `r#`. + - Keywords: `as`, `async`, `await`, `break`, `const`, `continue`, `crate`, `dyn`, `else`, `enum`, `extern`, `false`, `fn`, `for`, `if`, `impl`, `in`, `let`, `loop`, `match`, `mod`, `move`, `mut`, `pub`, `ref`, `return`, `self`, `Self`, `static`, `struct`, `super`, `trait`, `true`, `type`, `unsafe`, `use`, `where`, `while`, `yield`. + +- `useRustNamePolicy()` hook. + +Create `packages/rust/test/name-policy.test.tsx` with tests: + +- Each element type transforms correctly. +- Reserved words get `r#` prefix. +- Non-reserved words pass through unchanged. + +## Out of Scope + +- Name conflict resolution (use default from core). + +## Context Files to Read First + +- `packages/go/src/name-policy.ts` — Go's name policy (simplest). +- `packages/python/src/name-policy.ts` — Python's name policy. +- `packages/core/src/name-policy.ts` — `createNamePolicy()` API. + +## Implementation Guidance + +1. Use `createNamePolicy()` from core. +2. Use a case-conversion library or implement manually (check what other packages use — likely `change-case` or manual). +3. For reserved words, check against a Set and prefix with `r#`. +4. Note: `r#` is different from other packages that use `_` suffix. This is Rust-specific. + +## Acceptance Criteria + +- `createRustNamePolicy()` returns a working name policy. +- `PascalCase`, `snake_case`, `SCREAMING_SNAKE_CASE` applied correctly. +- All 37+ reserved words produce `r#` prefix. +- `name-policy.test.tsx` passes. + +## Definition of Done + +Name policy implemented and tested. + +## Validation Approach + +```bash +cd packages/rust && pnpm test -- --run name-policy +``` diff --git a/docs/backlog/tasks/T007b-name-conflict-resolver.md b/docs/backlog/tasks/T007b-name-conflict-resolver.md new file mode 100644 index 000000000..a44a70351 --- /dev/null +++ b/docs/backlog/tasks/T007b-name-conflict-resolver.md @@ -0,0 +1,59 @@ +# T007b: Name Conflict Resolver + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T007b | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T003 | +| **Blocks** | T022 | + +## Description + +Implement a custom name conflict resolver for Rust that prioritizes local declarations over imported symbols. + +## Goal + +When name conflicts occur, `use`-imported symbols should be renamed (with `_2`, `_3` suffixes) before local declarations. + +## Scope Included + +Create `packages/rust/src/name-conflict-resolver.ts`: + +- `rustNameConflictResolver(name: string, symbols: RustOutputSymbol[])`. +- Separates symbols into local declarations vs imported symbols (imported = those created by `addUse`). +- Keeps local declarations unchanged. +- Renames imported symbols with `_2`, `_3` suffixes. +- Export from `src/index.ts`. + +## Out of Scope + +- Integration with binder (done when CrateDirectory passes it to `createOutputBinder()`). + +## Context Files to Read First + +- `packages/typescript/src/name-conflict-resolver.ts` — TypeScript's resolver (closest pattern). +- `packages/core/src/symbols/symbol-table.ts` — how conflict resolution is invoked. + +## Acceptance Criteria + +- `rustNameConflictResolver` compiles and is exported. +- Local declarations are never renamed. +- Imported symbols are renamed with numeric suffixes on conflict. + +## Definition of Done + +Name conflict resolver exists, compiles, and is exported. + +## Completion Notes + +- Added `packages/rust/src/name-conflict-resolver.ts` with `rustNameConflictResolver(name, symbols)`. +- Resolver keeps local declarations unchanged and renames imported symbols first using `_2`, `_3`, etc. +- Imported detection supports alias symbols (from `addUse`-style local imports) and explicit import metadata markers. +- Exported resolver from `packages/rust/src/index.ts`. +- Added `packages/rust/test/name-conflict-resolver.test.ts` for local-vs-import priority and suffix behavior. diff --git a/docs/backlog/tasks/T008-parameter-descriptor.md b/docs/backlog/tasks/T008-parameter-descriptor.md new file mode 100644 index 000000000..208143f58 --- /dev/null +++ b/docs/backlog/tasks/T008-parameter-descriptor.md @@ -0,0 +1,72 @@ +# T008: Parameter Descriptor and Scope Hooks + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T008 | +| **Epic** | [E002: Symbol System](../epics/E002-symbol-system.md) | +| **Type** | foundation | +| **Status** | done | +| **Priority** | P1 | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T001 | +| **Blocks** | T013 (FunctionDeclaration needs ParameterDescriptor) | + +## Description + +Define the `ParameterDescriptor` interface for Rust function parameters and finalize scope hook exports. + +## Goal + +Provide a structured way to describe Rust function parameters. + +## Scope Included + +Create `packages/rust/src/parameter-descriptor.ts`: + +```typescript +export interface ParameterDescriptor { + name: string; + type?: Children; + mutable?: boolean; // mut binding + refType?: "&" | "&mut"; // reference type prefix +} + +export function isParameterDescriptor( + value: unknown, +): value is ParameterDescriptor; +``` + +Also ensure `src/symbols/scopes.ts` is complete with: + +- All scope type aliases. +- All hook functions (`useRustScope()`, `useRustModuleScope()`, `useRustCrateScope()`). + +Export both from `src/index.ts`. + +## Out of Scope + +- Lifetime annotations on parameters (deferred). + +## Context Files to Read First + +- `packages/python/src/parameter-descriptor.ts` — Python's ParameterDescriptor. +- `packages/typescript/src/parameter-descriptor.ts` — TypeScript's ParameterDescriptor. + +## Acceptance Criteria + +- `ParameterDescriptor` interface is exported. +- `isParameterDescriptor()` type guard works. +- Scope hooks are exported and typed. + +## Definition of Done + +Parameter descriptor and scope hooks exist, compile, and are exported. + +## Completion Notes + +- Added `packages/rust/src/parameter-descriptor.ts` with `ParameterDescriptor` containing only `name`, `type`, `mutable`, and `refType`. +- Implemented `isParameterDescriptor(value)` with object/name checks and `Namekey` support via `isNamekey()`. +- Exported the descriptor from `packages/rust/src/index.ts`. +- Added `packages/rust/test/parameter-descriptor.test.ts` with positive/negative guard coverage and practical descriptor usage expectations. diff --git a/docs/backlog/tasks/T009-source-file-crate-directory.md b/docs/backlog/tasks/T009-source-file-crate-directory.md new file mode 100644 index 000000000..f04e02821 --- /dev/null +++ b/docs/backlog/tasks/T009-source-file-crate-directory.md @@ -0,0 +1,133 @@ +# T009: SourceFile and CrateDirectory + +| Field | Value | +| ----------- | ------------------------------------------ | +| **Task ID** | T009 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T005 (Scope hierarchy), T007 (Name policy) | +| **Blocks** | T010, T022 | +| **Status** | Done | + +## Description + +Create the foundational structural components that represent Rust source files and crate directories. `SourceFile` wraps core's `SourceFile`, creates a `RustModuleScope`, sets `filetype="rust"`, provides the Rust `Reference` component, and reserves space for rendering `use` statements and `mod` declarations (both stubs initially). `CrateDirectory` creates a `RustCrateScope`, wraps core's `SourceDirectory`, and provides crate-level context. Also introduce `CrateContext` for sharing crate metadata down the tree. + +## Goal + +Enable rendering of `.rs` files within a crate directory structure, with correct scope creation and file-type metadata, so that downstream components (declarations, references) can be nested inside them. + +## Scope + +- `src/components/source-file.tsx` — `SourceFile` component. +- `src/components/crate-directory.tsx` — `CrateDirectory` component. +- `src/context/crate-context.tsx` — `CrateContext` with crate scope and metadata. +- `src/context/index.ts` — barrel export for all contexts. +- Update `test/utils.tsx` to use the real `SourceFile` and `CrateDirectory`. + +### Props + +```ts +interface SourceFileProps { + path: string; + children?: Children; + header?: Children; + headerComment?: Children; +} + +interface CrateDirectoryProps { + name: string; + version?: string; + edition?: string; // defaults to "2021" + children?: Children; +} +``` + +## Out of Scope + +- `use` statement rendering (stubbed; implemented in T022). +- `mod` declaration rendering (stubbed; implemented in T022). +- `Cargo.toml` generation (deferred to T030). +- Cross-module import tracking logic (E005). + +## Context Files + +| File | Why | +| -------------------------------------------------- | ---------------------------------------------- | +| `packages/go/src/components/SourceFile.tsx` | Reference pattern for wrapping core SourceFile | +| `packages/go/src/components/ModuleDirectory.tsx` | Reference pattern for directory + scope | +| `packages/core/src/components/SourceFile.tsx` | Core SourceFile being wrapped | +| `packages/core/src/components/SourceDirectory.tsx` | Core SourceDirectory being wrapped | +| `packages/go/src/context.ts` | Reference for language-level context | + +## Implementation Guidance + +### SourceFile + +1. Import `SourceFile as CoreSourceFile` from `@alloy-js/core`. +2. Create a `RustModuleScope` (from T005) for the file. +3. Pass `filetype="rust"` to the core SourceFile. +4. Provide the Rust `Reference` component via the core SourceFile's `reference` prop (initially a placeholder — real Reference comes in T010). +5. Render children inside the scope. +6. Leave stub comment blocks for `use` statement rendering and `mod` declaration rendering. + +```tsx +// Pseudocode structure +function SourceFile(props: SourceFileProps) { + // create RustModuleScope + // return + // {props.headerComment} + // {props.header} + // {/* stub: use statements will be rendered here */} + // {/* stub: mod declarations will be rendered here */} + // {props.children} + // +} +``` + +### CrateDirectory + +1. Import `SourceDirectory` from `@alloy-js/core`. +2. Create a `RustCrateScope` (from T005). +3. Wrap children in `CrateContext.Provider` with crate metadata. +4. Wrap in core `SourceDirectory`. + +### CrateContext + +1. Use `createContext` from `@alloy-js/core`. +2. Store crate name, version, edition, and the `RustCrateScope` reference. + +### Alloy Conventions + +- Components access props via `props.x` — do NOT destructure. +- Use `<>...` for fragments, not ``. +- File names are kebab-case; component names are PascalCase. + +## Acceptance Criteria + +- [x] `SourceFile` renders a `.rs` file with `filetype="rust"`. +- [x] `SourceFile` creates a `RustModuleScope` for its children. +- [x] `CrateDirectory` creates a `RustCrateScope`. +- [x] `CrateDirectory` provides `CrateContext` to descendants. +- [x] `CrateContext` exposes crate name, version, edition, and scope. +- [x] `test/utils.tsx` updated to use the real components. +- [x] Stub locations for `use` statements and `mod` declarations are present. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- Unit tests pass verifying SourceFile and CrateDirectory render correctly. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm test +``` + +## Implementation Notes / Learnings + +- `SourceFile` now wraps `@alloy-js/core` `SourceFile`, sets `filetype="rust"`, and creates a `RustModuleScope` keyed by file path with crate/module parent scope support (`packages/rust/src/components/source-file.tsx`). +- `CrateDirectory` creates a `RustCrateScope`, defaults crate edition to `"2021"` when omitted, and provides `CrateContext` metadata (`packages/rust/src/components/crate-directory.tsx`, `packages/rust/src/context/crate-context.tsx`). +- `test/utils.tsx` uses real `CrateDirectory` + `SourceFile` wrappers, and focused coverage in `test/source-file-crate-directory.test.tsx` verifies scope/context behavior, rust filetype, and output-neutral placeholder blocks. diff --git a/docs/backlog/tasks/T010-declaration-reference.md b/docs/backlog/tasks/T010-declaration-reference.md new file mode 100644 index 000000000..1b1691e08 --- /dev/null +++ b/docs/backlog/tasks/T010-declaration-reference.md @@ -0,0 +1,126 @@ +# T010: Declaration and Reference Basics + +| Field | Value | +| ----------- | ------------------------------------------------ | +| **Task ID** | T010 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T006 (Symbol factories), T009 (SourceFile/Crate) | +| **Blocks** | T011, T012, T013, T014, T015, T016, T017, T018 | +| **Status** | Done | + +## Description + +Create the base `Declaration` component that wraps core's `Declaration` and handles Rust visibility prefix rendering (`pub`, `pub(crate)`). Create the initial `Reference` component that resolves a `refkey` and renders the symbol's name. Import tracking for `use` statements is deferred to T022. + +## Goal + +Provide reusable declaration and reference primitives that all subsequent Rust declaration components (struct, enum, function, etc.) will build upon. + +## Scope + +- `src/components/declaration.tsx` — `Declaration` component. +- `src/components/reference.tsx` — `Reference` component. + +### Props + +```ts +interface DeclarationProps { + name: string; + refkey?: Refkey; + nameKind?: string; // e.g., "type", "function", "constant" + pub?: boolean; + pub_crate?: boolean; + children?: Children; +} + +interface ReferenceProps { + refkey: Refkey; +} +``` + +## Out of Scope + +- Import tracking and `use` statement generation (T022). +- Path-qualified references (`crate::module::Type`). +- Trait/impl-aware reference resolution. + +## Context Files + +| File | Why | +| ------------------------------------------------------ | -------------------------------------------- | +| `packages/go/src/symbols/reference.ts` | Reference pattern for symbol name resolution | +| `packages/typescript/src/components/Declaration.tsx` | Core Declaration wrapping pattern | +| `packages/core/src/components/Declaration.tsx` | Core Declaration being wrapped | +| `packages/csharp/src/components/CSharpDeclaration.tsx` | Visibility modifier pattern | + +## Implementation Guidance + +### Declaration + +1. Import `Declaration as CoreDeclaration` from `@alloy-js/core`. +2. Accept visibility props: `pub` and `pub_crate` (mutually exclusive; `pub` takes precedence). +3. Render visibility prefix BEFORE the core Declaration's content: + - `props.pub` → render `pub `. + - `props.pub_crate` → render `pub(crate) `. + - Neither → render nothing (private by default in Rust). +4. Pass `name`, `refkey`, `nameKind` through to core Declaration. +5. Render children inside the core Declaration. + +```tsx +// Pseudocode +function Declaration(props: DeclarationProps) { + const visibility = + props.pub ? "pub " + : props.pub_crate ? "pub(crate) " + : ""; + return ( + + {visibility} + {props.children} + + ); +} +``` + +### Reference + +1. Use core's symbol resolution mechanism (e.g., `resolve` or `useContext`) to look up the symbol by `refkey`. +2. Render the resolved symbol's name. +3. Do NOT generate `use` statements or path prefixes yet — just the bare name. + +### Alloy Conventions + +- Access props as `props.pub`, `props.name` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [ ] `Declaration` renders with no visibility prefix by default. +- [ ] `Declaration` renders `pub ` prefix when `pub={true}`. +- [ ] `Declaration` renders `pub(crate) ` prefix when `pub_crate={true}`. +- [ ] `pub` takes precedence over `pub_crate` if both are set. +- [ ] `Reference` resolves a `refkey` and renders the symbol's name. +- [ ] `Reference` works within a `SourceFile` scope. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- Unit tests for Declaration visibility and Reference resolution pass. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm test +``` + +## Completion Note + +Implemented `Declaration` and `Reference` base components, wired reference resolution through `SourceFile` scope/context, and added tests covering declaration visibility behavior plus reference resolution rendering. diff --git a/docs/backlog/tasks/T011-struct-declaration.md b/docs/backlog/tasks/T011-struct-declaration.md new file mode 100644 index 000000000..4ace71118 --- /dev/null +++ b/docs/backlog/tasks/T011-struct-declaration.md @@ -0,0 +1,126 @@ +# T011: StructDeclaration and Field + +| Field | Value | +| ----------- | ----------------------------------------------------- | +| **Task ID** | T011 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T006 (Symbol factories), T010 (Declaration/Reference) | +| **Blocks** | T020 (ImplBlock needs struct types) | +| **Status** | Done | + +## Description + +Create the `StructDeclaration` and `Field` components for generating Rust struct definitions. `StructDeclaration` creates a `NamedTypeSymbol` with `typeKind: "struct"` and a member scope for fields. It supports visibility, derive attributes, doc comments, type parameters, and where clauses. + +## Goal + +Enable users to generate Rust struct declarations with fields, visibility modifiers, derives, doc comments, and generic type parameters. + +## Scope + +- `src/components/struct-declaration.tsx` — `StructDeclaration` and `Field` components. +- `test/struct.test.tsx` — comprehensive tests. + +### Props + +```ts +interface StructDeclarationProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + derives?: (string | Refkey)[]; + attributes?: Children; + doc?: string; + typeParameters?: TypeParameterProp[]; + whereClause?: Children; + children?: Children; +} + +interface FieldProps { + name: string; + type: Children; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + doc?: string; +} +``` + +## Implementation Note on Optional Dependencies + +The `derives` prop renders `#[derive(...)]` if the Attribute component (T015) is available. If T015 is not yet implemented, the struct renders without derives — this is acceptable for initial testing. The `doc` prop similarly depends on T016 but is optional. + +## Out of Scope + +- Tuple structs (may be added later). +- Unit structs (may be added later). +- Serde attributes (user can supply via `attributes` prop). +- Impl blocks for structs (T020). + +## Context Files + +| File | Why | +| ------------------------------------------------------------- | --------------------------------- | +| `packages/csharp/src/components/struct/` | Similar struct concept in C# | +| `packages/go/src/components/StructDeclaration.tsx` | Go struct pattern | +| `packages/typescript/src/components/InterfaceDeclaration.tsx` | Named type with members pattern | +| `packages/rust/src/symbols/` (from T003–T006) | NamedTypeSymbol, symbol factories | + +## Implementation Guidance + +### StructDeclaration + +1. Use the `Declaration` component (T010) for symbol registration and visibility. +2. Create a `NamedTypeSymbol` with `typeKind: "struct"` via the symbol factory (T006). +3. Render structure: + +``` +{doc comment via DocComment} +{attributes} +{derives via DeriveAttribute} +[pub] struct Name [WhereClause] { + {children (Fields)} +} +``` + +4. Use `Indent` from `@alloy-js/core` for field indentation. +5. Each `Field` renders: `[pub] name: type,` with optional doc comment above. + +### Field + +1. Render visibility prefix if `pub` or `pub_crate`. +2. Render: `name: type,` (trailing comma). +3. If `doc` prop provided, render `DocComment` above the field. + +### Alloy Conventions + +- Access props as `props.name`, `props.pub` — do NOT destructure. +- Use `<>...` for fragments. +- Use `code` template tag for multi-line string literals if needed. + +## Acceptance Criteria + +- [x] Basic struct renders: `struct Foo {}`. +- [x] Pub struct renders: `pub struct Foo {}`. +- [x] Struct with derives renders: `#[derive(Debug, Clone)]\nstruct Foo {}`. +- [x] Struct with fields renders correctly with indentation and trailing commas. +- [x] Field visibility renders: `pub name: Type,`. +- [x] Struct with doc comment renders `/// ...` above the struct. +- [x] Struct with type parameters renders: `struct Foo {}`. +- [x] Struct with where clause renders correctly. +- [x] `NamedTypeSymbol` is created with `typeKind: "struct"`. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/struct.test.tsx` passes with all listed test cases. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm vitest run struct +``` diff --git a/docs/backlog/tasks/T012-enum-declaration.md b/docs/backlog/tasks/T012-enum-declaration.md new file mode 100644 index 000000000..18c18f967 --- /dev/null +++ b/docs/backlog/tasks/T012-enum-declaration.md @@ -0,0 +1,144 @@ +# T012: EnumDeclaration and EnumVariant + +| Field | Value | +| ----------- | ------------------------------------------------------------------------------------------------------------------- | +| **Task ID** | T012 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T006 (Symbol factories), T010 (Declaration/Reference), T015 (Attributes), T016 (DocComments), T017 (TypeParameters) | +| **Blocks** | — | +| **Status** | done | + +## Description + +Create the `EnumDeclaration` and `EnumVariant` components for generating Rust enum definitions. Supports all three variant kinds: unit, tuple, and struct. Creates a `NamedTypeSymbol` with `typeKind: "enum"` and a member scope for variants. + +## Goal + +Enable users to generate Rust enum declarations with unit, tuple, and struct variants, plus derives, doc comments, and type parameters. + +## Scope + +- `src/components/enum-declaration.tsx` — `EnumDeclaration` and `EnumVariant` components. +- `test/enum.test.tsx` — tests for all variant kinds. + +### Props + +```ts +interface EnumDeclarationProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + derives?: (string | Refkey)[]; + attributes?: Children; + doc?: string; + typeParameters?: TypeParameterProp[]; + children?: Children; // EnumVariant elements +} + +interface EnumVariantProps { + name: string; + refkey?: Refkey; + doc?: string; + fields?: Children[]; // tuple variant positional types + children?: Children; // struct variant fields (Field components) +} +``` + +### Variant Kind Resolution + +- **Unit variant**: no `fields` prop and no `children` → renders `Name,`. +- **Tuple variant**: `fields` prop provided → renders `Name(T1, T2),`. +- **Struct variant**: `children` provided → renders `Name {\n field: Type,\n},`. + +## Out of Scope + +- Discriminant values (`Foo = 1`). +- Enum methods (handled via ImplBlock in T020). +- Pattern matching helpers. + +## Context Files + +| File | Why | +| -------------------------------------------------------- | --------------------------------- | +| `packages/csharp/src/components/enum/` | Similar enum concept in C# | +| `packages/typescript/src/components/EnumDeclaration.tsx` | TypeScript enum pattern | +| `packages/rust/src/symbols/` (from T003–T006) | NamedTypeSymbol, symbol factories | + +## Implementation Guidance + +### EnumDeclaration + +1. Use `Declaration` (T010) for symbol registration and visibility. +2. Create `NamedTypeSymbol` with `typeKind: "enum"` via symbol factory. +3. Render structure: + +``` +{doc comment} +{attributes} +{derives} +[pub] enum Name { + {children (EnumVariants)} +} +``` + +### EnumVariant + +1. Determine variant kind from props: + - `props.fields` is defined and non-empty → tuple variant. + - `props.children` is defined → struct variant. + - Otherwise → unit variant. +2. Render accordingly: + +``` +// Unit +Name, + +// Tuple +Name(Type1, Type2), + +// Struct +Name { + field1: Type1, + field2: Type2, +}, +``` + +3. If `doc` prop, render `DocComment` above the variant. + +### Alloy Conventions + +- Access props as `props.name`, `props.fields` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] Unit variant renders: `Name,`. +- [x] Tuple variant renders: `Name(String, i32),`. +- [x] Struct variant renders: `Name {\n field: Type,\n},`. +- [x] Pub enum renders: `pub enum Foo {}`. +- [x] Enum with derives renders: `#[derive(Debug)]\nenum Foo {}`. +- [x] Enum with doc comment renders `/// ...` above. +- [x] Enum with type parameters renders: `enum Foo {}`. +- [x] Mixed variant kinds render correctly in one enum. +- [x] `NamedTypeSymbol` is created with `typeKind: "enum"`. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/enum.test.tsx` passes with all listed test cases. +- No regressions in existing tests. + +## Implementation Notes + +- Struct-style enum variants currently render their inner fields from raw children content (e.g. `field: Type,`) instead of reusing `Field`. +- `Field` creates symbols through `createFieldSymbol()`, which intentionally rejects non-`struct` owners; enum variants use `createVariantSymbol()` and a variant member scope instead. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm vitest run enum +``` diff --git a/docs/backlog/tasks/T013-function-declaration.md b/docs/backlog/tasks/T013-function-declaration.md new file mode 100644 index 000000000..a4665028e --- /dev/null +++ b/docs/backlog/tasks/T013-function-declaration.md @@ -0,0 +1,138 @@ +# T013: FunctionDeclaration and Parameters + +| Field | Value | +| ----------- | -------------------------------------------------------------------------------------------------------- | +| **Task ID** | T013 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T006 (Symbol factories), T008 (ParameterDescriptor), T010 (Declaration/Reference), T017 (TypeParameters) | +| **Blocks** | — | +| **Status** | done | + +## Description + +Create the `FunctionDeclaration` and `Parameters` components for generating Rust function definitions. `FunctionDeclaration` creates a `FunctionSymbol` and `RustFunctionScope`. It supports visibility, async/unsafe/const qualifiers, parameters, return types, type parameters, where clauses, and doc comments. The `receiver` prop (for `&self`, `&mut self`) is NOT implemented here — it is deferred to T021 (ImplBlock). + +## Goal + +Enable users to generate Rust function declarations with parameters, return types, qualifiers, generics, and doc comments. + +## Scope + +- `src/components/function-declaration.tsx` — `FunctionDeclaration` component. +- `src/components/parameters.tsx` — `Parameters` component. +- `test/function.test.tsx` — comprehensive tests. + +### Props + +```ts +interface FunctionDeclarationProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + async?: boolean; + unsafe?: boolean; + const?: boolean; + parameters?: ParameterDescriptor[]; + returnType?: Children; + typeParameters?: TypeParameterProp[]; + whereClause?: Children; + doc?: string; + receiver?: Children; // deferred to T021 — not implemented + children?: Children; // function body +} +``` + +### ParameterDescriptor (from T008) + +```ts +interface ParameterDescriptor { + name: string; + type: Children; + refkey?: Refkey; +} +``` + +## Out of Scope + +- `receiver` prop implementation (`&self`, `&mut self`) — deferred to T021. +- Closure / lambda syntax. +- Function pointers. +- Extern functions. + +## Context Files + +| File | Why | +| ------------------------------------------------------------ | --------------------------------- | +| `packages/go/src/components/FunctionDeclaration.tsx` | Go function declaration pattern | +| `packages/typescript/src/components/FunctionDeclaration.tsx` | TypeScript function pattern | +| `packages/csharp/src/components/MethodDeclaration.tsx` | Method with qualifiers pattern | +| `packages/rust/src/symbols/` (from T003–T006) | FunctionSymbol, RustFunctionScope | + +## Implementation Guidance + +### FunctionDeclaration + +1. Use `Declaration` (T010) for symbol registration and visibility. +2. Create `FunctionSymbol` via symbol factory (T006). +3. Create `RustFunctionScope` for the function body. +4. Render structure: + +``` +{doc comment} +[pub] [async] [unsafe] [const] fn name({Parameters}) [-> ReturnType] [WhereClause] { + {children} +} +``` + +5. Qualifier order: `pub async unsafe const fn` (follow Rust convention). +6. If no children (empty body), render `{}` on the same line. + +### Parameters + +1. Accept `ParameterDescriptor[]`. +2. Render comma-separated: `name: Type, name2: Type2`. +3. Handle empty array → render `()`. + +### Alloy Conventions + +- Access props as `props.async`, `props.parameters` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] Basic function renders: `fn foo() {}`. +- [x] Pub function renders: `pub fn foo() {}`. +- [x] Async function renders: `pub async fn foo() {}`. +- [x] Unsafe function renders: `unsafe fn foo() {}`. +- [x] Const function renders: `const fn foo() {}`. +- [x] Function with parameters renders: `fn foo(x: i32, y: String) {}`. +- [x] Function with return type renders: `fn foo() -> i32 {}`. +- [x] Function with type parameters renders: `fn foo(x: T) -> T {}`. +- [x] Function with where clause renders correctly. +- [x] Function with doc comment renders `/// ...` above. +- [x] Function with body renders children indented inside braces. +- [x] Empty body renders `{}` (not `{\n}`). +- [x] `FunctionSymbol` is created with correct properties. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/function.test.tsx` passes with all listed test cases. +- No regressions in existing tests. + +## Completion Notes + +- Implemented `FunctionDeclaration` and `Parameters` in `packages/rust/src/components/function-declaration.tsx` and `packages/rust/src/components/parameters.tsx`. +- Added/updated coverage in `packages/rust/test/function.test.tsx` for qualifier order, parameters, return types, generics/where clauses, doc comments, body formatting, and symbol creation. +- Kept `receiver` support deferred to T021 as planned. + +## Validation + +```bash +pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust exec vitest run test/function.test.tsx +``` + +Result: ✅ Passed (build succeeded; `test/function.test.tsx` — 1 file, 6 tests passed). diff --git a/docs/backlog/tasks/T014-type-alias-const.md b/docs/backlog/tasks/T014-type-alias-const.md new file mode 100644 index 000000000..b7abe2b1f --- /dev/null +++ b/docs/backlog/tasks/T014-type-alias-const.md @@ -0,0 +1,115 @@ +# T014: TypeAlias and ConstDeclaration + +| Field | Value | +| ----------- | ----------------------------------------------------- | +| **Task ID** | T014 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T006 (Symbol factories), T010 (Declaration/Reference) | +| **Blocks** | — | +| **Status** | done | + +## Description + +Create `TypeAlias` and `ConstDeclaration` components for generating Rust type aliases and constant declarations. + +## Goal + +Enable users to generate `type` aliases and `const` declarations in Rust output. + +## Scope + +- `src/components/type-alias.tsx` — `TypeAlias` component. +- `src/components/const-declaration.tsx` — `ConstDeclaration` component. +- Tests in `test/type-alias-const.test.tsx` or added to existing test files. + +### Props + +```ts +interface TypeAliasProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + typeParameters?: TypeParameterProp[]; + children?: Children; // the aliased type +} + +interface ConstDeclarationProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + type: Children; + children?: Children; // the value expression +} +``` + +## Out of Scope + +- `static` declarations. +- `const fn` (handled by FunctionDeclaration in T013). +- Complex const expressions or const generics. + +## Context Files + +| File | Why | +| ------------------------------------------------------------- | --------------------------------- | +| `packages/typescript/src/components/TypeAliasDeclaration.tsx` | TypeScript type alias pattern | +| `packages/go/src/components/TypeDeclaration.tsx` | Go type alias pattern | +| `packages/rust/src/symbols/` (from T003–T006) | Symbol factories, NamedTypeSymbol | + +## Implementation Guidance + +### TypeAlias + +1. Use `Declaration` (T010) for symbol registration and visibility. +2. Create a `NamedTypeSymbol` with `typeKind: "type-alias"` via factory. +3. Render: + +``` +[pub] type Name = {children}; +``` + +4. `children` represents the aliased type (e.g., `Vec`). + +### ConstDeclaration + +1. Use `Declaration` (T010) for symbol registration and visibility. +2. Name policy should apply `SCREAMING_SNAKE_CASE` to const names (via `nameKind: "constant"`). +3. Render: + +``` +[pub] const NAME: {type} = {children}; +``` + +4. `props.type` is the type annotation; `props.children` is the value. + +### Alloy Conventions + +- Access props as `props.name`, `props.type` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] Type alias renders: `type Foo = Bar;`. +- [x] Pub type alias renders: `pub type Foo = Bar;`. +- [x] Type alias with type parameters renders: `type Foo = Vec;`. +- [x] Const renders: `const MAX_SIZE: usize = 100;`. +- [x] Pub const renders: `pub const MAX_SIZE: usize = 100;`. +- [x] Const name is transformed to SCREAMING_SNAKE_CASE by name policy. +- [x] Symbols are created with correct typeKind/symbolKind. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- Tests pass for both components. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm test +``` diff --git a/docs/backlog/tasks/T015-attributes.md b/docs/backlog/tasks/T015-attributes.md new file mode 100644 index 000000000..fc3068ade --- /dev/null +++ b/docs/backlog/tasks/T015-attributes.md @@ -0,0 +1,126 @@ +# T015: Attribute and DeriveAttribute + +| Field | Value | +| ----------- | ---------------------------------- | +| **Task ID** | T015 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T010 (Declaration/Reference) | +| **Blocks** | — | +| **Status** | Done | + +## Description + +Create `Attribute` and `DeriveAttribute` components for rendering Rust attributes. Attributes are metadata annotations that appear on the line BEFORE the item they annotate (e.g., `#[derive(Debug)]`, `#[serde(rename_all = "camelCase")]`). + +## Goal + +Enable users to annotate Rust declarations with arbitrary attributes and derive macros. + +## Scope + +- `src/components/attribute.tsx` — `Attribute` and `DeriveAttribute` components. +- `test/attributes.test.tsx` — tests. + +### Props + +```ts +interface AttributeProps { + name: string | Refkey; + args?: Children; +} + +interface DeriveAttributeProps { + traits: (string | Refkey)[]; +} +``` + +## Out of Scope + +- Inner attributes (`#![...]`) — may be added later. +- Proc macro attributes. +- Attribute validation (users can pass any attribute name). + +## Context Files + +| File | Why | +| --------------------------------------------- | -------------------------------------- | +| `packages/csharp/src/components/attributes/` | C# attribute pattern (similar concept) | +| `packages/java/src/components/Annotation.tsx` | Java annotation pattern | + +## Implementation Guidance + +### Attribute + +1. Render `#[name]` when no `args` provided. +2. Render `#[name(args)]` when `args` provided. +3. If `name` is a `Refkey`, resolve it to a symbol name (for custom derive traits). +4. Attributes render on their own line, BEFORE the declaration. + +```tsx +function Attribute(props: AttributeProps) { + // Resolve name if Refkey + const name = /* resolve props.name */; + if (props.args) { + return <>#[{name}({props.args})]; + } + return <>#[{name}]; +} +``` + +### DeriveAttribute + +1. Sugar for `#[derive(Trait1, Trait2, ...)]`. +2. Resolve each trait — if string, use as-is; if Refkey, resolve to name. +3. Join with `, `. + +```tsx +function DeriveAttribute(props: DeriveAttributeProps) { + // Resolve each trait in props.traits + const resolved = /* map and resolve */; + return ; +} +``` + +### Usage Pattern + +Attributes are typically passed via the `attributes` or `derives` props on declaration components: + +```tsx + + ... + +// Renders: +// #[derive(Debug, Clone)] +// struct Foo { +// ... +// } +``` + +### Alloy Conventions + +- Access props as `props.name`, `props.traits` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] `Attribute` renders `#[test]` for simple attribute. +- [x] `Attribute` renders `#[cfg(test)]` with args. +- [x] `DeriveAttribute` renders `#[derive(Debug)]` for single trait. +- [x] `DeriveAttribute` renders `#[derive(Debug, Clone, Serialize)]` for multiple traits. +- [x] Attributes render on the line before the annotated item. +- [x] Refkey-based trait names resolve correctly. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/attributes.test.tsx` passes. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm vitest run attributes +``` diff --git a/docs/backlog/tasks/T016-doc-comments.md b/docs/backlog/tasks/T016-doc-comments.md new file mode 100644 index 000000000..d466c1a5d --- /dev/null +++ b/docs/backlog/tasks/T016-doc-comments.md @@ -0,0 +1,120 @@ +# T016: DocComment and ModuleDocComment + +| Field | Value | +| ----------- | ---------------------------------- | +| **Task ID** | T016 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T001 (Package scaffold) | +| **Blocks** | — | +| **Status** | done | + +## Description + +Create `DocComment` and `ModuleDocComment` components for rendering Rust documentation comments. `DocComment` renders outer doc comments (`/// ...`) used on items. `ModuleDocComment` renders inner doc comments (`//! ...`) used at the top of modules/crates. + +## Goal + +Enable Rust declarations to include doc comments, and modules/crates to include module-level documentation. + +## Scope + +- `src/components/doc-comment.tsx` — `DocComment` and `ModuleDocComment` components. +- Tests in `test/doc-comment.test.tsx`. + +### Props + +```ts +interface DocCommentProps { + children?: Children; // string content +} + +interface ModuleDocCommentProps { + children?: Children; // string content +} +``` + +## Out of Scope + +- Markdown rendering within doc comments. +- Doc comment links (`[`Type`]`). +- `#[doc = "..."]` attribute form. +- Doc tests. + +## Context Files + +| File | Why | +| -------------------------------------------------- | ------------------------ | +| `packages/java/src/components/JavadocComment.tsx` | Java doc comment pattern | +| `packages/csharp/src/components/XmlDocComment.tsx` | C# doc comment pattern | + +## Implementation Guidance + +### DocComment + +1. Accept string children. +2. Split by newline. +3. Prefix each line with `/// `. +4. Render all lines, each on its own line. +5. Empty/undefined children → render nothing. + +```tsx +function DocComment(props: DocCommentProps) { + if (!props.children) return <>; + // Split children text into lines + // Render each line as: /// {line} + // Each line on its own output line +} +``` + +### ModuleDocComment + +1. Same as `DocComment` but uses `//! ` prefix instead of `/// `. +2. Typically placed at the top of a `SourceFile`. + +```tsx +function ModuleDocComment(props: ModuleDocCommentProps) { + if (!props.children) return <>; + // Split children text into lines + // Render each line as: //! {line} +} +``` + +### Usage Pattern + +Declaration components accept a `doc` string prop and render `DocComment` internally: + +```tsx +// Inside StructDeclaration: +{ + props.doc && {props.doc}; +} +``` + +### Alloy Conventions + +- Access props as `props.children` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] `DocComment` renders single line: `/// Hello`. +- [x] `DocComment` renders multi-line: `/// Line 1\n/// Line 2`. +- [x] `DocComment` renders nothing for empty/undefined children. +- [x] `ModuleDocComment` renders: `//! Module docs`. +- [x] `ModuleDocComment` renders multi-line: `//! Line 1\n//! Line 2`. +- [x] `ModuleDocComment` renders nothing for empty/undefined children. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/doc-comment.test.tsx` passes. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm vitest run doc-comment +``` diff --git a/docs/backlog/tasks/T017-type-parameters.md b/docs/backlog/tasks/T017-type-parameters.md new file mode 100644 index 000000000..f70e56a2b --- /dev/null +++ b/docs/backlog/tasks/T017-type-parameters.md @@ -0,0 +1,143 @@ +# T017: TypeParameters and WhereClause + +| Field | Value | +| ----------- | ---------------------------------- | +| **Task ID** | T017 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T001 (Package scaffold) | +| **Blocks** | — | +| **Status** | done | + +## Description + +Create `TypeParameters` and `WhereClause` components for rendering Rust generic type parameter lists and where clauses. These are used by struct, enum, function, type alias, trait, and impl block components. + +## Goal + +Enable declaration components to render generic type parameters with trait bounds and where clauses. + +## Scope + +- `src/components/type-parameters.tsx` — `TypeParameters` and `WhereClause` components. +- Tests in `test/type-parameters.test.tsx`. + +### Props and Types + +```ts +interface TypeParameterProp { + name: string; + constraint?: Children; // trait bounds, e.g., "Display + Clone" +} + +interface TypeParametersProps { + params: TypeParameterProp[]; +} + +interface WhereClauseProps { + children?: Children; // free-form where clause content +} +``` + +## Out of Scope + +- Lifetime parameters (`'a`). +- Const generics (`const N: usize`). +- Default type parameters (`T = String`). +- Associated types in bounds. + +## Context Files + +| File | Why | +| ----------------------------------------------------------------- | ------------------------------------ | +| `packages/typescript/src/components/TypeParameterDeclaration.tsx` | TypeScript generic parameter pattern | +| `packages/csharp/src/components/GenericParameters.tsx` | C# generic parameter pattern | +| `packages/go/src/components/TypeParameters.tsx` | Go type parameter pattern | + +## Implementation Guidance + +### TypeParameters + +1. Accept array of `TypeParameterProp`. +2. If empty array or undefined, render nothing. +3. Render `<` + comma-separated params + `>`. +4. Each param: `name` if no constraint, `name: constraint` if constraint present. + +```tsx +function TypeParameters(props: TypeParametersProps) { + if (!props.params || props.params.length === 0) return <>; + // Render: +} +``` + +Examples: + +- `[{name: "T"}]` → `` +- `[{name: "T", constraint: "Display"}]` → `` +- `[{name: "T"}, {name: "U", constraint: "Display + Clone"}]` → `` + +### WhereClause + +1. Accept free-form children content. +2. If no children, render nothing. +3. Render `where ` followed by children on the next line (or same line for short clauses). + +```tsx +function WhereClause(props: WhereClauseProps) { + if (!props.children) return <>; + // Render: where {children} + // e.g.: where T: Display + Clone, U: Debug +} +``` + +### Usage Pattern + +Used by declaration components: + +```tsx +T: Clone} +> + ... + +// Renders: +// struct Foo where T: Clone { +// ... +// } +``` + +### Alloy Conventions + +- Access props as `props.params`, `props.children` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] Single type parameter renders: ``. +- [x] Multiple type parameters render: ``. +- [x] Type parameter with constraint renders: ``. +- [x] Multiple params with mixed bounds render: ``. +- [x] Empty/undefined params render nothing (no `<>`). +- [x] Where clause renders: `where T: Display + Clone`. +- [x] Where clause with multiple constraints renders correctly. +- [x] Empty/undefined where clause renders nothing. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/type-parameters.test.tsx` passes. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm vitest run type-parameters +``` + +## Implementation Notes + +- TSX parser gotcha: when test literals include `<...>` (for example `impl Thing`), wrap the text in a JS expression (`{"impl Thing"}`) to avoid JSX parse errors. diff --git a/docs/backlog/tasks/T018-value-component.md b/docs/backlog/tasks/T018-value-component.md new file mode 100644 index 000000000..df9feecbf --- /dev/null +++ b/docs/backlog/tasks/T018-value-component.md @@ -0,0 +1,144 @@ +# T018: Value Component + +| Field | Value | +| ----------- | ---------------------------------- | +| **Task ID** | T018 | +| **Epic** | E003 — Core Declaration Components | +| **Deps** | T001 (Package scaffold) | +| **Blocks** | — | +| **Status** | Done | + +## Description + +Create a `Value` component that converts JavaScript/TypeScript values into Rust literal syntax. This is used when generating const values, default parameters, or inline expressions. + +## Goal + +Enable automatic conversion of JS values to Rust literal representations for use in code generation. + +## Scope + +- `src/components/value.tsx` — `Value` component. +- Tests in `test/value.test.tsx`. + +### Props + +```ts +interface ValueProps { + value: unknown; +} +``` + +### Conversion Table + +| JS Type | Rust Output | Example | +| ---------------- | ---------------- | ----------------------- | +| `string` | `"str"` | `"hello"` → `"hello"` | +| `number` (int) | `42` | `42` → `42` | +| `number` (float) | `42.0` | `42.5` → `42.5` | +| `boolean` | `true` / `false` | `true` → `true` | +| `null` | `None` | `null` → `None` | +| `undefined` | `None` | `undefined` → `None` | +| `Array` | `vec![a, b, c]` | `[1, 2]` → `vec![1, 2]` | + +## Out of Scope + +- Object/Map → Rust HashMap literal (complex; users do this manually). +- Tuple literals. +- Raw string literals (`r#"..."#`). +- Byte strings (`b"..."`). +- Character literals (`'c'`). +- Nested struct initialization. + +## Context Files + +| File | Why | +| ---------------------------------------------- | ----------------------------------- | +| `packages/java/src/components/Value.tsx` | Java value conversion pattern | +| `packages/python/src/components/Atom.tsx` | Python literal conversion pattern | +| `packages/typescript/src/components/Value.tsx` | TypeScript value conversion pattern | + +## Implementation Guidance + +### Value + +1. Accept `props.value` of type `unknown`. +2. Switch on the runtime type: + +```tsx +function Value(props: ValueProps) { + const v = props.value; + + if (v === null || v === undefined) { + return <>None; + } + + if (typeof v === "boolean") { + return <>{v ? "true" : "false"}; + } + + if (typeof v === "number") { + // Distinguish integer from float + if (Number.isInteger(v)) { + return <>{String(v)}; + } + return <>{String(v)}; + } + + if (typeof v === "string") { + return <>"{v}"; + } + + if (Array.isArray(v)) { + // Recursively render each element as a Value + // Render: vec![elem1, elem2, elem3] + return <>vec![{/* comma-separated Value elements */}]; + } + + // Fallback: toString + return <>{String(v)}; +} +``` + +3. For arrays, recursively render each element using ``. +4. String escaping: escape `\`, `"`, `\n`, `\t` in string values. + +### Integer vs Float Detection + +- Use `Number.isInteger(v)` to distinguish. +- Integer `42` → `42`. +- Float `42.0` in JS is `42` (integer) — this is a known edge case. Consider providing a float hint or rendering `42.0` when the number has no fractional part but the user wants a float. For now, rely on `Number.isInteger`. + +### Alloy Conventions + +- Access props as `props.value` — do NOT destructure. +- Use `<>...` for fragments. +- File names: kebab-case. Components: PascalCase. + +## Acceptance Criteria + +- [x] String renders: `"hello"`. +- [x] Integer renders: `42`. +- [x] Float renders: `3.14`. +- [x] Boolean true renders: `true`. +- [x] Boolean false renders: `false`. +- [x] Null renders: `None`. +- [x] Undefined renders: `None`. +- [x] Array renders: `vec![1, 2, 3]`. +- [x] Nested array renders: `vec![vec![1, 2], vec![3, 4]]`. +- [x] Empty array renders: `vec![]`. +- [x] String with special characters is properly escaped. + +## Definition of Done + +- All acceptance criteria met. +- `pnpm build` succeeds for `@alloy-js/rust`. +- `test/value.test.tsx` passes with all listed test cases. +- No regressions in existing tests. + +## Validation + +```bash +cd packages/rust +pnpm build && pnpm test +``` diff --git a/docs/backlog/tasks/T019-trait-declaration.md b/docs/backlog/tasks/T019-trait-declaration.md new file mode 100644 index 000000000..dfc62a2a1 --- /dev/null +++ b/docs/backlog/tasks/T019-trait-declaration.md @@ -0,0 +1,104 @@ +# T019: TraitDeclaration Component + +| Field | Value | +| ----------------- | ----------------------------------------------------------------- | +| **ID** | T019 | +| **Epic** | [E004 — Traits and Impl Blocks](../epics/E004-traits-and-impl.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T006, T010, T013, T017 | +| **Blocks** | T020 | + +--- + +## Description + +Implement the `TraitDeclaration` component for the `@alloy-js/rust` package. Traits are Rust's primary abstraction mechanism for defining shared behavior. This component must create a `NamedTypeSymbol` with `typeKind: "trait"`, establish a `RustTraitScope`, and render the full trait syntax including visibility, name, type parameters, supertraits, where clauses, and body. + +## Goal + +Enable generation of Rust trait declarations with method signatures, default implementations, supertraits, and generic type parameters. + +## Scope + +- Create `src/components/trait-declaration.tsx`. +- Define `TraitDeclarationProps` interface: + - `name: string` — trait name. + - `refkey?: Refkey` — optional refkey for cross-referencing. + - `pub?: boolean` — visibility modifier. + - `typeParameters?: Children` — generic type parameters. + - `supertraits?: Children[]` — list of supertrait bounds (rendered as `Trait1 + Trait2`). + - `whereClause?: Children` — where clause content. + - `doc?: Children` — doc comment content. + - `children?: Children` — trait body (method signatures and default implementations). +- Create a `NamedTypeSymbol` with `typeKind: "trait"` in the enclosing scope. +- Create a `RustTraitScope` as the trait body scope. +- Methods inside the trait are method signatures (no body) or default implementations (with body). +- Create `test/trait.test.tsx`. + +## Out of Scope + +- Associated types (render inline as raw content for now). +- Trait objects (`dyn Trait`). +- Auto traits. +- Negative impls. + +## Context Files + +The implementation agent should read these files first: + +- `packages/csharp/src/components/interface/InterfaceDeclaration.tsx` — closest analog in existing Alloy packages. +- `packages/rust/src/symbols/rust-trait-scope.ts` — the scope type to use (from T006). +- `packages/rust/src/components/function-declaration.tsx` — method rendering (from T013). +- `packages/rust/src/components/type-parameters.tsx` — generic parameters (from T017). +- `packages/rust/src/symbols/named-type-symbol.ts` — symbol creation (from T010). + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/trait-declaration.tsx`. +2. **Props interface**: Define `TraitDeclarationProps` with all props listed above. Do NOT destructure props in the component definition — use `props.name`, `props.pub`, etc. +3. **Symbol creation**: Use the binder to create a `NamedTypeSymbol` with `typeKind: "trait"` in the enclosing scope. +4. **Scope**: Create a `RustTraitScope` as the body scope. Methods declared as children are registered in this scope. +5. **Rendering pattern**: + ``` + /// doc comment + [pub ]trait Name[: Super1 + Super2][ where WhereClause] { + method_signature_1; + method_signature_2; + fn default_method(&self) { + // body + } + } + ``` +6. **Supertraits**: Join `props.supertraits` with `+` separator. Prefix with `: ` if non-empty. +7. **Use `code` template tag** for raw string fragments. +8. **Reference the C# `InterfaceDeclaration`** for the overall component structure, symbol creation, and scope management patterns. + +## Acceptance Criteria + +- [x] `TraitDeclaration` renders a basic trait with no methods. +- [x] `TraitDeclaration` renders a trait with method signatures (no body). +- [x] `TraitDeclaration` renders a trait with supertraits (`: Display + Debug`). +- [x] `TraitDeclaration` renders a trait with default method implementations. +- [x] `TraitDeclaration` renders a trait with type parameters and where clause. +- [x] `TraitDeclaration` with `pub` renders `pub trait`. +- [x] Doc comments render correctly above the trait. +- [x] A `NamedTypeSymbol` with `typeKind: "trait"` is created and resolvable via refkey. + +## Definition of Done + +- `src/components/trait-declaration.tsx` exists and exports `TraitDeclaration`. +- `TraitDeclarationProps` interface is exported. +- `test/trait.test.tsx` passes with all acceptance criteria covered. +- Component is re-exported from `src/components/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/trait.test.tsx +``` diff --git a/docs/backlog/tasks/T020-impl-block.md b/docs/backlog/tasks/T020-impl-block.md new file mode 100644 index 000000000..60d01e660 --- /dev/null +++ b/docs/backlog/tasks/T020-impl-block.md @@ -0,0 +1,98 @@ +# T020: ImplBlock Component + +| Field | Value | +| ----------------- | ----------------------------------------------------------------- | +| **ID** | T020 | +| **Epic** | [E004 — Traits and Impl Blocks](../epics/E004-traits-and-impl.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T006, T010, T013, T017, T019 | +| **Blocks** | T021 | + +--- + +## Description + +Implement the `ImplBlock` component for the `@alloy-js/rust` package. Impl blocks in Rust attach methods and associated items to a type (inherent impl) or implement a trait for a type (trait impl). This construct has no direct analog in existing Alloy language packages — it is architecturally novel because methods are declared separately from the type definition. + +## Goal + +Enable generation of both inherent and trait impl blocks, with methods correctly added to the target type's member space. + +## Scope + +- Create `src/components/impl-block.tsx`. +- Define `ImplBlockProps` interface: + - `type: Refkey | Children` — the target type being implemented. + - `trait?: Refkey | Children` — optional trait being implemented for the type. + - `typeParameters?: Children` — generic type parameters. + - `whereClause?: Children` — where clause content. + - `children?: Children` — impl body (methods, associated items). +- Create a `RustImplScope` with the target type as owner. +- Methods declared inside the impl block are added to the target type's "members" space. +- Create `test/impl.test.tsx`. + +## Out of Scope + +- Associated types as first-class props (render inline). +- Negative impls (`impl !Send for Type`). +- `unsafe impl`. +- Multiple impl blocks merging member spaces (each impl block independently adds to the type). + +## Context Files + +- `packages/rust/src/symbols/rust-impl-scope.ts` — the scope type to use (from T006). +- `packages/rust/src/components/function-declaration.tsx` — method rendering (from T013). +- `packages/rust/src/components/type-parameters.tsx` — generic parameters (from T017). +- `packages/rust/src/symbols/named-type-symbol.ts` — symbol for target type resolution (from T010). +- `packages/rust/src/components/trait-declaration.tsx` — trait that may be implemented (from T019). + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/impl-block.tsx`. +2. **Props**: Do NOT destructure — use `props.type`, `props.trait`, etc. +3. **Scope**: Create a `RustImplScope`. The scope should track the target type so that methods declared as children can be added to the type's member space. +4. **Type resolution**: `props.type` can be a `Refkey` (resolve to type name) or `Children` (render inline). +5. **Inherent impl rendering**: + ``` + impl TypeName [where WhereClause] { + fn method(&self) { ... } + } + ``` +6. **Trait impl rendering**: + ``` + impl TraitName for TypeName [where WhereClause] { + fn method(&self) { ... } + } + ``` +7. **Member registration**: When a `FunctionDeclaration` is rendered inside an `ImplBlock`, it should be registered in the target type's member namespace. This allows other code to reference `Type::method`. +8. **Use `code` template tag** for raw string rendering. +9. **No direct analog** in existing packages — design from Rust semantics and Alloy patterns. + +## Acceptance Criteria + +- [x] `ImplBlock` renders an inherent impl with methods. +- [x] `ImplBlock` renders a trait impl with methods. +- [x] `ImplBlock` renders with type parameters. +- [x] `ImplBlock` renders with a where clause. +- [x] Methods inside impl block are added to target type's member space. +- [x] `props.type` works with both `Refkey` and inline `Children`. +- [x] `props.trait` works with both `Refkey` and inline `Children`. + +## Definition of Done + +- `src/components/impl-block.tsx` exists and exports `ImplBlock`. +- `ImplBlockProps` interface is exported. +- `test/impl.test.tsx` passes with all acceptance criteria covered. +- Component is re-exported from `src/components/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/impl.test.tsx +``` diff --git a/docs/backlog/tasks/T021-self-receiver.md b/docs/backlog/tasks/T021-self-receiver.md new file mode 100644 index 000000000..7e429e882 --- /dev/null +++ b/docs/backlog/tasks/T021-self-receiver.md @@ -0,0 +1,93 @@ +# T021: Self Receiver in FunctionDeclaration + +| Field | Value | +| ----------------- | ----------------------------------------------------------------- | +| **ID** | T021 | +| **Epic** | [E004 — Traits and Impl Blocks](../epics/E004-traits-and-impl.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T013, T020 | +| **Blocks** | — | + +--- + +## Description + +Update `FunctionDeclaration` to support the `receiver` prop, enabling method-style functions inside `ImplBlock` and `TraitDeclaration`. In Rust, methods take `self` as their first parameter in various forms: `&self`, `&mut self`, `self` (by value), or no self parameter (associated function). + +## Goal + +Enable `FunctionDeclaration` to automatically handle self receiver rendering when used inside impl blocks and trait declarations, with sensible defaults and explicit override. + +## Scope + +- Add `receiver` prop to `FunctionDeclarationProps`: `"&self" | "&mut self" | "self" | "none"`. +- When inside an `ImplBlock` or `TraitDeclaration` (detected via scope context), default `receiver` to `"&self"`. +- When `receiver` is `"none"`, no self parameter is rendered (associated function / static method). +- When outside an impl block or trait, the `receiver` prop is ignored. +- Render receiver as the first parameter before any other parameters. +- Update `test/function.test.tsx` with receiver-specific tests. + +## Out of Scope + +- Custom self types (e.g., `self: Box`, `self: Rc`). +- `Pin<&mut Self>` receivers. + +## Context Files + +- `packages/rust/src/components/function-declaration.tsx` — the component to update (from T013). +- `packages/rust/src/components/impl-block.tsx` — provides impl block context (from T020). +- `packages/rust/src/components/trait-declaration.tsx` — provides trait context (from T019). +- `packages/rust/src/symbols/rust-impl-scope.ts` — scope to detect impl context. +- `packages/rust/src/symbols/rust-trait-scope.ts` — scope to detect trait context. + +## Implementation Guidance + +1. **File**: Update `packages/rust/src/components/function-declaration.tsx`. +2. **Props**: Add `receiver?: "&self" | "&mut self" | "self" | "none"` to `FunctionDeclarationProps`. Do NOT destructure — use `props.receiver`. +3. **Context detection**: Check if the function is inside a `RustImplScope` or `RustTraitScope` by walking the scope chain or using a context value. +4. **Default behavior**: + - Inside `ImplBlock` or `TraitDeclaration`: default receiver is `"&self"`. + - Outside these scopes: no receiver regardless of prop value. +5. **Rendering**: When receiver is present and not `"none"`, render it as the first parameter: + ``` + fn method_name(&self, param1: Type1, param2: Type2) -> ReturnType { + // body + } + ``` + When receiver is `"none"`: + ``` + fn associated_fn(param1: Type1) -> ReturnType { + // body + } + ``` +6. **Comma handling**: If there are additional parameters after the receiver, insert a comma and space after the receiver. +7. **Use `code` template tag** for raw string fragments. + +## Acceptance Criteria + +- [x] Method with `&self` receiver renders `fn name(&self)`. +- [x] Method with `&mut self` receiver renders `fn name(&mut self)`. +- [x] Method with `self` (by value) receiver renders `fn name(self)`. +- [x] Method with `receiver="none"` renders no self parameter (associated function). +- [x] Default receiver inside `ImplBlock` is `&self`. +- [x] Default receiver inside `TraitDeclaration` is `&self`. +- [x] Receiver with additional parameters renders correctly: `fn name(&self, x: i32)`. +- [x] Function outside impl/trait ignores receiver prop. + +## Definition of Done + +- `FunctionDeclarationProps` includes `receiver` prop. +- `FunctionDeclaration` correctly renders self receivers. +- Updated tests in `test/function.test.tsx` pass. + +## Validation + +```bash +cd packages/rust +npx vitest run test/function.test.tsx +``` diff --git a/docs/backlog/tasks/T022-reference-resolution.md b/docs/backlog/tasks/T022-reference-resolution.md new file mode 100644 index 000000000..0d956641d --- /dev/null +++ b/docs/backlog/tasks/T022-reference-resolution.md @@ -0,0 +1,88 @@ +# T022: Reference Resolution + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T022 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T005, T010 | +| **Blocks** | T023, T026 | + +--- + +## Description + +Implement Rust-specific reference resolution logic. When a symbol is referenced via `refkey`, the resolver must determine the relationship between the reference site and the symbol definition (same module, same crate but different module, or external crate) and generate the appropriate `use` import or render the name directly. + +## Goal + +Enable automatic import generation when referencing symbols across modules and crates, following Rust's module and import conventions. + +## Scope + +- Create or update `src/symbols/reference.tsx` (or `.ts`). +- Implement a `ref()` function (or update the existing `Reference` component) that: + 1. Resolves a `Refkey` via the binder to get the target symbol and `ResolutionResult`. + 2. Determines the relationship: same-module, same-crate-different-module, or external-crate. + 3. **Same module**: render the symbol name only (no import needed). + 4. **Same crate, different module**: call `moduleScope.addUse("crate::path::to", symbol)` to register a use import, then render the symbol name. + 5. **External crate**: call `moduleScope.addUse("::path", symbol)` to register the import AND call `crateScope.addDependency()` to register the crate dependency. +- Build the use path from `ResolutionResult.pathDown` (the scope chain from resolution root to the symbol). +- **Prelude handling**: maintain a `PRELUDE_TYPES` set of types that are automatically in scope (e.g., `Option`, `Result`, `Vec`, `String`, `Box`, `Some`, `None`, `Ok`, `Err`). Skip use generation for these types. +- Maintain a `PRELUDE_TYPES: Set` constant with Rust prelude types. Skip `use` generation for prelude types. Full list: Option, Some, None, Result, Ok, Err, Vec, String, ToString, Box, Clone, Copy, Default, Drop, Eq, PartialEq, Ord, PartialOrd, Iterator, IntoIterator, From, Into, TryFrom, TryInto, AsRef, AsMut, Send, Sync, Sized, Unpin, ToOwned, Fn, FnMut, FnOnce, bool, char, f32, f64, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, str. +- Create `test/reference.test.tsx` with basic resolution tests. + +## Out of Scope + +- Glob imports (`use path::*`). +- Re-exports (`pub use`). +- Aliased imports (`use path::Type as Alias`). +- Fully qualified syntax (`::method`). + +## Context Files + +- `packages/go/src/symbols/reference.ts` — closest analog in existing Alloy packages. +- `packages/rust/src/symbols/rust-module-scope.ts` — module scope with import tracking (from T005). +- `packages/rust/src/symbols/rust-crate-scope.ts` — crate scope with dependency tracking (from T005). +- `packages/core/src/components/Reference.tsx` — base Reference component. +- `packages/core/src/binder.ts` — binder and resolution result types. + +## Implementation Guidance + +1. **File**: `packages/rust/src/symbols/reference.tsx`. +2. **Resolution flow**: + - Resolve the refkey using the binder: `binder.resolve(refkey)`. + - Get the `ResolutionResult` which includes `pathDown` (array of scopes from common ancestor to target). + - Walk `pathDown` to build the Rust path segments (module names, crate name). +3. **Same-module check**: If the target symbol's nearest module scope is the same as the reference site's module scope, render name only. +4. **Same-crate check**: If the target symbol's crate scope matches the reference site's crate scope but the module scope differs, generate a `crate::` prefixed path. +5. **External-crate check**: If crate scopes differ, use the external crate name and register a dependency. +6. **Prelude bypass**: Before generating any import, check if the symbol name is in `PRELUDE_TYPES`. If so, render the name directly. +7. **Module scope API**: Call `moduleScope.addUse(path, symbolName)` where `path` is the full use path without the final symbol name. +8. **Reference**: Study `packages/go/src/symbols/reference.ts` carefully — it implements a similar resolution pattern for Go imports. + +## Acceptance Criteria + +- [x] Reference to a symbol in the same module renders just the symbol name. +- [x] Reference to a symbol in a different module of the same crate triggers a `crate::mod::Symbol` use import. +- [x] Reference to a symbol in an external crate triggers the correct use import and registers a crate dependency. +- [x] Prelude types (`Option`, `Result`, `Vec`, etc.) do not generate use statements. +- [x] The use path is correctly built from `ResolutionResult.pathDown`. + +## Definition of Done + +- `src/symbols/reference.tsx` exists and exports the reference resolution logic. +- `test/reference.test.tsx` passes with all acceptance criteria covered. +- Module is re-exported from `src/symbols/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/reference.test.tsx +``` diff --git a/docs/backlog/tasks/T023-use-statements.md b/docs/backlog/tasks/T023-use-statements.md new file mode 100644 index 000000000..e237ec144 --- /dev/null +++ b/docs/backlog/tasks/T023-use-statements.md @@ -0,0 +1,94 @@ +# T023: Use Statements + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T023 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T022 | +| **Blocks** | T026 | + +--- + +## Description + +Implement `UseStatement` and `UseStatements` components that render Rust `use` declarations. `UseStatements` reads accumulated imports from `RustModuleScope` and renders them with proper grouping and sorting according to Rust conventions. + +## Goal + +Generate correctly formatted, grouped, and sorted `use` statements in each source file based on symbols referenced across module boundaries. + +## Scope + +- Create `src/components/use-statement.tsx`. +- `UseStatement` component: renders a single `use path::Symbol;` line. +- `UseStatements` component: reads `RustModuleScope.imports` and renders all accumulated use statements. +- **Flat syntax**: Each symbol gets its own `use` statement: `use path::A;`, `use path::B;`. +- **Sorting**: Follow Rust convention: + 1. `std::` imports first. + 2. External crate imports second. + 3. `crate::` imports third. + 4. Alphabetical within each group. + 5. Blank line between groups. +- Update `SourceFile` component to render `UseStatements` after any `mod` declarations and before the main content. +- Create `test/use-statements.test.tsx`. + +**MVP Decision:** Use flat `use` statements (`use path::A; use path::B;`) for simplicity. Tree grouping (`use path::{A, B};`) is deferred to post-MVP polish. Flat syntax is valid Rust and `rustfmt` can group if desired. + +## Out of Scope + +- Tree grouping (`use path::{A, B};`) — deferred to post-MVP. +- Glob imports (`use path::*`). +- Re-exports (`pub use`). +- Aliased imports (`use path::Type as Alias`). +- Nested path groups (`use std::{io::{self, Read}, fmt}`). + +## Context Files + +- `packages/rust/src/symbols/rust-module-scope.ts` — scope with import tracking (from T005). +- `packages/rust/src/components/source-file.tsx` — the file component to update (from T009). +- `packages/go/src/components/ImportStatements.tsx` — analog in Go package. +- `packages/rust/src/symbols/reference.tsx` — adds imports to module scope (from T022). + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/use-statement.tsx`. +2. **`UseStatement` component**: Takes `path: string` and `symbol: string` props. Renders: + - `use path::Symbol;` +3. **`UseStatements` component**: No props needed — reads from the nearest `RustModuleScope` via context. + - Collect all imports from `moduleScope.imports` (a map of path → symbol names). + - Group by category: `std`, external, `crate`. + - Sort alphabetically within each group. + - Render with blank lines between groups. +4. **Integration with SourceFile**: Update `SourceFile` to include `` in the rendered output, positioned after `mod` declarations and before the file's main children content. +5. **Do NOT destructure props** — use `props.path`, `props.symbols`, etc. +6. **Use `code` template tag** for raw string fragments like `use `, `::`, `{`, `}`, `;`. + +## Acceptance Criteria + +- [x] Single import renders as `use std::fmt::Display;`. +- [x] Multiple imports from same path render as flat statements: `use std::fmt::Debug;` and `use std::fmt::Display;`. +- [x] Imports are sorted: `std::` → external → `crate::`. +- [x] Blank line separates import groups. +- [x] Alphabetical order within each group. +- [x] `UseStatements` correctly reads from `RustModuleScope`. +- [x] `SourceFile` renders `UseStatements` in the correct position. + +## Definition of Done + +- `src/components/use-statement.tsx` exists and exports `UseStatement` and `UseStatements`. +- `test/use-statements.test.tsx` passes with all acceptance criteria covered. +- `SourceFile` updated to include `UseStatements`. +- Components are re-exported from `src/components/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/use-statements.test.tsx +``` diff --git a/docs/backlog/tasks/T024-module-directory.md b/docs/backlog/tasks/T024-module-directory.md new file mode 100644 index 000000000..86625c3b9 --- /dev/null +++ b/docs/backlog/tasks/T024-module-directory.md @@ -0,0 +1,90 @@ +# T024: Module Directory + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T024 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T009, T005 | +| **Blocks** | T025 | + +--- + +## Description + +Implement the `ModuleDirectory` component for representing Rust modules that correspond to a filesystem directory (e.g., `src/models/` with a `mod.rs` inside). This maps to Rust's module-as-directory convention where a module named `models` is either `models.rs` or `models/mod.rs`. + +## Goal + +Enable generation of multi-file module structures with correct directory layout and `mod.rs` generation. + +## Scope + +- Create `src/components/module-directory.tsx`. +- Define `ModuleDirectoryProps`: + - `path: string` — directory path relative to parent (e.g., `"models"`). + - `pub?: boolean` — whether the module is publicly visible. + - `children?: Children` — contents (source files, nested module directories). +- Create a `SourceDirectory` from `@alloy-js/core` wrapping a `RustModuleScope`. +- Register as a child module in the parent module scope (for `mod` declaration generation). +- Generate a `mod.rs` inside the directory if the directory contains child modules or source files. +- Create `test/module-directory.test.tsx`. + +## Out of Scope + +- Inline modules (`mod name { ... }` within a file). +- Module-level attributes (`#![...]`). +- Path attribute (`#[path = "..."]`) for non-standard module locations. + +## Context Files + +- `packages/go/src/components/SourceDirectory.tsx` — closest analog in existing Alloy packages. +- `packages/rust/src/components/source-file.tsx` — source file component (from T009). +- `packages/rust/src/symbols/rust-module-scope.ts` — module scope (from T005). +- `packages/core/src/components/SourceDirectory.tsx` — base directory component. + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/module-directory.tsx`. +2. **Props**: Do NOT destructure — use `props.path`, `props.pub`, `props.children`. +3. **Directory creation**: Use `SourceDirectory` from `@alloy-js/core` to create the filesystem directory. +4. **Module scope**: Create a `RustModuleScope` for the directory, linked to the parent module scope. +5. **Parent registration**: Register this module in the parent scope's child modules list so that `mod name;` declarations can be auto-generated (see T025). +6. **mod.rs generation**: If the directory has children, generate a `mod.rs` file inside the directory. This file will contain `mod` declarations for any child modules and `use` statements. +7. **Reference**: Study `packages/go/src/components/SourceDirectory.tsx` for the pattern of wrapping `SourceDirectory` with language-specific scope. + +## Acceptance Criteria + +- [x] `ModuleDirectory` creates a filesystem directory. +- [x] `ModuleDirectory` creates a `RustModuleScope` for the directory. +- [x] `ModuleDirectory` registers as a child module in the parent scope. +- [x] Nested `ModuleDirectory` components create nested directories. +- [x] `pub` prop controls module visibility registration. + +## Definition of Done + +- `src/components/module-directory.tsx` exists and exports `ModuleDirectory`. +- `ModuleDirectoryProps` interface is exported. +- `test/module-directory.test.tsx` passes with all acceptance criteria covered. +- Component is re-exported from `src/components/index.ts`. + +## Completion Notes + +- Implemented `ModuleDirectory` in `packages/rust/src/components/module-directory.tsx` using `SourceDirectory` + `Scope` with a newly created `RustModuleScope`. +- Parent registration is implemented via `addChildModule(moduleName, visibility)` when the parent scope is `RustCrateScope` or `RustModuleScope`. +- Module names are derived from the last segment of `path` (e.g. `models/http` registers `http`). +- `pub` maps to `"pub"` visibility in parent child-module metadata; omitted `pub` keeps visibility undefined. +- Coverage is implemented in `packages/rust/test/module-directory.test.tsx`, including nested module directory registration and scope ancestry checks. +- `mod.rs` declaration/content generation remains owned by T025 (Auto mod declarations), which consumes the child-module metadata populated by this task. + +## Validation + +```bash +cd packages/rust +npx vitest run test/module-directory.test.tsx +``` diff --git a/docs/backlog/tasks/T025-mod-declarations.md b/docs/backlog/tasks/T025-mod-declarations.md new file mode 100644 index 000000000..a16a342b4 --- /dev/null +++ b/docs/backlog/tasks/T025-mod-declarations.md @@ -0,0 +1,92 @@ +# T025: Mod Declarations + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T025 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T009, T024 | +| **Blocks** | T027 | + +--- + +## Description + +Update `SourceFile` to auto-generate `mod name;` declarations for registered child modules. In Rust, a module must be declared in its parent module's file (e.g., `lib.rs` declares `mod models;`). This task automates that declaration based on the child modules registered in the `RustModuleScope`. + +## Goal + +Eliminate manual `mod` declaration management — child modules are automatically declared in their parent module's file. + +## Scope + +- Update `SourceFile` (or create a `ModDeclarations` component) to render `mod` declarations. +- Read child modules from `RustModuleScope.childModules`. +- Render `pub mod name;` or `mod name;` based on module visibility. +- Sort `mod` declarations alphabetically. +- Only render `mod` declarations in module root files: crate root (`lib.rs` / `main.rs`) or module roots (`mod.rs`). +- Position `mod` declarations before `use` statements and main content. +- Update `test/source-file.test.tsx` or create `test/mod-declarations.test.tsx`. + +## Out of Scope + +- Inline module bodies (`mod name { ... }`). +- Conditional compilation on modules (`#[cfg(...)] mod name;`). +- `#[path = "..."]` attributes. + +## Context Files + +- `packages/rust/src/components/source-file.tsx` — file to update (from T009). +- `packages/rust/src/symbols/rust-module-scope.ts` — scope with child module tracking (from T005). +- `packages/rust/src/components/module-directory.tsx` — registers child modules (from T024). + +## Implementation Guidance + +1. **File**: Update `packages/rust/src/components/source-file.tsx` or create `packages/rust/src/components/mod-declarations.tsx`. +2. **Reading child modules**: Access `RustModuleScope.childModules` — a list of `{ name: string, pub: boolean }` entries registered by `ModuleDirectory` and `SourceFile` children. +3. **Rendering**: + ``` + mod auth; + pub mod models; + pub mod routes; + ``` +4. **Sorting**: Alphabetical by module name. +5. **Positioning**: `mod` declarations appear first in the file, before `use` statements and main content. The order in a Rust file is: `mod` declarations → `use` statements → items. +6. **Module root detection**: Only render `mod` declarations if the current file is a module root. Detect by checking if the file is `lib.rs`, `main.rs`, or `mod.rs`. +7. **Use `code` template tag** for raw string fragments. + +## Acceptance Criteria + +- [x] Crate root (`lib.rs`) renders `mod` declarations for child modules. +- [x] Module root (`mod.rs`) renders `mod` declarations for nested child modules. +- [x] `pub mod name;` renders for public modules. +- [x] `mod name;` renders for private modules. +- [x] `mod` declarations are sorted alphabetically. +- [x] `mod` declarations appear before `use` statements. +- [x] Non-root files do not render `mod` declarations. +- [x] A crate with 3 modules generates correct `mod` declarations. + +## Definition of Done + +- `SourceFile` (or `ModDeclarations`) correctly renders `mod` declarations. +- Tests pass with all acceptance criteria covered. +- Changes are re-exported if new components are created. + +## Completion Notes + +- Completed in `packages/rust/src/components/source-file.tsx` with automatic `mod` declaration rendering from registered child modules. +- Declaration output now respects visibility (`pub mod` vs `mod`), root-file-only rendering (`lib.rs`, `main.rs`, `mod.rs`), ordering before `use` statements, and alphabetical sorting. +- Useful learning: when child modules are registered from nested paths, using the last path segment as the module name is essential to keep generated `mod` declarations correct. + +## Validation + +```bash +cd packages/rust +npx vitest run test/mod-declarations.test.tsx +npx vitest run test/source-file.test.tsx +``` diff --git a/docs/backlog/tasks/T026-import-integration-tests.md b/docs/backlog/tasks/T026-import-integration-tests.md new file mode 100644 index 000000000..28d487d28 --- /dev/null +++ b/docs/backlog/tasks/T026-import-integration-tests.md @@ -0,0 +1,109 @@ +# T026: Import Integration Tests + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T026 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | test | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T022, T023, T025 | +| **Blocks** | — | + +--- + +## Description + +Create comprehensive integration tests for the import and reference resolution system. These tests exercise the full pipeline: symbol creation → cross-module reference → resolution → use statement generation → rendering. + +## Goal + +Validate that the import system works end-to-end across all reference scenarios, catching integration issues that unit tests on individual components might miss. + +## Scope + +- Create `test/imports.test.tsx` with integration tests covering: + 1. Same-module reference: symbol and reference in the same file → no `use` generated. + 2. Same-crate, different module: symbol in `models/mod.rs`, reference in `routes/mod.rs` → `use crate::models::Symbol;`. + 3. Multiple imports from same path: two symbols from same module → `use crate::models::{SymbolA, SymbolB};`. + 4. Import sorting: mix of `std::`, external, and `crate::` imports → correct group order with blank lines. + 5. Prelude types: reference to `Option`, `Result`, `Vec` → no `use` statement. +- Create `test/reference.test.tsx` with reference resolution tests: + 1. Resolve refkey to same-module symbol. + 2. Resolve refkey to different-module symbol (same crate). + 3. Resolve refkey to external crate symbol. + 4. Prelude type bypass. +- **Negative test:** Reference to a private symbol from another module should produce a diagnostic error or warning (similar to Go's uppercase export check). + +## Out of Scope + +- Performance testing. +- Concurrent resolution. +- Error recovery for unresolvable references. + +## Context Files + +- `packages/rust/src/symbols/reference.tsx` — reference resolution (from T022). +- `packages/rust/src/components/use-statement.tsx` — use statement rendering (from T023). +- `packages/rust/src/components/source-file.tsx` — file rendering with imports. +- `packages/rust/src/components/crate-directory.tsx` — crate setup (from T009). +- `packages/go/test/` — reference for test patterns in existing Alloy packages. + +## Implementation Guidance + +1. **File**: `packages/rust/test/imports.test.tsx` and `packages/rust/test/reference.test.tsx`. +2. **Test structure**: Each test should set up a minimal crate with source files, declare symbols, reference them, and assert on the rendered output. +3. **Same-module test**: + ```tsx + // Define struct and function in same file + // Reference struct from function return type + // Assert: no use statement in output + ``` +4. **Cross-module test**: + ```tsx + // Define struct in models/mod.rs + // Reference struct from routes/mod.rs + // Assert: use crate::models::StructName; appears in routes/mod.rs output + ``` +5. **Grouped imports test**: + ```tsx + // Reference two symbols from same module + // Assert: use crate::models::{SymbolA, SymbolB}; + ``` +6. **Sorting test**: + ```tsx + // Reference symbols from std, external crate, and crate modules + // Assert: std first, then external, then crate, with blank lines + ``` +7. **Prelude test**: + ```tsx + // Reference Option, Result, Vec + // Assert: no use statements generated + ``` +8. **Use `toStringOutput()` or equivalent** from test utilities to capture rendered output. + +## Acceptance Criteria + +- [x] Same-module reference generates no `use` statement. +- [x] Cross-module reference generates correct `use crate::...` statement. +- [x] Multiple imports from same path are grouped with braces. +- [x] Import sorting follows `std` → external → `crate` convention. +- [x] Prelude types do not generate `use` statements. +- [x] Reference resolution returns correct symbol for all scenarios. + +## Definition of Done + +- `test/imports.test.tsx` exists and all tests pass. +- `test/reference.test.tsx` exists and all tests pass. +- Tests cover all five import scenarios and four reference scenarios. + +## Validation + +```bash +cd packages/rust +npx vitest run test/imports.test.tsx +npx vitest run test/reference.test.tsx +``` diff --git a/docs/backlog/tasks/T027-module-structure-tests.md b/docs/backlog/tasks/T027-module-structure-tests.md new file mode 100644 index 000000000..74289ac54 --- /dev/null +++ b/docs/backlog/tasks/T027-module-structure-tests.md @@ -0,0 +1,102 @@ +# T027: Module Structure Tests + +| Field | Value | +| ----------------- | ------------------------------------------------------------------------ | +| **ID** | T027 | +| **Epic** | [E005 — Module System & Imports](../epics/E005-module-system-imports.md) | +| **Type** | test | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T024, T025, T026 | +| **Blocks** | — | + +--- + +## Description + +Create comprehensive tests for the Rust module structure system, validating that crates with multiple files, nested directories, auto-generated `mod` declarations, and cross-module references work correctly end-to-end. + +## Goal + +Validate that the full module system (crate → module directory → source file → mod declarations → use statements → cross-module references) works as an integrated whole. + +## Scope + +- Create `test/module-structure.test.tsx` with tests covering: + 1. Crate with multiple source files at root level. + 2. Nested module directories (e.g., `src/models/user/`). + 3. Auto `mod` declarations in `lib.rs` for top-level modules. + 4. Auto `mod` declarations in submodule `mod.rs` for nested modules. + 5. Cross-module references with automatic `use` generation. + 6. Full golden scenario matching PRD section 7.2: multi-module crate with `User` struct in `models` and `greet` function in `services` that references `User`. + +## Out of Scope + +- Workspace-level multi-crate scenarios. +- Binary crate with `main.rs`. +- Build script (`build.rs`). + +## Context Files + +- `packages/rust/src/components/crate-directory.tsx` — crate setup (from T009). +- `packages/rust/src/components/source-file.tsx` — file component (from T009). +- `packages/rust/src/components/module-directory.tsx` — directory module (from T024). +- `packages/rust/src/components/use-statement.tsx` — import rendering (from T023). +- `packages/rust/src/symbols/reference.tsx` — reference resolution (from T022). +- `docs/prd/` — PRD section 7.2 for the golden scenario spec. + +## Implementation Guidance + +1. **File**: `packages/rust/test/module-structure.test.tsx`. +2. **Multi-file crate test**: + ```tsx + // CrateDirectory with lib.rs + two SourceFiles (models.rs, routes.rs) + // Assert: lib.rs contains mod models; mod routes; + ``` +3. **Nested directories test**: + ```tsx + // CrateDirectory > ModuleDirectory("models") > ModuleDirectory("user") + // Assert: lib.rs has mod models; + // Assert: models/mod.rs has mod user; + // Assert: models/user/mod.rs exists + ``` +4. **Cross-module reference test**: + ```tsx + // Define User struct in models module with a refkey + // Reference User from services module + // Assert: services/mod.rs contains use crate::models::User; + ``` +5. **PRD 7.2 golden scenario**: + ```tsx + // Full multi-module crate: + // src/lib.rs (mod models; mod services;) + // src/models.rs (pub struct User { name: String, age: u32 }) + // src/services.rs (use crate::models::User; pub fn greet(user: &User) -> String { ... }) + // Assert exact file outputs match expected + ``` +6. **Use `toStringOutput()` or equivalent** to capture and assert rendered file contents. +7. **Assert file tree structure** — validate that the correct files and directories are generated. + +## Acceptance Criteria + +- [x] Crate with multiple source files generates correct file tree. +- [x] Nested module directories create correct directory structure. +- [x] `lib.rs` contains auto-generated `mod` declarations for top-level modules. +- [x] `mod.rs` in subdirectories contains `mod` declarations for nested modules. +- [x] Cross-module references generate correct `use` statements. +- [x] PRD 7.2 golden scenario produces exact expected output. + +## Definition of Done + +- `test/module-structure.test.tsx` exists and all tests pass. +- Tests cover all six scenarios listed in scope. + +## Validation + +```bash +cd packages/rust +npx vitest run test/module-structure.test.tsx +``` diff --git a/docs/backlog/tasks/T028-create-crate.md b/docs/backlog/tasks/T028-create-crate.md new file mode 100644 index 000000000..42ba3c617 --- /dev/null +++ b/docs/backlog/tasks/T028-create-crate.md @@ -0,0 +1,101 @@ +# T028: createCrate Factory + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T028 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T003, T005, T006 | +| **Blocks** | T029, T031 | + +--- + +## Description + +Implement the `createCrate()` factory function for the `@alloy-js/rust` package. This factory allows external crates (like `serde`, `tokio`, `std`) to be described declaratively so that their symbols can be referenced and resolved without rendering the crate itself. The factory implements the `SymbolCreator` protocol for lazy symbol creation via the binder. + +## Goal + +Enable declarative description of external crates so that referencing their symbols automatically generates correct `use` statements and `Cargo.toml` dependencies. + +## Scope + +- Create `src/create-crate.ts`. +- `createCrate(descriptor)` function: + - Takes a `CrateDescriptor` — a nested object mapping module paths to named exports. + - Returns a `CrateRef` — an object keyed by module path then symbol name, each value being a `Refkey`. + - Implements the `SymbolCreator` protocol so the binder lazily creates symbols when first referenced. +- `CrateDescriptor` type definition: + ```ts + interface CrateDescriptor { + name: string; + version?: string; + modules: Record>; + } + ``` +- `SymbolDescriptor` — describes the kind, name, and metadata of each symbol. +- Create `test/create-crate.test.tsx`. + +## Out of Scope + +- Feature flags on crate dependencies. +- Optional dependencies. +- Workspace dependency inheritance. +- Proc-macro crates. + +## Context Files + +- `packages/go/src/create-module.ts` — closest pattern in existing Alloy packages. +- `packages/core/src/binder.ts` — binder and SymbolCreator protocol. +- `packages/rust/src/symbols/rust-crate-scope.ts` — crate scope (from T005). +- `packages/rust/src/symbols/rust-module-scope.ts` — module scope (from T005). + +## Implementation Guidance + +1. **File**: `packages/rust/src/create-crate.ts`. +2. **Descriptor shape**: Design `CrateDescriptor` to mirror the module structure of the external crate: + ```ts + const serde = createCrate({ + name: "serde", + version: "1.0", + modules: { + "": { Serialize: { kind: "trait" }, Deserialize: { kind: "trait" } }, + json: { + to_string: { kind: "function" }, + from_str: { kind: "function" }, + }, + }, + }); + // serde[""].Serialize → Refkey + // serde.json.to_string → Refkey + ``` +3. **SymbolCreator protocol**: The returned object must implement `SymbolCreator` so the binder can lazily instantiate symbols when they are first resolved. Study `packages/go/src/create-module.ts` for the exact protocol. +4. **Refkey generation**: Generate stable refkeys for each symbol in the descriptor. +5. **Crate metadata**: Store crate name and version so that when a symbol is referenced, the crate can be added to `Cargo.toml` dependencies. +6. **Return type**: `CrateRef` should be a deeply-typed object where `crateRef.modulePath.symbolName` returns a `Refkey`. + +## Acceptance Criteria + +- [x] `createCrate()` returns a `CrateRef` with refkeys for all described symbols. +- [x] Symbols are lazily created when first resolved by the binder. +- [x] Crate name and version are accessible for dependency tracking. +- [x] Module paths correctly map to Rust module hierarchy. +- [x] Root module symbols (path `""`) are accessible at `crateRef[""].SymbolName`. + +## Definition of Done + +- `src/create-crate.ts` exists and exports `createCrate`, `CrateDescriptor`, `CrateRef`. +- `test/create-crate.test.tsx` passes with all acceptance criteria covered. +- Module is re-exported from `src/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/create-crate.test.tsx +``` diff --git a/docs/backlog/tasks/T029-std-builtins.md b/docs/backlog/tasks/T029-std-builtins.md new file mode 100644 index 000000000..e1c6be6af --- /dev/null +++ b/docs/backlog/tasks/T029-std-builtins.md @@ -0,0 +1,179 @@ +# T029: Standard Library Builtins + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T029 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | blocked | +| **Priority** | P0 — required for v1 | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T028, T036 | +| **Blocks** | — | + +--- + +## Description + +Define the Rust standard library (`std`) as a crate descriptor using `createCrate()`, covering the most commonly used types, traits, and functions. Also define the `PRELUDE_TYPES` list used by reference resolution (T022) to skip import generation for types that are automatically in scope. + +## Goal + +Provide a ready-to-use `std` crate descriptor so that generated code can reference standard library types and traits with automatic import resolution. + +## Scope + +- Create `src/builtins/std.ts` — the std crate descriptor. +- Create `src/builtins/index.ts` — barrel export. +- Define std crate modules and symbols: + - **Root / prelude**: `Option`, `Some`, `None`, `Result`, `Ok`, `Err`, `Vec`, `String`, `Box`. + - **`rc`**: `Rc`. + - **`sync`**: `Arc`. + - **`collections`**: `HashMap`, `HashSet`, `BTreeMap`, `BTreeSet`. + - **`fmt`**: `Display`, `Debug`, `Formatter`. + - **`io`**: `Read`, `Write`, `Result` (as `IoResult`). + - **`clone`**: `Clone`. + - **`default`**: `Default`. + - **`convert`**: `From`, `Into`. +- Define `PRELUDE_TYPES` — a `Set` of type names in the Rust prelude that do not need `use` statements: + - `Option`, `Some`, `None`, `Result`, `Ok`, `Err`, `Vec`, `String`, `Box`, `Clone`, `Default`, `From`, `Into`, `Drop`, `Fn`, `FnMut`, `FnOnce`, `Iterator`, `Send`, `Sync`, `Sized`, `Copy`, `ToString`. +- Export `PRELUDE_TYPES` for use in reference resolution (T022). +- Create `test/builtins.test.tsx`. + +## Out of Scope + +- Exhaustive std coverage (only common types). +- Nightly-only std features. +- `std::os` platform-specific modules. +- `std::ffi` and `std::ptr`. + +## Context Files + +- `packages/rust/src/create-crate.ts` — crate factory (from T028). +- `packages/rust/src/symbols/reference.tsx` — consumes `PRELUDE_TYPES` (from T022). +- Rust std library documentation for module paths and type names. + +## Implementation Guidance + +1. **File**: `packages/rust/src/builtins/std.ts`. +2. **Crate descriptor**: + + ```ts + import { createCrate } from "../create-crate.js"; + + export const std = createCrate({ + name: "std", + modules: { + "": { + Option: { kind: "enum" }, + Result: { kind: "enum" }, + Vec: { kind: "struct" }, + String: { kind: "struct" }, + Box: { kind: "struct" }, + }, + rc: { Rc: { kind: "struct" } }, + sync: { Arc: { kind: "struct" } }, + collections: { + HashMap: { kind: "struct" }, + HashSet: { kind: "struct" }, + BTreeMap: { kind: "struct" }, + BTreeSet: { kind: "struct" }, + }, + fmt: { + Display: { kind: "trait" }, + Debug: { kind: "trait" }, + Formatter: { kind: "struct" }, + }, + io: { + Read: { kind: "trait" }, + Write: { kind: "trait" }, + }, + clone: { Clone: { kind: "trait" } }, + default: { Default: { kind: "trait" } }, + convert: { + From: { kind: "trait" }, + Into: { kind: "trait" }, + }, + }, + }); + ``` + +3. **PRELUDE_TYPES**: + ```ts + export const PRELUDE_TYPES = new Set([ + "Option", + "Some", + "None", + "Result", + "Ok", + "Err", + "Vec", + "String", + "Box", + "Clone", + "Default", + "From", + "Into", + "Drop", + "Fn", + "FnMut", + "FnOnce", + "Iterator", + "Send", + "Sync", + "Sized", + "Copy", + "ToString", + ]); + ``` +4. **Barrel export**: `src/builtins/index.ts` re-exports `std` and `PRELUDE_TYPES`. +5. **Tests**: Verify that refkeys are generated for all described symbols and that `PRELUDE_TYPES` contains the expected entries. + +## Acceptance Criteria + +- [ ] `std` crate descriptor provides refkeys for all listed symbols. +- [ ] `PRELUDE_TYPES` contains all standard prelude types. +- [ ] `std[""].Option` returns a valid `Refkey`. +- [ ] `std.fmt.Display` returns a valid `Refkey`. +- [ ] `std.collections.HashMap` returns a valid `Refkey`. +- [ ] Referencing a prelude type does not generate a `use` statement. +- [ ] Referencing a non-prelude std type generates `use std::module::Type;`. + +## Definition of Done + +- `src/builtins/std.ts` exists and exports `std` crate descriptor. +- `src/builtins/index.ts` exists and exports `std` and `PRELUDE_TYPES`. +- `test/builtins.test.tsx` passes with all acceptance criteria covered. +- Builtins are re-exported from `src/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/builtins.test.tsx +``` + +## Blocked Reason (2026-03-11) + +Validation failed repeatedly on the same issue while implementing `std` builtins: +export typing for `std` in `src/builtins/std.ts` causes TypeScript/API Extractor +portability failures (`TS2742` and export-surface warnings). After 3 build/test +validation attempts on this issue, work stopped per execution rules. + +## Unblock Plan (v1) + +Two changes are needed before retrying: + +1. **T036 (builtin crate support):** `createCrate()` must support `builtin: true` so that `std` does not appear in Cargo.toml `[dependencies]`. Without this, referencing `HashMap` would incorrectly add `std = "*"` to Cargo.toml. + +2. **TS2742 fix:** Use explicit exported type annotations on the `std` constant instead of relying on type inference from `createCrate()`. The inferred generic return type is too complex for API Extractor's portability check. Pattern: + +```typescript +const stdDescriptor = { name: "std", builtin: true, modules: { ... } } as const; +export type StdCrate = CrateRef & SymbolCreator & ExternalCrate; +export const std: StdCrate = createCrate(stdDescriptor); +``` + +See also: custom instruction note on avoiding TS2742/API Extractor portability errors with explicit type aliases. diff --git a/docs/backlog/tasks/T030-cargo-toml.md b/docs/backlog/tasks/T030-cargo-toml.md new file mode 100644 index 000000000..42d533542 --- /dev/null +++ b/docs/backlog/tasks/T030-cargo-toml.md @@ -0,0 +1,106 @@ +# T030: Cargo.toml File Generation + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T030 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T009, T031 | +| **Blocks** | T034 | + +--- + +## Description + +Implement `CargoTomlFile` component that generates a valid `Cargo.toml` manifest file for a Rust crate. The component renders the `[package]` section and `[dependencies]` section, combining explicitly provided dependencies with those automatically tracked by the reference resolution system. + +## Goal + +Enable automatic `Cargo.toml` generation with correct package metadata and dependency declarations, including dependencies discovered through cross-crate references. + +## Scope + +- Create `src/components/cargo-toml-file.tsx`. +- Define `CargoTomlFileProps`: + - `name: string` — package name. + - `version?: string` — package version (default `"0.1.0"`). + - `edition?: string` — Rust edition (default `"2021"`). + - `dependencies?: Record` — explicitly declared dependencies. +- `CrateDependency` type: `string` (version only) or `{ version: string, features?: string[] }`. +- Render `[package]` section with name, version, edition. +- Render `[dependencies]` section by merging: + - Explicit dependencies from `props.dependencies`. + - Auto-tracked dependencies from `RustCrateScope.dependencies`. +- Sort dependencies alphabetically. +- Update `CrateDirectory` to include `CargoTomlFile` in its output. +- Create `test/cargo-toml.test.tsx`. + +## Out of Scope + +- `[dev-dependencies]`. +- `[build-dependencies]`. +- `[features]` section. +- `[workspace]` configuration. +- `[profile]` sections. +- `[lib]` / `[[bin]]` target configuration. + +## Context Files + +- `packages/rust/src/components/crate-directory.tsx` — crate directory to update (from T009). +- `packages/rust/src/symbols/rust-crate-scope.ts` — crate scope with dependency tracking (from T005). + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/cargo-toml-file.tsx`. +2. **Props**: Do NOT destructure — use `props.name`, `props.version`, etc. +3. **TOML rendering**: Use `code` template tag to render TOML format. Do NOT use a TOML library — the output is simple enough to template directly: + + ```toml + [package] + name = "my-crate" + version = "0.1.0" + edition = "2021" + + [dependencies] + serde = "1.0" + tokio = { version = "1.0", features = ["full"] } + ``` + +4. **Dependency merging**: Combine `props.dependencies` (explicit) with `crateScope.dependencies` (auto-tracked). If a dependency appears in both, the explicit one takes precedence. +5. **Dependency rendering**: + - Simple version: `name = "version"` + - With features: `name = { version = "version", features = ["feat1", "feat2"] }` +6. **Sort dependencies** alphabetically by crate name. +7. **CrateDirectory update**: Modify `CrateDirectory` to render `` as part of its output, passing through relevant props. +8. **File output**: Use `SourceFile` with `filetype="toml"` or render as a raw file at `Cargo.toml` path. + +## Acceptance Criteria + +- [x] `CargoTomlFile` renders correct `[package]` section. +- [x] `CargoTomlFile` renders `[dependencies]` with explicit dependencies. +- [x] `CargoTomlFile` merges auto-tracked dependencies from `RustCrateScope`. +- [x] Simple version dependency renders as `name = "version"`. +- [x] Dependency with features renders as `name = { version = "version", features = [...] }`. +- [x] Dependencies are sorted alphabetically. +- [x] Default version is `"0.1.0"` and default edition is `"2021"`. +- [x] `CrateDirectory` includes `CargoTomlFile` in output. + +## Definition of Done + +- `src/components/cargo-toml-file.tsx` exists and exports `CargoTomlFile`. +- `CargoTomlFileProps` and `CrateDependency` types are exported. +- `test/cargo-toml.test.tsx` passes with all acceptance criteria covered. +- `CrateDirectory` updated to render `CargoTomlFile`. +- Component is re-exported from `src/components/index.ts`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/cargo-toml.test.tsx +``` diff --git a/docs/backlog/tasks/T031-dependency-tracking.md b/docs/backlog/tasks/T031-dependency-tracking.md new file mode 100644 index 000000000..f565c9d8a --- /dev/null +++ b/docs/backlog/tasks/T031-dependency-tracking.md @@ -0,0 +1,92 @@ +# T031: Dependency Tracking + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T031 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T005, T022, T028 | +| **Blocks** | — | + +--- + +## Description + +Wire up the full dependency tracking pipeline: when reference resolution detects an external crate symbol, the crate dependency is automatically registered in `RustCrateScope.dependencies`, which is then consumed by `CargoTomlFile` to render the `[dependencies]` section. + +## Goal + +Ensure that referencing any symbol from an external crate automatically adds that crate to `Cargo.toml` dependencies without manual intervention. + +## Scope + +- Update `RustCrateScope` to expose a `dependencies` map: `Map`. +- Add `addDependency(name: string, dependency: CrateDependency)` method to `RustCrateScope`. +- Update reference resolution (T022) to call `crateScope.addDependency()` when resolving an external crate symbol. +- Ensure `createCrate()` descriptors (T028) provide crate name and version for dependency registration. +- `CargoTomlFile` (T030) reads `crateScope.dependencies`. +- Create `test/dependency-tracking.test.tsx`. + +## Out of Scope + +- Feature flag tracking per-reference. +- Dev dependencies. +- Build dependencies. +- Dependency version conflict resolution. + +## Context Files + +- `packages/rust/src/symbols/rust-crate-scope.ts` — crate scope to update (from T005). +- `packages/rust/src/symbols/reference.tsx` — reference resolution to update (from T022). +- `packages/rust/src/create-crate.ts` — crate factory with version info (from T028). +- `packages/rust/src/components/cargo-toml-file.tsx` — consumes dependencies (from T030). + +## Implementation Guidance + +1. **RustCrateScope update**: Add a `dependencies` property (e.g., `Map`) and an `addDependency()` method. If the same crate is added multiple times, keep the first registration (or merge features). +2. **Reference resolution update**: In the external-crate branch of `ref()`, after generating the use path, also call: + ```ts + crateScope.addDependency(externalCrate.name, { + version: externalCrate.version, + }); + ``` +3. **createCrate integration**: Ensure the crate descriptor's `name` and `version` are accessible during resolution so the dependency can be registered with the correct version. +4. **End-to-end test**: Create a test that: + - Defines an external crate via `createCrate({ name: "serde", version: "1.0", ... })`. + - References `serde::Serialize` from a source file. + - Asserts that the rendered `Cargo.toml` includes `serde = "1.0"` in `[dependencies]`. +5. **Deduplication**: If the same crate is referenced from multiple files, only one dependency entry should appear. + +## Acceptance Criteria + +- [x] `RustCrateScope.dependencies` tracks registered dependencies. +- [x] `addDependency()` correctly adds a crate dependency. +- [x] Reference to external crate symbol auto-registers dependency. +- [x] Duplicate references to same crate produce single dependency entry. +- [x] `CargoTomlFile` renders auto-tracked dependencies. +- [x] End-to-end: reference `serde::Serialize` → `Cargo.toml` includes `serde = "1.0"`. + +## Definition of Done + +- `RustCrateScope` has `dependencies` and `addDependency()`. +- Reference resolution calls `addDependency()` for external crates. +- `test/dependency-tracking.test.tsx` passes with all acceptance criteria covered. + +## Implementation Evidence + +- `packages/rust/src/scopes/rust-crate-scope.ts` +- `packages/rust/src/symbols/reference.ts` +- `packages/rust/test/reference.test.tsx` +- `packages/rust/test/create-crate.test.tsx` + +## Validation + +```bash +cd packages/rust +npx vitest run test/dependency-tracking.test.tsx +``` diff --git a/docs/backlog/tasks/T032-stc-wrappers.md b/docs/backlog/tasks/T032-stc-wrappers.md new file mode 100644 index 000000000..c11b8007f --- /dev/null +++ b/docs/backlog/tasks/T032-stc-wrappers.md @@ -0,0 +1,95 @@ +# T032: STC Wrappers + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T032 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T011, T012, T013, T014, T015, T016, T017, T018, T019, T020, T021 | +| **Blocks** | — | + +--- + +## Description + +Create STC (Source Tree Construction) wrappers for all Rust components using the `stc()` utility from `@alloy-js/core`. STC wrappers provide a programmatic API for constructing component trees without JSX, which is useful for code generation from data models and TypeSpec emitters. + +## Goal + +Provide a complete STC API surface for the Rust package, enabling programmatic tree construction alongside JSX-based construction. + +## Scope + +- Create `src/components/stc/index.ts`. +- Wrap the following components with `stc()`: + - `StructDeclaration` + - `Field` + - `EnumDeclaration` + - `EnumVariant` + - `FunctionDeclaration` + - `TraitDeclaration` + - `ImplBlock` + - `TypeAlias` + - `ConstDeclaration` + - `Attribute` + - `DeriveAttribute` + - `DocComment` +- Re-export from `src/components/index.ts` and `src/index.ts`. +- Create `test/stc.test.tsx` with basic tests. + +## Out of Scope + +- STC wrappers for internal/non-public components. +- STC wrappers for layout components (`SourceFile`, `CrateDirectory`). + +## Context Files + +- `packages/go/src/components/stc/index.ts` — exact pattern to follow. +- `packages/csharp/src/components/stc/index.ts` — another reference. +- `packages/core/src/stc.ts` — the `stc()` utility implementation. + +## Implementation Guidance + +1. **File**: `packages/rust/src/components/stc/index.ts`. +2. **Pattern**: Follow `packages/go/src/components/stc/index.ts` exactly: + + ```ts + import { stc } from "@alloy-js/core"; + import { StructDeclaration as StructDeclarationComponent } from "../struct-declaration.js"; + // ... other imports + + export const StructDeclaration = stc(StructDeclarationComponent); + export const Field = stc(FieldComponent); + // ... etc + ``` + +3. **Naming**: The STC export should use the same name as the component. Import the component with an alias if needed to avoid conflicts. +4. **Completeness**: Wrap every public component that takes props. Skip components that are purely internal. +5. **Re-export**: Ensure `src/components/index.ts` has `export * from "./stc/index.js"` or a namespaced export (check Go package pattern). +6. **Tests**: Verify that each STC wrapper can be called programmatically and produces the same output as the JSX equivalent. + +## Acceptance Criteria + +- [x] All listed components have STC wrappers. +- [x] STC wrappers produce identical output to JSX equivalents. +- [x] Wrappers are exported from `src/components/stc/index.ts`. +- [x] Wrappers are re-exported from package entry point. +- [x] Basic test verifies at least 3 STC wrappers work correctly. + +## Definition of Done + +- `src/components/stc/index.ts` exists with all wrappers. +- `test/stc.test.tsx` passes. +- Wrappers are accessible from `@alloy-js/rust`. + +## Validation + +```bash +cd packages/rust +npx vitest run test/stc.test.tsx +``` diff --git a/docs/backlog/tasks/T033-barrel-exports.md b/docs/backlog/tasks/T033-barrel-exports.md new file mode 100644 index 000000000..80d6e1173 --- /dev/null +++ b/docs/backlog/tasks/T033-barrel-exports.md @@ -0,0 +1,112 @@ +# T033: Barrel Exports + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T033 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | infra | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T032 | +| **Blocks** | — | + +--- + +## Description + +Verify and complete all barrel export files (`index.ts`) in the `@alloy-js/rust` package so that consumers can import everything they need from the package entry point. This includes components, symbols, scopes, name policy, create-crate factory, parameter descriptors, builtins, context utilities, and STC wrappers. + +## Goal + +Ensure the `@alloy-js/rust` package has a clean, complete public API surface that can be consumed by external projects and TypeSpec emitters. + +## Scope + +- Verify `src/index.ts` re-exports: + - `src/components/index.ts` (all components + STC wrappers). + - `src/symbols/index.ts` (all symbols, scopes, factories). + - `src/builtins/index.ts` (std descriptor, `PRELUDE_TYPES`). + - `src/create-crate.ts` (factory + types). + - `src/name-policy.ts` (Rust naming conventions). + - Any context or utility modules. +- Verify `src/components/index.ts` exports all components: + - `StructDeclaration`, `Field`, `EnumDeclaration`, `EnumVariant`, `FunctionDeclaration`, `TraitDeclaration`, `ImplBlock`, `TypeAlias`, `ConstDeclaration`, `Attribute`, `DeriveAttribute`, `DocComment`, `UseStatement`, `UseStatements`, `SourceFile`, `CrateDirectory`, `ModuleDirectory`, `CargoTomlFile`. + - STC wrappers from `./stc/index.js`. +- Verify `src/symbols/index.ts` exports all symbols and scopes: + - `RustCrateScope`, `RustModuleScope`, `RustImplScope`, `RustTraitScope`. + - `NamedTypeSymbol`, reference utilities. +- Add any missing exports. +- Create a compile test: a TypeScript file that imports key items from `@alloy-js/rust` to verify they resolve. + +## Out of Scope + +- API documentation generation. +- Deprecation warnings. +- Subpath exports in `package.json`. + +## Context Files + +- `packages/go/src/index.ts` — reference for barrel export pattern. +- `packages/csharp/src/index.ts` — another reference. +- `packages/rust/src/index.ts` — the file to verify and update. +- `packages/rust/package.json` — package entry point configuration. + +## Implementation Guidance + +1. **Audit**: Read through all `src/**/*.ts` and `src/**/*.tsx` files to identify every public export. +2. **Component index**: Ensure `src/components/index.ts` has an export line for every component file: + ```ts + export { + StructDeclaration, + StructDeclarationProps, + } from "./struct-declaration.js"; + export { Field, FieldProps } from "./field.js"; + // ... etc + export * from "./stc/index.js"; + ``` +3. **Symbol index**: Ensure `src/symbols/index.ts` exports all scopes and symbol types. +4. **Root index**: Ensure `src/index.ts` aggregates all sub-indexes: + ```ts + export * from "./components/index.js"; + export * from "./symbols/index.js"; + export * from "./builtins/index.js"; + export * from "./create-crate.js"; + export * from "./name-policy.js"; + ``` +5. **Compile test**: Create a test file that imports key types and verifies they are defined: + ```ts + import { + StructDeclaration, + RustCrateScope, + createCrate, + std, + } from "@alloy-js/rust"; + // If this compiles, exports are correct + ``` +6. **Check for conflicts**: Ensure no naming conflicts between re-exported modules. + +## Acceptance Criteria + +- [x] `src/index.ts` re-exports all current public modules (`components`, `symbols`, `scopes`, `context`, name policy/conflict, parameter descriptor, `create-crate`). +- [x] `src/components/index.ts` exports all components and STC wrappers. +- [x] `src/symbols/index.ts` exports all symbol primitives and reference utilities. +- [x] `PRELUDE_TYPES` is publicly exported from `src/symbols/reference.ts` via `src/symbols/index.ts`. +- [x] A consumer file can import key items from `@alloy-js/rust` and `@alloy-js/rust/stc`. +- [x] No export naming conflicts were introduced. +- [x] Package compiles and tests cleanly. + +## Definition of Done + +- All barrel export files are complete and consistent. +- Compile test passes. +- `tsc --noEmit` succeeds for the package. + +## Validation + +```bash +cd packages/rust +npx tsc --noEmit +``` diff --git a/docs/backlog/tasks/T034-golden-scenarios.md b/docs/backlog/tasks/T034-golden-scenarios.md new file mode 100644 index 000000000..864c8ff84 --- /dev/null +++ b/docs/backlog/tasks/T034-golden-scenarios.md @@ -0,0 +1,126 @@ +# T034: Golden Scenario Tests + +| Field | Value | +| ----------------- | ----------------------------------------------------------------------------------- | +| **ID** | T034 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | test | +| **Status** | done | +| **Priority** | high | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T026, T027, T030, T031 | +| **Blocks** | — | + +--- + +## Description + +Create golden scenario tests that validate complete, realistic Rust code generation end-to-end. These scenarios match the examples from PRD section 7 and serve as the ultimate integration tests for the `@alloy-js/rust` package. + +## Goal + +Prove that the package can generate correct, complete Rust projects including file structure, `Cargo.toml`, module declarations, imports, types, functions, traits, and impl blocks. + +## Scope + +- Create `test/golden-scenarios.test.tsx` with the following scenarios: + +### Scenario 7.1: Struct with Impl + +- `Point` struct with `x: f64`, `y: f64` fields. +- Inherent impl with `new(x: f64, y: f64) -> Self` and `distance(&self, other: &Point) -> f64`. +- Validates: struct + impl + method rendering. + +### Scenario 7.2: Multi-Module Crate with Imports + +- Crate with `models` and `services` modules. +- `User` struct in `models` with `name: String`, `age: u32`. +- `greet(user: &User) -> String` function in `services`. +- Validates: module structure + cross-module import + `Cargo.toml`. + +### Scenario 7.3: Trait and Impl + +- `Greetable` trait with `fn greet(&self) -> String`. +- `User` struct. +- `impl Greetable for User` with implementation. +- Validates: trait declaration + trait impl rendering. + +### Scenario 7.4: Enum with All Variant Kinds + +- `Shape` enum with: + - `Circle { radius: f64 }` (struct variant). + - `Rectangle { width: f64, height: f64 }` (struct variant). + - `Point(f64, f64)` (tuple variant). + - `Nothing` (unit variant). +- Validates: all enum variant types. + +### Scenario 7.5: Cargo.toml with Dependencies + +- Crate that references `serde::Serialize`. +- Validates: `Cargo.toml` includes `serde` in `[dependencies]`. + +## Out of Scope + +- Negative tests (invalid input). +- Performance benchmarks. +- Compilation of generated Rust code (validated by inspection). + +## Context Files + +- `docs/prd/` — PRD section 7 for scenario specifications. +- `packages/go/test/` — reference for golden test patterns in existing Alloy packages. +- All component source files in `packages/rust/src/components/`. + +## Implementation Guidance + +1. **File**: `packages/rust/test/golden-scenarios.test.tsx`. +2. **Test pattern**: Each test should: + - Construct a complete component tree using JSX. + - Render to string output. + - Assert exact match against expected output. +3. **Scenario 7.1 structure**: + ```tsx + + + + + + + + + ... + + + ... + + + + + ``` +4. **Exact output assertion**: Use snapshot testing or string comparison to validate the exact rendered output matches expected Rust code. +5. **Each scenario is a separate `it()` or `test()` block** for clear failure reporting. +6. **Use refkeys** to wire up cross-references between components. + +## Acceptance Criteria + +- [ ] Scenario 7.1 (struct with impl) produces correct output. +- [ ] Scenario 7.2 (multi-module crate) produces correct file tree and contents. +- [ ] Scenario 7.3 (trait and impl) produces correct output. +- [ ] Scenario 7.4 (enum with all variant kinds) produces correct output. +- [ ] Scenario 7.5 (Cargo.toml with dependencies) produces correct manifest. +- [ ] All five scenarios pass. + +## Definition of Done + +- `test/golden-scenarios.test.tsx` exists with all five scenarios. +- All tests pass. +- Output matches expected Rust code exactly. + +## Validation + +```bash +cd packages/rust +npx vitest run test/golden-scenarios.test.tsx +``` diff --git a/docs/backlog/tasks/T035-edge-cases.md b/docs/backlog/tasks/T035-edge-cases.md new file mode 100644 index 000000000..72d90e822 --- /dev/null +++ b/docs/backlog/tasks/T035-edge-cases.md @@ -0,0 +1,151 @@ +# T035: Edge Case Tests + +| Field | Value | +| ----------------- | ---------------------------------------------------------------------------------------- | +| **ID** | T035 | +| **Epic** | [E006 — External Deps, Build & Polish](../epics/E006-external-deps-build-polish.md) | +| **Type** | test | +| **Status** | done | +| **Priority** | medium | +| **Owner** | AI coding agent | +| **AI Executable** | yes | +| **Human Review** | yes | +| **Dependencies** | T011, T012, T013, T014, T015, T016, T017, T018, T019, T020, T021, T022, T023, T024, T025 | +| **Blocks** | — | + +--- + +## Description + +Create tests for edge cases and boundary conditions across all Rust package components. These tests ensure robustness and catch regressions for unusual but valid inputs. + +## Goal + +Validate that all components handle edge cases gracefully — empty content, single elements, reserved words, deduplication, and overlapping constructs. + +## Scope + +- Create `test/edge-cases.test.tsx` with tests for: + +### Empty constructs + +1. **Empty struct**: `struct Empty {}` — struct with no fields. +2. **Empty enum**: `enum Never {}` — enum with no variants. +3. **Function with no params and no return type**: `fn noop() {}`. + +### Minimal constructs + +4. **Struct with single field**: `struct Wrapper { value: i32 }`. +5. **Enum with single unit variant**: `enum Unit { Only }`. + +### Reserved words + +6. **Reserved word as identifier**: Using `type` as a field name should render as `r#type`. +7. **Other reserved words**: `self`, `super`, `crate`, `fn`, `struct`, `enum`, `trait`, `impl` — ensure the name policy escapes them with `r#` prefix. + +### Import deduplication + +8. **Duplicate references**: Referencing the same symbol from multiple locations in the same file should produce only one `use` statement. +9. **Multiple references, same path**: Referencing two symbols from the same module should produce a grouped import, not two separate ones. + +### Multiple impl blocks + +10. **Multiple impl blocks for same type**: Two separate `ImplBlock` components targeting the same type should both render correctly without conflict. + +### Visibility and prelude + +11. **Negative test: private symbol cross-module reference**: Reference to a private symbol from another module should produce a diagnostic error or warning. +12. **Test: prelude types**: `Option`, `Vec`, `String` referenced without generating `use`. + +## Out of Scope + +- Fuzzing / random input testing. +- Performance stress testing. +- Invalid input error messages (not testing error handling, just valid edge cases). + +## Context Files + +- `packages/rust/src/components/` — all component source files. +- `packages/rust/src/name-policy.ts` — reserved word handling (from T003). +- `packages/rust/src/symbols/reference.tsx` — import deduplication (from T022). +- `packages/rust/src/components/use-statement.tsx` — import grouping (from T023). + +## Implementation Guidance + +1. **File**: `packages/rust/test/edge-cases.test.tsx`. +2. **Test structure**: Group tests by category using `describe()` blocks: + ```tsx + describe("edge cases", () => { + describe("empty constructs", () => { + it("renders empty struct", () => { ... }); + it("renders empty enum", () => { ... }); + it("renders function with no params or return", () => { ... }); + }); + describe("minimal constructs", () => { ... }); + describe("reserved words", () => { ... }); + describe("import deduplication", () => { ... }); + describe("multiple impl blocks", () => { ... }); + }); + ``` +3. **Empty struct test**: + ```tsx + + // Expected: struct Empty {} + ``` +4. **Reserved word test**: + ```tsx + + + + // Expected field name: r#type + ``` +5. **Import deduplication test**: + ```tsx + // Reference same symbol twice in one file + // Assert only one use statement appears + ``` +6. **Multiple impl blocks test**: + ```tsx + + + + + + + // Both impl blocks render correctly + ``` +7. **Assert exact output** for each test case. + +## Acceptance Criteria + +- [x] Empty struct renders `struct Empty {}`. +- [x] Empty enum renders `enum Never {}`. +- [x] Function with no params/return renders `fn noop() {}`. +- [x] Single-field struct renders correctly. +- [x] Reserved word `type` renders as `r#type`. +- [x] Duplicate references produce single `use` statement. +- [x] Same-path imports are grouped. +- [x] Multiple impl blocks for same type render without conflict. + +## Implementation Notes + +- Added `packages/rust/test/edge-cases.test.tsx` with category-based coverage for: + - empty/minimal constructs; + - reserved-word escaping (`type`, `self`, `super`, `crate`, `fn`, `struct`, `enum`, `trait`, `impl`); + - import deduplication and same-path grouping; + - multiple impl blocks on one type; + - private cross-module reference failure; + - prelude import skipping (`Option`, `Vec`, `String`). +- Used deterministic assertions with `toRenderTo(d\`...\`)` and exact rendered-file checks for module import scenarios. + +## Definition of Done + +- `test/edge-cases.test.tsx` exists with all twelve test cases. +- All tests pass. + +## Validation + +```bash +cd packages/rust +npx vitest run test/edge-cases.test.tsx +``` diff --git a/docs/backlog/tasks/T036-builtin-crate-support.md b/docs/backlog/tasks/T036-builtin-crate-support.md new file mode 100644 index 000000000..81311877c --- /dev/null +++ b/docs/backlog/tasks/T036-builtin-crate-support.md @@ -0,0 +1,160 @@ +# T036 — Builtin Crate Support in createCrate / ref + +| Field | Value | +| ------------------------- | --------------------------------------------------- | +| **ID** | T036 | +| **Epic** | [E006](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P0 — blocks T029 (std builtins) | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T028 (createCrate factory) | +| **Blocks** | T029 (std builtins) | + +--- + +## Description + +Add support for marking crates as "builtin" (implicit) so that referencing their symbols generates `use` statements but does NOT add the crate to `Cargo.toml` `[dependencies]`. + +Rust's `std` crate is always available without an explicit dependency declaration. The current `createCrate()` → `ref()` → `addDependency()` pipeline unconditionally records every external crate as a Cargo.toml dependency. This would incorrectly produce `std = "*"` in Cargo.toml when std builtins are referenced. + +Go solves this with a `builtin: boolean` parameter in `createModule()` that propagates to `PackageSymbol` (see `packages/go/src/create-module.ts` line 102). + +--- + +## Goal + +After this task, `createCrate({ name: "std", builtin: true, ... })` creates a crate descriptor whose symbols generate `use` statements on reference but are excluded from Cargo.toml dependency tracking. + +--- + +## Scope Included + +1. Add optional `builtin?: boolean` field to `CrateDescriptor` interface +2. Store `builtin` flag on `CrateFactoryState` in `create-crate.ts` +3. Expose `isBuiltinCrate(crate: ExternalCrate): boolean` utility function +4. Modify `ref()` in `reference.ts` to skip `sourceCrate.addDependency()` when the target crate is builtin +5. Add tests for builtin crate behavior + +--- + +## Out of Scope + +- Implementing the actual `std` descriptor (that's T029) +- Changes to `CargoTomlFile` rendering (it already reads from `RustCrateScope.dependencies`) +- Changes to `UseStatement` / `UseStatements` (use generation already works correctly) + +--- + +## Context Files + +| File | Relevance | +| ---------------------------------------------- | ------------------------------------------ | +| `packages/rust/src/create-crate.ts` | Add `builtin` to descriptor and state | +| `packages/rust/src/symbols/reference.ts` | Conditional `addDependency()` on line ~101 | +| `packages/rust/src/scopes/rust-crate-scope.ts` | Where dependencies are stored | +| `packages/go/src/create-module.ts` | Go's builtin pattern (lines 102-113) | +| `packages/rust/test/create-crate.test.tsx` | Add builtin test cases | + +--- + +## Implementation Guidance + +### Step 1: Update `CrateDescriptor` interface + +In `packages/rust/src/create-crate.ts`: + +```typescript +export interface CrateDescriptor { + name: string; + version?: string; + builtin?: boolean; // NEW: if true, crate is implicit (e.g., std) — no Cargo.toml entry + modules: TModules; +} +``` + +### Step 2: Store builtin flag on factory state + +In `CrateFactoryState`: + +```typescript +interface CrateFactoryState { + name: string; + version?: string; + builtin: boolean; // NEW + scopes: WeakMap; +} +``` + +Set from descriptor: `builtin: descriptor.builtin ?? false`. + +### Step 3: Expose helper + +```typescript +export function isBuiltinCrate(crate: ExternalCrate): boolean { + return getFactoryState(crate).builtin; +} +``` + +### Step 4: Modify `ref()` in `reference.ts` + +At approximately line 101, wrap the `addDependency` call: + +```typescript +// Before: +sourceCrate.addDependency(targetCrate.name, targetCrate.version ?? "*"); + +// After: +import { isBuiltinCrate, type ExternalCrate } from "../create-crate.js"; +// ... in the external crate branch: +if (!(isBuiltinCrate(/* the external crate object */))) { + sourceCrate.addDependency(targetCrate.name, targetCrate.version ?? "*"); +} +``` + +**Key challenge:** The `ref()` function resolves symbols via the binder and gets a `ResolutionResult`. It needs access to the `ExternalCrate` object to check if it's builtin. The crate scope is available via `targetCrate` (a `RustCrateScope`). The factory state stores `scopes: WeakMap` — but we need the reverse lookup: from `RustCrateScope` to `ExternalCrate`. + +**Approach options:** + +1. Store `builtin` on `RustCrateScope` itself (simplest — add `builtin: boolean` constructor param) +2. Maintain a reverse map from scope to factory state + +Option 1 is recommended — it's simpler and `RustCrateScope` already accepts constructor args. + +### Step 5: Alternative — Store builtin on RustCrateScope + +In `rust-crate-scope.ts`, add a `builtin` property. In `create-crate.ts`, pass `builtin` when creating the scope via `createScope(RustCrateScope, name, version, { binder, metadata: { external: true, builtin: true } })`. + +Then in `reference.ts`: `if (!targetCrate.metadata?.builtin) { sourceCrate.addDependency(...); }`. + +--- + +## Acceptance Criteria + +- [x] `CrateDescriptor` interface has optional `builtin` field +- [x] `createCrate({ name: "std", builtin: true, modules: { ... } })` creates a crate +- [x] Referencing a symbol from a builtin crate generates `use std::collections::HashMap;` +- [x] Builtin crate does NOT appear in `RustCrateScope.dependencies` +- [x] Existing non-builtin crate behavior unchanged (serde tests still pass) +- [x] `isBuiltinCrate()` or equivalent check is exposed + +--- + +## Definition of Done + +```bash +pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test +``` + +All existing tests pass. New builtin crate test passes. + +## Completion Notes + +- Added `builtin?: boolean` to `CrateDescriptor` and persisted it in `CrateFactoryState`. +- Added exported `isBuiltinCrate(crate)` utility in `create-crate.ts`. +- Added `builtin` support to `RustCrateScope` options/property and propagated it during scope creation. +- Updated `ref()` to keep generating `use` statements while skipping dependency tracking for builtin crates. +- Added/ran coverage in `packages/rust/test/create-crate.test.tsx` for builtin-crate behavior. diff --git a/docs/backlog/tasks/T037-complete-stc-wrappers.md b/docs/backlog/tasks/T037-complete-stc-wrappers.md new file mode 100644 index 000000000..8eded4e8a --- /dev/null +++ b/docs/backlog/tasks/T037-complete-stc-wrappers.md @@ -0,0 +1,94 @@ +# T037 — Complete STC Wrappers + +| Field | Value | +| ------------------------- | --------------------------------------------------- | +| **ID** | T037 | +| **Epic** | [E006](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T032 (initial STC wrappers) | +| **Blocks** | — | + +--- + +## Description + +T032 implemented STC wrappers for 12 declaration components. Go wraps all 28 of its components (see `packages/go/src/components/stc/index.ts`). This task adds STC wrappers for the remaining user-facing Rust components to reach parity. + +--- + +## Goal + +All user-facing Rust components have STC wrappers, matching Go's coverage pattern. + +--- + +## Scope Included + +Add STC wrappers for: + +1. `ModuleDocComment` — parallels `DocComment` (already wrapped) +2. `SourceFile` — Go wraps its `SourceFile` +3. `ModuleDirectory` — Go wraps its `ModuleDirectory` +4. `CrateDirectory` — Go wraps its `SourceDirectory` (equivalent) +5. `CargoTomlFile` — structural but user-constructed +6. `Reference` — Go wraps its `Reference` +7. `Parameters` — useful for programmatic construction +8. `TypeParameters` — useful for programmatic construction +9. `Value` — useful for programmatic construction + +--- + +## Out of Scope + +Internal/auto-rendered components that users don't construct directly: + +- `UseStatement` / `UseStatements` (auto-rendered by SourceFile) +- `ModDeclarations` (auto-rendered by SourceFile) +- `Declaration` (internal wrapper, not user-facing) + +--- + +## Context Files + +| File | Relevance | +| ------------------------------------------- | ------------------------------- | +| `packages/rust/src/components/stc/index.ts` | Add new wrappers here | +| `packages/go/src/components/stc/index.ts` | Reference pattern (28 wrappers) | +| `packages/rust/src/components/index.ts` | Component exports to wrap | +| `packages/rust/test/stc.test.tsx` | Update tests | + +--- + +## Implementation Guidance + +Follow the existing pattern in `stc/index.ts`: + +```typescript +import { stc } from "@alloy-js/core"; +import { SourceFile as SourceFileComponent } from "../source-file.js"; +// ... +export const SourceFile = stc(SourceFileComponent); +``` + +Update `stc.test.tsx` to verify at least 2-3 of the new wrappers produce identical output to their JSX equivalents. + +--- + +## Acceptance Criteria + +- [x] All user-facing components listed in scope have STC wrappers +- [x] `stc.test.tsx` updated with tests for new wrappers +- [x] Build passes: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` + +--- + +## Definition of Done + +```bash +pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test +``` diff --git a/docs/backlog/tasks/T038-crate-type-prop.md b/docs/backlog/tasks/T038-crate-type-prop.md new file mode 100644 index 000000000..86d382c0e --- /dev/null +++ b/docs/backlog/tasks/T038-crate-type-prop.md @@ -0,0 +1,116 @@ +# T038 — CrateDirectory crateType Prop + +| Field | Value | +| ------------------------- | --------------------------------------------------- | +| **ID** | T038 | +| **Epic** | [E006](../epics/E006-external-deps-build-polish.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P3 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (CrateDirectory / SourceFile) | +| **Blocks** | — | + +--- + +## Description + +Add a `crateType` prop to `CrateDirectory` to distinguish library crates from binary crates. Currently users can already use `` to create binary crates (the SourceFile `isModuleRootPath()` function recognizes `main.rs`), but there's no explicit semantic marker. + +--- + +## Goal + +`CrateDirectory` accepts `crateType?: "lib" | "bin"` and propagates it via context for downstream components. + +--- + +## Scope Included + +1. Add `crateType?: "lib" | "bin"` to `CrateDirectoryProps` (default `"lib"`) +2. Add `crateType` to `CrateContextValue` +3. Pass through in `CrateDirectory` component +4. Add test for binary crate context + +--- + +## Out of Scope + +- Cargo.toml `[lib]` / `[[bin]]` section rendering (explicitly deferred in T030) +- Auto-generating `main.rs` vs `lib.rs` (users create SourceFile explicitly) +- Validation that `crateType="bin"` crates have `main.rs` + +--- + +## Context Files + +| File | Relevance | +| --------------------------------------------------------- | ------------------------------ | +| `packages/rust/src/components/crate-directory.tsx` | Add prop | +| `packages/rust/src/context/crate-context.tsx` | Add to context value | +| `packages/rust/src/components/source-file.tsx` | Already handles main.rs/lib.rs | +| `packages/rust/test/source-file-crate-directory.test.tsx` | Add test | + +--- + +## Implementation Guidance + +### crate-directory.tsx + +```typescript +export interface CrateDirectoryProps { + name: string; + version?: string; + edition?: string; + crateType?: "lib" | "bin"; // NEW + dependencies?: Record; + includeCargoToml?: boolean; + children?: Children; +} +``` + +### crate-context.tsx + +```typescript +export interface CrateContextValue { + scope: RustCrateScope; + name: string; + version?: string; + edition: string; + crateType: "lib" | "bin"; // NEW +} +``` + +Default to `"lib"` in `CrateDirectory`: `crateType: props.crateType ?? "lib"`. + +--- + +## Acceptance Criteria + +- [x] `CrateDirectoryProps` has `crateType` field +- [x] `CrateContextValue` has `crateType` field +- [x] `useCrateContext()` returns `crateType` +- [x] Default is `"lib"` when not specified +- [x] Test verifies context value for both lib and bin +- [x] Build passes: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` + +--- + +## Definition of Done + +```bash +pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test +``` + +## Implementation Notes + +- Added `crateType?: "lib" | "bin"` to `CrateDirectoryProps` with default `"lib"` in `CrateDirectory`. +- Extended `CrateContextValue` with `crateType` and propagated through crate context so consumers receive the semantic crate kind. +- Added/updated test coverage in `source-file-crate-directory` tests to verify crate context behavior for both default lib and explicit bin crates. + +## Validation + +- Command: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` +- Result: Passed. diff --git a/docs/backlog/tasks/T039-reference-scope-traversal.md b/docs/backlog/tasks/T039-reference-scope-traversal.md new file mode 100644 index 000000000..846579889 --- /dev/null +++ b/docs/backlog/tasks/T039-reference-scope-traversal.md @@ -0,0 +1,65 @@ +# T039 — Reference Component Scope Traversal + +| Field | Value | +| ------------------------- | ----------------------------------------------------------- | +| **ID** | T039 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P0 — critical | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T010 (Declaration & Reference), T022 (Reference Resolution) | +| **Blocks** | — | + +--- + +## Description + +The `Reference` component (and the underlying `ref()` function in `symbols/reference.ts`) unconditionally calls `useRustModuleScope()` at line 64, which throws `"Expected a Rust module scope"` when used inside any non-module scope — including `RustFunctionScope`, `RustImplScope`, `RustTraitScope`, and struct/enum member scopes. + +This makes `Reference` unusable in: + +- Struct field types (`} />`) +- Function parameter types and return types +- Impl block and trait block contents + +This defeats the purpose of the reference/import system, forcing users to use plain strings for all type references in nested positions. + +--- + +## Goal + +`Reference` works in any scope position by traversing up the scope hierarchy to find the enclosing `RustModuleScope`, rather than requiring the immediate scope to be a module scope. + +--- + +## Scope Included + +- Modify `ref()` in `packages/rust/src/symbols/reference.ts` to use scope traversal (e.g., `useRustScope()` + walk up via `enclosingModule` or parent chain) instead of `useRustModuleScope()` +- Ensure `use` statements are still added to the correct enclosing module scope +- Add tests for Reference inside struct fields, function parameters, return types, impl blocks, and trait blocks + +--- + +## Scope Excluded + +- Changes to the core scope hierarchy +- New scope types + +--- + +## Acceptance Criteria + +- [x] `` resolves correctly inside `` +- [x] `` resolves correctly inside function `parameters` and `returnType` +- [x] `` resolves correctly inside `` and `` +- [x] Auto-generated `use` statements appear in the correct enclosing module's source file +- [x] Existing Reference tests continue to pass + +--- + +## Evidence + +Discovered while building `samples/rust-example/`. The `ref()` function at `packages/rust/src/symbols/reference.ts:64` calls `useRustModuleScope()` which is implemented in `packages/rust/src/scopes/contexts.ts` and throws if the current scope is not `RustModuleScope` or `RustCrateScope`. diff --git a/docs/backlog/tasks/T040-missing-newlines-between-items.md b/docs/backlog/tasks/T040-missing-newlines-between-items.md new file mode 100644 index 000000000..ea264fe46 --- /dev/null +++ b/docs/backlog/tasks/T040-missing-newlines-between-items.md @@ -0,0 +1,89 @@ +# T040 — Missing Newlines Between Sibling Items + +| Field | Value | +| ------------------------- | ------------------------------------------------ | +| **ID** | T040 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P0 — critical | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T011 (StructDeclaration), T012 (EnumDeclaration) | +| **Blocks** | — | + +--- + +## Description + +Enum variants, struct fields, and other sibling items within container components are rendered on the same line with no line breaks between them. Doc comments concatenate directly onto the previous item's closing text. + +Examples from generated output: + +``` +NotFound,/// The store has reached its maximum capacity. +StorageFull, +``` + +``` +pub value: V,pub created_at: std::time::Instant, +``` + +Additionally, doc comments and attributes/derives run together: + +``` +/// Error types for the key-value store.#[derive(Debug, Clone)] +pub enum StoreError { +``` + +--- + +## Goal + +Proper line breaks between all sibling items within enum declarations, struct declarations, and between doc comments and their associated declarations. + +--- + +## Scope Included + +- `packages/rust/src/components/enum-declaration.tsx` — add `` between enum variants and between doc comment and `#[derive(...)]`/declaration +- `packages/rust/src/components/struct-declaration.tsx` — add `` between struct fields +- Ensure doc comments, attributes, derives, and declarations each start on their own line + +--- + +## Scope Excluded + +- Changes to core rendering engine +- Other component types (these are the primary affected ones) + +--- + +## Acceptance Criteria + +- [x] Each enum variant starts on its own line +- [x] Each struct field starts on its own line +- [x] Doc comments are separated from the following `#[derive(...)]` or declaration by a newline +- [x] `#[derive(...)]` is on its own line above the declaration +- [x] Custom `#[attribute]` is on its own line +- [x] Existing tests updated to reflect correct formatting + +--- + +## Evidence + +Discovered in `samples/rust-example/` generated output. The `EnumDeclaration` and `StructDeclaration` components in `packages/rust/src/components/` do not add `` joiners between their children. + +--- + +## Implementation Summary + +- Added explicit `` sibling separators in enum and struct declaration rendering so each variant/field emits on its own line. +- Fixed spacing boundaries between doc comments, derives/attributes, and declarations so annotation and declaration blocks no longer concatenate. +- Updated formatting expectations in related tests to lock in newline behavior. + +## Validation + +- `pnpm --filter @alloy-js/rust build` ✅ +- `pnpm --filter @alloy-js/rust test` ✅ diff --git a/docs/backlog/tasks/T041-trait-abstract-methods.md b/docs/backlog/tasks/T041-trait-abstract-methods.md new file mode 100644 index 000000000..660c28d0a --- /dev/null +++ b/docs/backlog/tasks/T041-trait-abstract-methods.md @@ -0,0 +1,67 @@ +# T041 — Trait Methods Should Render as Abstract Signatures + +| Field | Value | +| ------------------------- | --------------------------------------------------- | +| **ID** | T041 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T013 (FunctionDeclaration), T019 (TraitDeclaration) | +| **Blocks** | — | + +--- + +## Description + +When `` is used with no children (self-closing) inside a ``, it renders an empty body `{}` instead of a semicolon `;`: + +```rust +// Current output: +fn to_bytes(&self) -> Result> {} + +// Expected output: +fn to_bytes(&self) -> Result>; +``` + +In Rust, trait method signatures without default implementations must end with `;`, not `{}`. An empty body `{}` is syntactically valid but semantically different — it provides a default implementation that does nothing and returns `()`. + +--- + +## Goal + +`FunctionDeclaration` with no children inside a `TraitDeclaration` renders as `fn name(...) -> Type;` (semicolon-terminated abstract signature). + +--- + +## Scope Included + +- Modify `packages/rust/src/components/function-declaration.tsx` (around line 109) to detect when: + 1. `children` is undefined/null + 2. The parent scope is `RustTraitScope` +- In that case, render `;` instead of `" {}"` + +--- + +## Scope Excluded + +- Impl block methods (empty body `{}` is valid there) +- Functions with explicit empty children (empty string/fragment) + +--- + +## Acceptance Criteria + +- [x] Self-closing `` inside `` renders `fn name();` +- [x] `body` inside `` renders `fn name() { body }` (default impl) +- [x] Functions outside traits with no children continue to render `fn name() {}` +- [x] Existing trait tests updated + +--- + +## Evidence + +Discovered in `samples/rust-example/` generated `traits/mod.rs`. The `FunctionDeclaration` component at `packages/rust/src/components/function-declaration.tsx:109` always renders `" {}"` for empty children regardless of scope context. diff --git a/docs/backlog/tasks/T042-enum-tuple-variants.md b/docs/backlog/tasks/T042-enum-tuple-variants.md new file mode 100644 index 000000000..df8429ae1 --- /dev/null +++ b/docs/backlog/tasks/T042-enum-tuple-variants.md @@ -0,0 +1,80 @@ +# T042 — Enum Tuple Variant Support + +| Field | Value | +| ------------------------- | ---------------------------------- | +| **ID** | T042 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T012 (EnumDeclaration) | +| **Blocks** | — | + +--- + +## Description + +`EnumVariant` with children renders struct-variant syntax `{ Type }` instead of tuple-variant syntax `(Type)`: + +```rust +// Current output: +SerializationError { + String +} + +// Expected output: +SerializationError(String) +``` + +The `EnumVariantProps` interface has both `fields?: Children[]` and `children?: Children`, but the API doesn't clearly distinguish between tuple variants (parenthesized) and struct variants (braced). The `children` prop always uses brace syntax. + +--- + +## Goal + +`EnumVariant` correctly renders both tuple variants `Name(Type1, Type2)` and struct variants `Name { field: Type }`. + +--- + +## Scope Included + +- Modify `packages/rust/src/components/enum-declaration.tsx` EnumVariant component to: + - Render tuple syntax `(...)` when `fields` prop is provided (or add a `tuple` boolean prop) + - Render struct syntax `{ ... }` when `children` prop is provided + - Or: add a clear API distinction (e.g., `kind: "tuple" | "struct"` prop) +- Update tests for both variant kinds +- Update documentation/examples + +--- + +## Scope Excluded + +- Unit variants (already work correctly) + +--- + +## Acceptance Criteria + +- [x] Tuple variants render as `VariantName(Type1, Type2)` +- [x] Struct variants render as `VariantName { field_name: Type }` +- [x] Unit variants continue to render as `VariantName` +- [x] Clear API to distinguish tuple vs struct variants + +--- + +## Completion Notes + +- Added `kind?: "unit" | "tuple" | "struct"` to `EnumVariantProps` so callers can explicitly choose tuple vs struct rendering. +- Kept backward-compatible inference: + - `fields` still renders tuple variants. + - `children` still renders struct variants when `kind` is omitted. +- Added test coverage for tuple rendering from `children` when `kind="tuple"` and updated mixed-variant tests to use explicit `kind` values for API clarity. + +--- + +## Evidence + +Discovered in `samples/rust-example/` generated `error/mod.rs`. The `EnumVariant` component at `packages/rust/src/components/enum-declaration.tsx:110-116` always renders brace syntax for children. diff --git a/docs/backlog/tasks/T043-standalone-sourcefile-module-registration.md b/docs/backlog/tasks/T043-standalone-sourcefile-module-registration.md new file mode 100644 index 000000000..fdf0b9e13 --- /dev/null +++ b/docs/backlog/tasks/T043-standalone-sourcefile-module-registration.md @@ -0,0 +1,77 @@ +# T043 — Standalone SourceFile Module Registration + +| Field | Value | +| ------------------------- | ---------------------------------------------------------- | +| **ID** | T043 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T009 (SourceFile / CrateDirectory), T025 (ModDeclarations) | +| **Blocks** | — | + +--- + +## Description + +A `` placed directly inside `` does not auto-register as a child module. In Rust, a file `config.rs` next to `lib.rs` should generate `mod config;` in `lib.rs`. Currently, only `` calls `addChildModule()` on the parent scope. + +This means standalone source files (not inside a ModuleDirectory) are invisible to the module system and their `mod` declarations are missing from the crate root. + +--- + +## Goal + +Non-root `SourceFile` components (paths that aren't `lib.rs`, `main.rs`, or `mod.rs`) automatically register as child modules in their parent scope. + +--- + +## Scope Included + +- Modify `packages/rust/src/components/source-file.tsx` to call `scopeParent.addChildModule()` for non-root files +- Extract module name from filename (e.g., `config.rs` → `config`) +- Optionally support a `pub` prop on SourceFile for visibility control +- Add tests for standalone file module registration + +--- + +## Scope Excluded + +- Changes to ModuleDirectory behavior +- Nested path handling (e.g., `foo/bar.rs` — use ModuleDirectory for nested paths) + +--- + +## Acceptance Criteria + +- [x] `` inside `` generates `mod config;` in lib.rs +- [x] `` generates `pub mod config;` +- [x] Root files (`lib.rs`, `main.rs`, `mod.rs`) are NOT registered as child modules +- [x] Module registration works alongside ModuleDirectory children +- [x] Existing tests continue to pass + +--- + +## Completion Notes + +- Updated `SourceFile` module registration so standalone non-root files register themselves with their parent module declarations, while `lib.rs`/`main.rs`/`mod.rs` are explicitly excluded from self-registration. +- Added/updated coverage for standalone source-file registration behavior, including root-file non-registration cases. + +## Validation + +- `pnpm --filter @alloy-js/rust exec vitest run test/source-file-crate-directory.test.tsx` ✅ +- `pnpm --filter @alloy-js/rust build` ✅ +- `pnpm --filter @alloy-js/rust test` ✅ + +## Learning Note + +- When rendering Rust module trees, treat non-root `SourceFile` nodes as declarative children that must call parent module registration; never self-register module-root files (`lib.rs`, `main.rs`, `mod.rs`) or roots will duplicate module declarations. + +--- + +## Evidence + +Discovered in `samples/rust-example/` where `config.rs` is a standalone SourceFile. The generated `lib.rs` has `pub mod error; pub mod store; pub mod traits;` (from ModuleDirectories) but no `mod config;`. diff --git a/docs/backlog/tasks/T044-function-default-receiver.md b/docs/backlog/tasks/T044-function-default-receiver.md new file mode 100644 index 000000000..ca59eefb8 --- /dev/null +++ b/docs/backlog/tasks/T044-function-default-receiver.md @@ -0,0 +1,70 @@ +# T044 — FunctionDeclaration Default Receiver in Impl Blocks + +| Field | Value | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **ID** | T044 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | improvement | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T013 (FunctionDeclaration), T021 (Self Receiver) | +| **Blocks** | — | +| **Resolution** | No code changes required. Behavior is already correctly implemented: `FunctionDeclaration` defaults to `"&self"` for methods in `ImplBlock`/`TraitDeclaration`, with `receiver="none"` opt-out for associated functions. Existing tests validate the pattern. | + +--- + +## Description + +When `FunctionDeclaration` is inside an `ImplBlock` or `TraitDeclaration` and the `receiver` prop is not specified, it defaults to `"&self"` (line 36 in `function-declaration.tsx`): + +```typescript +const effectiveReceiver = isMethod ? (props.receiver ?? "&self") : "none"; +``` + +This makes it impossible to write associated functions (like constructors) without explicitly passing `receiver="none"`: + +```tsx +// Must do this for every constructor: + +``` + +While Rust methods commonly use `&self`, associated functions (constructors, factory methods) are equally common and the implicit `&self` is surprising. + +--- + +## Goal + +Evaluate and improve the default receiver behavior for functions in impl blocks. + +--- + +## Scope Included + +- Review whether defaulting to `"&self"` is the right design choice +- Option A: Change default to `"none"` (explicit receivers always required) +- Option B: Keep current default but document it clearly +- Option C: Use heuristics (e.g., functions named `new` default to `"none"`) +- Update tests and documentation for whichever option is chosen + +--- + +## Scope Excluded + +- Free functions (already default to no receiver) + +--- + +## Acceptance Criteria + +- [x] Design decision documented (default `"&self"` for methods is correct; `receiver="none"` for associated functions) +- [x] All existing tests already validate the pattern (no code changes needed) +- [x] Associated functions like `new()` have clear pattern (`receiver="none"` explicit opt-out) + +--- + +## Evidence + +Discovered in `samples/rust-example/` where `Store::new()` and `Config::new()` required explicit `receiver="none"`. The default at `packages/rust/src/components/function-declaration.tsx:36`. diff --git a/docs/backlog/tasks/T045-mod-declarations-render-order.md b/docs/backlog/tasks/T045-mod-declarations-render-order.md new file mode 100644 index 000000000..71d379683 --- /dev/null +++ b/docs/backlog/tasks/T045-mod-declarations-render-order.md @@ -0,0 +1,71 @@ +# T045 — ModDeclarations Render Order Dependency + +| Field | Value | +| ------------------------- | ---------------------------------------------------------- | +| **ID** | T045 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | improvement | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T025 (ModDeclarations), T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +`ModDeclarations` reads `props.scope.childModules` at render time via `Array.from(props.scope.childModules.values())`. Although `childModules` is declared as `shallowReactive(new Map())`, the rendering does not re-trigger when modules are added after initial render. + +This means `` must be placed AFTER all `` children in the JSX tree, otherwise mod declarations are empty: + +```tsx +// ❌ lib.rs renders with empty mod declarations + + + ... + + +// ✅ Works — lib.rs after modules + + ... + + +``` + +--- + +## Goal + +`ModDeclarations` correctly renders all child modules regardless of JSX ordering. + +--- + +## Scope Included + +- Investigate why `shallowReactive` Map changes don't trigger re-rendering in `ModDeclarations` +- Fix reactivity tracking so `ModDeclarations` re-renders when `childModules` is mutated +- Alternatively, restructure rendering to ensure modules are always registered before `ModDeclarations` renders +- Add a test that places `SourceFile path="lib.rs"` BEFORE `ModuleDirectory` children + +--- + +## Scope Excluded + +- Changes to core reactive rendering engine (unless minimal) + +--- + +## Acceptance Criteria + +- [x] `` renders correct mod declarations regardless of JSX position +- [x] Existing module structure tests continue to pass +- [x] New test validates order-independence + +--- + +## Evidence + +Discovered in `samples/rust-example/` where placing `lib.rs` before modules produced empty mod declarations. The `ModDeclarations` component at `packages/rust/src/components/mod-declarations.tsx:26` snapshots the map. The reactive Map is at `packages/rust/src/scopes/rust-crate-scope.ts:29`. diff --git a/docs/backlog/tasks/T046-struct-expression.md b/docs/backlog/tasks/T046-struct-expression.md new file mode 100644 index 000000000..7c1e4fca6 --- /dev/null +++ b/docs/backlog/tasks/T046-struct-expression.md @@ -0,0 +1,138 @@ +# T046 — StructExpression + FieldInit Components + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T046 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +The most common raw `code` template pattern in the Rust sample (8 of 24 instances) is struct/Self literal expressions with field initialization and optional struct update syntax: + +```rust +Self { + max_capacity: capacity, + ..self +} +``` + +Currently this must be written as raw code: + +```tsx +{ + code` + Self { + max_capacity: capacity, + ..self + } +`; +} +``` + +A dedicated component would make this composable and type-safe. + +--- + +## Goal + +Provide `StructExpression` and `FieldInit` components for constructing struct literal expressions in Rust. + +--- + +## Proposed API + +```tsx + + {code`capacity`} + +// Renders: Self { max_capacity: capacity, ..self } + +// Without spread: + + {code`value`} + {code`Instant::now()`} + {code`EntryStatus::Active`} + +// Renders: Entry { value, created_at: Instant::now(), status: EntryStatus::Active } + +// Shorthand (field name matches variable): + + + {code`None`} + +// Renders: Self { max_capacity, default_ttl: None } +``` + +### Props + +```typescript +interface StructExpressionProps { + type: Children; // Struct name or Self + spread?: Children; // Optional ..spread source (e.g., "self") + children?: Children; // FieldInit children +} + +interface FieldInitProps { + name: string; // Field name + children?: Children; // Value expression (omit for shorthand) +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/struct-expression.tsx` — StructExpression and FieldInit components +- Field initializers separated by commas with line breaks +- Struct update syntax (`..spread`) rendered after fields +- Shorthand field initialization (name only, no value) +- STC wrappers for both components +- Unit tests + +--- + +## Scope Excluded + +- Tuple struct construction (e.g., `Point(1, 2)`) — separate task if needed +- Symbol integration / Reference resolution inside expressions (depends on T039) + +--- + +## Precedent + +TypeScript package has `ObjectExpression` + `ObjectProperty` (at `packages/typescript/src/components/ObjectExpression.tsx`) which serves the same purpose for JS object literals with property shorthand, spread, and computed keys. + +--- + +## Acceptance Criteria + +- [x] `` renders `Self { ... }` +- [x] `{value}` renders `x: value` +- [x] `` renders shorthand `x` +- [x] `spread` prop renders `..source` after all fields +- [x] Fields separated by commas with proper formatting +- [x] STC wrappers exported + +--- + +## Completion Notes + +- Added `StructExpression` and `FieldInit` components in `packages/rust/src/components/struct-expression.tsx`. +- Added `StructExpression`/`FieldInit` STC exports in `packages/rust/src/components/stc/index.ts`. +- Added unit coverage in `packages/rust/test/struct-expression.test.tsx` and STC parity coverage in `packages/rust/test/stc.test.tsx`. + +--- + +## Evidence + +8 of 24 raw code instances in `samples/rust-example/` are struct/Self literal expressions. Found in `store-module.tsx` (5 instances) and `config-file.tsx` (6 instances including the `new()` constructor and all builder methods). diff --git a/docs/backlog/tasks/T047-match-expression.md b/docs/backlog/tasks/T047-match-expression.md new file mode 100644 index 000000000..3ffb15bb5 --- /dev/null +++ b/docs/backlog/tasks/T047-match-expression.md @@ -0,0 +1,131 @@ +# T047 — MatchExpression + MatchArm Components + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T047 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +`match` is one of Rust's most distinctive and heavily-used constructs. The sample has 2 match expressions written as raw code, but any non-trivial Rust code generator will produce many more. Currently: + +```tsx +{ + code` + match self { + Self::NotFound => write!(f, "key not found"), + Self::StorageFull => write!(f, "storage is full"), + Self::SerializationError(msg) => write!(f, "serialization error: {}", msg), + } +`; +} +``` + +--- + +## Goal + +Provide `MatchExpression` and `MatchArm` components for Rust pattern matching. + +--- + +## Proposed API + +```tsx + + + {code`write!(f, "key not found")`} + + + {code`write!(f, "storage is full")`} + + + {code`write!(f, "serialization error: {}", msg)`} + + + +// With guard clause: + + {code`Err(StoreError::NotFound)`} + + +// Multi-line arm body: + + + {code`return Err(StoreError::NotFound);`} + + {code`Ok(&entry.value)`} + + +// Wildcard: + + {code`unreachable!()`} + +``` + +### Props + +```typescript +interface MatchExpressionProps { + expression: Children; // The value being matched + children: Children; // MatchArm children +} + +interface MatchArmProps { + pattern: Children; // The pattern (e.g., "Some(x)", "None", "_") + guard?: Children; // Optional guard clause ("if condition") + children: Children; // Arm body expression or block +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/match-expression.tsx` — MatchExpression and MatchArm +- Single-expression arms render as `pattern => expression,` +- Multi-statement arms render with block `pattern => { ... }` +- Optional `guard` prop renders `if guard` after pattern +- Proper indentation and comma separation +- STC wrappers +- Unit tests + +--- + +## Scope Excluded + +- Pattern destructuring components (patterns remain as raw strings/Children) +- Exhaustiveness checking + +--- + +## Precedent + +TypeScript package has `SwitchStatement` + `CaseClause` (at `packages/typescript/src/components/SwitchStatement.tsx`). The `CaseClause` supports expression matching, default cases, break statements, and optional block wrapping — similar concerns to match arms. + +--- + +## Acceptance Criteria + +- [x] `` renders `match expr { ... }` +- [x] `expr` renders `X => expr,` +- [x] Multi-statement arm bodies render with block syntax `X => { ... }` +- [x] `guard` prop renders `if guard` after pattern +- [x] Arms properly separated and indented +- [x] STC wrappers exported + +--- + +## Evidence + +2 match expressions in `samples/rust-example/` — `error-module.tsx` (Display impl with 4 arms) and `store-module.tsx` (get method with nested match). Match is ubiquitous in idiomatic Rust code. diff --git a/docs/backlog/tasks/T048-if-expression.md b/docs/backlog/tasks/T048-if-expression.md new file mode 100644 index 000000000..e4abeb8c4 --- /dev/null +++ b/docs/backlog/tasks/T048-if-expression.md @@ -0,0 +1,117 @@ +# T048 — IfExpression + ElseIfClause + ElseClause Components + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T048 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +Rust `if` is an expression (can return values). The sample has 2 if-block patterns and 1 `if let` pattern written as raw code. Both TypeScript and C# packages already provide IfStatement components. + +--- + +## Goal + +Provide `IfExpression`, `ElseIfClause`, and `ElseClause` components for Rust if-expressions, including `if let` support. + +--- + +## Proposed API + +```tsx +// Simple if: + + {code`return Err(StoreError::StorageFull);`} + + +// if / else if / else: + + {code`Err(StoreError::NotFound)`} + + {code`Err(StoreError::NotFound)`} + + + {code`Ok(&entry.value)`} + + + +// if let (condition is just a string containing `let`): + + + {code`return Err(StoreError::NotFound);`} + + +``` + +### Props + +```typescript +interface IfExpressionProps { + condition: Children; // Condition or "let pattern = expr" + children: Children; // Body + optional ElseIfClause/ElseClause +} + +interface ElseIfClauseProps { + condition: Children; + children: Children; +} + +interface ElseClauseProps { + children: Children; +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/if-expression.tsx` — IfExpression, ElseIfClause, ElseClause +- Renders `if condition { body }` with proper braces and indentation +- Supports `if let` through the condition prop (no special handling needed) +- Chained else-if / else clauses +- STC wrappers +- Unit tests + +--- + +## Scope Excluded + +- `while let` / `loop` / `for` components (separate tasks) +- Ternary-style inline if (Rust doesn't have ternary; if-else is the expression form) + +--- + +## Precedent + +- TypeScript: `IfStatement` + `ElseIfClause` + `ElseClause` at `packages/typescript/src/components/IfStatement.tsx` +- C#: `IfStatement` + `ElseIf` + `Else` at `packages/csharp/src/components/if/` + +Both use the same pattern: condition prop + children body + sibling clause components. + +--- + +## Acceptance Criteria + +- [x] `body` renders `if condition { body }` +- [x] `` renders ` else if condition { body }` +- [x] `` renders ` else { body }` +- [x] `if let` patterns work via condition prop +- [x] Proper indentation of body +- [x] STC wrappers exported + +--- + +## Evidence + +2 if-blocks and 1 if-let in `samples/rust-example/` — `store-module.tsx` insert method (capacity check) and get method (expiration check with nested if-let). diff --git a/docs/backlog/tasks/T049-let-binding.md b/docs/backlog/tasks/T049-let-binding.md new file mode 100644 index 000000000..b47de45dd --- /dev/null +++ b/docs/backlog/tasks/T049-let-binding.md @@ -0,0 +1,104 @@ +# T049 — LetBinding Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T049 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +Variable bindings (`let` / `let mut`) are fundamental to Rust function bodies. The sample embeds them in raw code blocks. A dedicated component enables composability with other expression components. + +--- + +## Goal + +Provide a `LetBinding` component for Rust variable declarations. + +--- + +## Proposed API + +```tsx +// Simple binding: + + {code`self.data.len()`} + +// Renders: let before = self.data.len(); + +// Mutable binding with type: + + ... + +// Renders: let mut entry: Entry = Entry { ... }; + +// Destructuring (pattern as name): + + {code`pair`} + +// Renders: let (key, value) = pair; +``` + +### Props + +```typescript +interface LetBindingProps { + name: string; // Variable name or destructuring pattern + mutable?: boolean; // let mut + type?: Children; // Optional type annotation + children?: Children; // Initializer expression +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/let-binding.tsx` +- `let` / `let mut` with optional type annotation and initializer +- Trailing semicolon +- STC wrapper +- Unit tests + +--- + +## Scope Excluded + +- Symbol registration in scope (future enhancement) +- `const` bindings in function bodies (use `ConstDeclaration`) + +--- + +## Acceptance Criteria + +- [x] `value` renders `let x = value;` +- [x] `mutable` prop renders `let mut x = value;` +- [x] `type` prop renders `let x: Type = value;` +- [x] Without children renders `let x;` (uninitialized) +- [x] STC wrapper exported + +--- + +## Evidence + +Implemented in: + +- `packages/rust/src/components/let-binding.tsx` +- `packages/rust/src/components/index.ts` +- `packages/rust/src/components/stc/index.ts` +- `packages/rust/test/let-binding.test.tsx` + +Validation: + +- `pnpm --filter @alloy-js/rust build` +- `pnpm --filter @alloy-js/rust test` diff --git a/docs/backlog/tasks/T050-function-call-expression.md b/docs/backlog/tasks/T050-function-call-expression.md new file mode 100644 index 000000000..6fa29115e --- /dev/null +++ b/docs/backlog/tasks/T050-function-call-expression.md @@ -0,0 +1,104 @@ +# T050 — FunctionCallExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T050 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +Function and method invocations appear 4 times in the sample as raw code. A component makes them composable, especially when combined with `Reference` (T039) for auto-imported function targets. + +--- + +## Goal + +Provide a `FunctionCallExpression` component for function and method invocations. + +--- + +## Proposed API + +```tsx +// No-arg call: + +// Renders: self.data.len() + +// With arguments: + +// Renders: self.data.insert(key, entry) + +// With complex args: + +// Renders: HashMap::new() + +// Turbofish: +"]} /> +// Renders: collect::>() +``` + +### Props + +```typescript +interface FunctionCallExpressionProps { + target: Children; // Function/method path + args?: Children[]; // Arguments + typeArgs?: Children[]; // Optional turbofish type arguments +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/function-call-expression.tsx` +- Arguments separated by commas with proper wrapping +- Optional turbofish syntax (`::`) +- STC wrapper +- Unit tests + +--- + +## Scope Excluded + +- Method chaining (see MemberExpression/AccessExpression — future task) +- Async `.await` calls + +--- + +## Precedent + +TypeScript: `FunctionCallExpression` at `packages/typescript/src/components/FunctionCallExpression.tsx` — takes `target` and `args` with auto-wrapping for long argument lists. + +--- + +## Acceptance Criteria + +- [x] No-arg calls render `target()` +- [x] Args render `target(arg1, arg2)` +- [x] Turbofish renders `target::(args)` +- [x] Long arg lists wrap with proper indentation +- [x] STC wrapper exported + +--- + +## Evidence + +4 method call instances in `samples/rust-example/store-module.tsx` — `self.data.len()`, `self.data.is_empty()`, `self.data.insert(key, entry)`, `self.data.remove(key)`. + +## Completion Notes + +Previously blocked snapshot formatting issues are resolved. Revalidated successfully with: + +- `pnpm --filter @alloy-js/rust build` +- `pnpm --filter @alloy-js/rust test` diff --git a/docs/backlog/tasks/T051-closure-expression.md b/docs/backlog/tasks/T051-closure-expression.md new file mode 100644 index 000000000..7b74da3ed --- /dev/null +++ b/docs/backlog/tasks/T051-closure-expression.md @@ -0,0 +1,112 @@ +# T051 — ClosureExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T051 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +Rust closures are used extensively with iterators (`.map()`, `.filter()`, `.retain()`, etc.). The sample has 1 closure in raw code, but any iterator-heavy code generator will produce many more. + +--- + +## Goal + +Provide a `ClosureExpression` component for Rust closure expressions. + +--- + +## Proposed API + +```tsx +// Simple closure: + + {code`entry.is_valid()`} + +// Renders: |_, entry| entry.is_valid() + +// Multi-line closure: + + + {code`entry.created_at.elapsed() <= ttl`} + {code`true`} + + +// Renders: |_, entry| { if let Some(ttl) = entry.ttl { ... } else { true } } + +// Move closure with return type: + + {code`x > 0`} + +// Renders: move |x: i32| -> bool { x > 0 } +``` + +### Props + +```typescript +interface ClosureParam { + name: string; + type?: Children; +} + +interface ClosureExpressionProps { + parameters: ClosureParam[]; + move?: boolean; // move keyword for ownership capture + returnType?: Children; // Optional return type annotation + children: Children; // Closure body +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/closure-expression.tsx` +- Parameter list with optional type annotations +- `move` keyword support +- Optional return type +- Single-expression vs block body rendering +- STC wrapper +- Unit tests + +--- + +## Scope Excluded + +- Async closures (`async move || {}`) +- Fn trait bounds (covered by TypeParameters) + +--- + +## Acceptance Criteria + +- [x] `body` renders `|params| body` +- [x] Multi-statement body auto-wraps in braces +- [x] `move` prop renders `move |params| body` +- [x] `returnType` prop renders `|params| -> Type { body }` +- [x] Parameter types render `|x: Type|` +- [x] STC wrapper exported + +--- + +## Evidence + +1 closure in `samples/rust-example/store-module.tsx` — `.retain(|_, entry| { ... })` in evict_expired method. Closures are ubiquitous in idiomatic Rust iterator chains. + +Implemented in: + +- `packages/rust/src/components/closure-expression.tsx` +- `packages/rust/src/components/index.ts` +- `packages/rust/src/components/stc/index.ts` +- `packages/rust/test/closure-expression.test.tsx` diff --git a/docs/backlog/tasks/T052-return-macro.md b/docs/backlog/tasks/T052-return-macro.md new file mode 100644 index 000000000..96e6b269c --- /dev/null +++ b/docs/backlog/tasks/T052-return-macro.md @@ -0,0 +1,100 @@ +# T052 — ReturnExpression + MacroCall Components + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T052 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P3 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | T053 (Update rust-example sample) | + +--- + +## Description + +Two small but useful expression components: early returns and macro invocations. These appear multiple times in the sample and are common in all Rust code. + +--- + +## Goal + +Provide `ReturnExpression` and `MacroCall` components. + +--- + +## Proposed API + +### ReturnExpression + +```tsx +{code`Err(StoreError::NotFound)`} +// Renders: return Err(StoreError::NotFound) + + +// Renders: return +``` + +```typescript +interface ReturnExpressionProps { + children?: Children; // Return value (omit for bare return) +} +``` + +### MacroCall + +```tsx + +// Renders: format!("store::{}", self.data.len()) + + +// Renders: vec![1, 2, 3] + + +// Renders: println!("Hello, {}!", name) +``` + +```typescript +interface MacroCallProps { + name: string; // Macro name (without !) + args?: Children[]; // Arguments + bracket?: "paren" | "bracket" | "brace"; // Delimiter style (default: paren) +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/return-expression.tsx` +- `packages/rust/src/components/macro-call.tsx` +- Macro delimiter styles: `()` (default), `[]` (for `vec!`), `{}` (for `macro_rules!` invocations) +- STC wrappers for both +- Unit tests + +--- + +## Scope Excluded + +- `macro_rules!` definition component (separate task) +- Proc macro support + +--- + +## Acceptance Criteria + +- [x] `value` renders `return value` +- [x] `` renders `return` +- [x] `` renders `format!(...)` +- [x] `bracket="bracket"` renders `vec![...]` +- [x] STC wrappers exported + +--- + +## Evidence + +2 return expressions and 1 format macro in `samples/rust-example/store-module.tsx`. Macros like `format!`, `println!`, `vec!`, `write!`, `todo!`, `unimplemented!` are fundamental to Rust. diff --git a/docs/backlog/tasks/T053-update-rust-example.md b/docs/backlog/tasks/T053-update-rust-example.md new file mode 100644 index 000000000..08208b9d8 --- /dev/null +++ b/docs/backlog/tasks/T053-update-rust-example.md @@ -0,0 +1,80 @@ +# T053 — Update rust-example Sample with Expression Components + +| Field | Value | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **ID** | T053 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | test | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T039 (Reference scope traversal), T040 (Missing newlines), T041 (Trait abstract methods), T042 (Enum tuple variants), T043 (Standalone SourceFile registration), T046 (StructExpression), T047 (MatchExpression), T048 (IfExpression), T049 (LetBinding), T050 (FunctionCallExpression), T051 (ClosureExpression), T052 (ReturnExpression + MacroCall) | +| **Blocks** | — | + +--- + +## Description + +Once all bug fixes (T039–T045) and new expression/statement components (T046–T052) are implemented, refactor `samples/rust-example/` to: + +1. Replace raw `code` template literals with the new expression components +2. Use `Reference` in field types, parameters, and return types (enabled by T039) +3. Verify that newline formatting is correct (T040) +4. Use proper enum tuple variant syntax (T042) +5. Verify trait abstract methods render with `;` (T041) +6. Verify standalone `config.rs` gets a `mod config;` declaration (T043) +7. Verify lib.rs renders correctly regardless of JSX ordering (T045) + +--- + +## Goal + +The `samples/rust-example/` sample demonstrates the full power of `@alloy-js/rust` with minimal raw code, serving as both a test and a showcase. + +--- + +## Scope Included + +- Refactor all 4 component files in `samples/rust-example/src/components/`: + - `error-module.tsx` — Use `MatchExpression` for Display impl, `Reference` in types + - `traits-module.tsx` — Verify abstract method rendering + - `store-module.tsx` — Use `StructExpression`, `IfExpression`, `MatchExpression`, `LetBinding`, `ClosureExpression`, `Reference` in fields/params + - `config-file.tsx` — Use `StructExpression` for all builder methods, `Reference` in field types +- Update `src/externals.ts` if needed +- Update `src/index.tsx` if needed +- Verify build succeeds and generated output is syntactically correct Rust +- Remove workarounds that were needed before bug fixes + +--- + +## Scope Excluded + +- Changes to the Rust package itself (those are in T039–T052) +- New sample features beyond what currently exists + +--- + +## Acceptance Criteria + +- [ ] `pnpm --filter rust-example build` succeeds +- [ ] `node dist/src/index.js` generates all files without errors +- [ ] Generated Rust code has proper formatting (newlines between items) +- [ ] Raw `code` template usage reduced from 24 instances to ≤4 +- [ ] `Reference` used for type annotations in fields, parameters, and return types +- [ ] Generated `lib.rs` includes `mod config;` for standalone config.rs +- [ ] Generated trait methods use `;` not `{}` +- [ ] Generated enum tuple variants use `(Type)` not `{ Type }` + +--- + +## Evidence + +This task integrates all fixes and new components discovered during the initial `samples/rust-example/` creation. See the analysis in the session plan for the full breakdown of 24 raw code instances and which components replace them. + +## Completion Notes + +- Replaced most raw `code`-literal expression usage in `samples/rust-example/src/components/` with the expression/statement components delivered in T046–T052 (including struct, match, if, let-binding, call, closure, return, and macro coverage). +- Reduced remaining raw `code` literals in the sample component layer to **2** targeted cases. +- Validation passed for the Rust package workflow: `pnpm --filter @alloy-js/rust build` and `pnpm --filter @alloy-js/rust test`. diff --git a/docs/backlog/tasks/T054-lifetime-parameters.md b/docs/backlog/tasks/T054-lifetime-parameters.md new file mode 100644 index 000000000..aa97d356d --- /dev/null +++ b/docs/backlog/tasks/T054-lifetime-parameters.md @@ -0,0 +1,114 @@ +# T054 — Lifetime Parameter Support + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T054 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T017 (TypeParameters) | +| **Blocks** | — | + +--- + +## Description + +Lifetimes are a core Rust feature with no current support in the package. The `TypeParameters` component and `TypeParameterProp` interface only handle type parameters (``), not lifetime parameters (`<'a, 'b>`). Lifetime annotations are needed on functions, structs, enums, traits, and impl blocks. + +```rust +fn longest<'a>(a: &'a str, b: &'a str) -> &'a str { + if a.len() > b.len() { a } else { b } +} + +struct Ref<'a, T> { + data: &'a T, +} +``` + +--- + +## Goal + +Extend `TypeParameters` and `TypeParameterProp` to support lifetime parameters alongside type parameters. + +--- + +## Proposed API + +```tsx + + ... + + +// Lifetime + type parameters: + + + + +// Lifetime bounds: + +``` + +### Props Extension + +```typescript +interface TypeParameterProp { + name?: string; // Type parameter name (existing) + lifetime?: string; // Lifetime name (e.g., "'a") — new + constraint?: string; // Bounds (existing, works for both) +} +``` + +--- + +## Scope Included + +- Extend `TypeParameterProp` to accept `lifetime` as alternative to `name` +- Lifetimes render before type parameters (`<'a, 'b, T, U>`) +- Lifetime bounds (`'b: 'a`) +- Lifetime constraints on type parameters (`T: 'a + Clone`) +- Works on functions, structs, enums, traits, and impl blocks +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `{ lifetime: "'a" }` renders `'a` in type parameter list +- [x] Lifetimes appear before type parameters in output +- [x] Lifetime bounds render correctly (`'b: 'a`) +- [x] Type parameters with lifetime bounds render (`T: 'a + Clone`) +- [x] Existing type parameter tests continue to pass + +## Implementation Notes + +- Extended `TypeParameterProp` with optional `lifetime` support while preserving existing `name`-based parameters. +- Updated `TypeParameters` rendering to order lifetimes before type parameters regardless of input order. +- Added direct unit coverage in `packages/rust/test/type-parameters.test.tsx` and integration coverage in `packages/rust/test/lifetime-parameters.test.tsx` for function/struct/enum/trait/impl use-cases. diff --git a/docs/backlog/tasks/T055-for-expression.md b/docs/backlog/tasks/T055-for-expression.md new file mode 100644 index 000000000..0a4e8a7e8 --- /dev/null +++ b/docs/backlog/tasks/T055-for-expression.md @@ -0,0 +1,78 @@ +# T055 — ForExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T055 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +`for` loops are the most common iteration pattern in Rust. No component exists for them. + +```rust +for item in collection { + process(item); +} + +for (i, val) in list.iter().enumerate() { + println!("{}: {}", i, val); +} +``` + +--- + +## Proposed API + +```tsx + + {code`process(item);`} + + + + ... + + +// With label: + + ... + +``` + +### Props + +```typescript +interface ForExpressionProps { + pattern: Children; // Loop variable or destructuring pattern + iterator: Children; // The iterable expression + label?: string; // Optional loop label ('outer) + children: Children; // Loop body +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/for-expression.tsx` +- Renders `for pattern in iterator { body }` +- Optional loop label +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `body` renders `for x in items { body }` +- [x] `label` prop renders `'label: for x in items { body }` +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T056-while-loop-expression.md b/docs/backlog/tasks/T056-while-loop-expression.md new file mode 100644 index 000000000..503b66a8f --- /dev/null +++ b/docs/backlog/tasks/T056-while-loop-expression.md @@ -0,0 +1,96 @@ +# T056 — WhileExpression + LoopExpression Components + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T056 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +`while` and `loop` are fundamental control flow constructs in Rust. `loop` is the only way to create unconditional infinite loops and is commonly used with `break` to return values. + +```rust +while condition { + do_work(); +} + +while let Some(item) = iter.next() { + process(item); +} + +loop { + if done { break result; } +} +``` + +--- + +## Proposed API + +```tsx +// while: + + {code`process(stack.pop());`} + + +// while let: + + {code`process(item);`} + + +// loop: + + {code`if done { break result; }`} + + +// With label: + + ... + +``` + +### Props + +```typescript +interface WhileExpressionProps { + condition: Children; // Condition or "let pattern = expr" + label?: string; // Optional loop label + children: Children; +} + +interface LoopExpressionProps { + label?: string; // Optional loop label + children: Children; +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/while-expression.tsx` — WhileExpression +- `packages/rust/src/components/loop-expression.tsx` — LoopExpression +- Optional loop labels on both +- `while let` via condition prop (same pattern as IfExpression) +- STC wrappers +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `` renders `while condition { body }` +- [x] `while let` works via condition prop +- [x] `` renders `loop { body }` +- [x] Label prop renders `'label: while/loop { body }` +- [x] STC wrappers exported diff --git a/docs/backlog/tasks/T057-break-continue.md b/docs/backlog/tasks/T057-break-continue.md new file mode 100644 index 000000000..895b928ef --- /dev/null +++ b/docs/backlog/tasks/T057-break-continue.md @@ -0,0 +1,75 @@ +# T057 — BreakExpression + ContinueExpression Components + +| Field | Value | +| ------------------------- | ------------------------------------------------------------- | +| **ID** | T057 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T055 (ForExpression), T056 (WhileExpression + LoopExpression) | +| **Blocks** | — | + +--- + +## Description + +`break` and `continue` are loop control expressions. In Rust, `break` can carry a value (to return from `loop`), and both support loop labels for nested loop control. + +```rust +break; +break 'outer; +break value; +break 'outer value; +continue; +continue 'outer; +``` + +--- + +## Proposed API + +```tsx + // break; + // break 'outer; +{code`result`} // break result; +{code`result`} // break 'outer result; + + // continue; + // continue 'outer; +``` + +### Props + +```typescript +interface BreakExpressionProps { + label?: string; // Optional loop label + children?: Children; // Optional break value +} + +interface ContinueExpressionProps { + label?: string; // Optional loop label +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/break-expression.tsx` +- `packages/rust/src/components/continue-expression.tsx` +- STC wrappers +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `` renders `break` +- [x] `value` renders `break 'outer value` +- [x] `` renders `continue` +- [x] `` renders `continue 'outer` +- [x] STC wrappers exported diff --git a/docs/backlog/tasks/T058-tuple-struct.md b/docs/backlog/tasks/T058-tuple-struct.md new file mode 100644 index 000000000..4529c0d61 --- /dev/null +++ b/docs/backlog/tasks/T058-tuple-struct.md @@ -0,0 +1,95 @@ +# T058 — TupleStruct Declaration + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T058 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T011 (StructDeclaration) | +| **Blocks** | — | + +--- + +## Description + +Rust has three struct forms, but only named-field structs are currently supported: + +```rust +// Named-field (supported): +struct User { name: String, age: u32 } + +// Tuple struct (NOT supported): +struct Point(i32, i32); +struct Wrapper(String); +struct Id(u64); + +// Unit struct (partially supported — renders empty braces): +struct Marker; +``` + +Tuple structs are common for newtype patterns, wrapper types, and simple data containers. They need a different syntax from named-field structs (parentheses instead of braces, types instead of named fields). + +--- + +## Proposed API + +Option A — Extend `StructDeclaration` with a `tuple` mode: + +```tsx + +// Renders: pub struct Point(i32, i32); + + +// Renders: #[derive(Debug)] pub struct Wrapper(String); +``` + +Option B — Separate component: + +```tsx + +``` + +### Props (Option A extension) + +```typescript +// Extend existing StructDeclarationProps: +interface StructDeclarationProps { + // ... existing props ... + tuple?: boolean; // Render as tuple struct + types?: Children[]; // Tuple field types (when tuple=true) + unit?: boolean; // Render as unit struct (no body) +} +``` + +--- + +## Scope Included + +- Support tuple struct syntax `struct Name(Type1, Type2);` +- Support unit struct syntax `struct Name;` +- Visibility, derives, attributes, doc comments, and generics on tuple structs +- Symbol registration (creates NamedTypeSymbol like regular structs) +- STC wrapper +- Unit tests + +--- + +## Scope Excluded + +- Tuple struct field visibility (`pub` on individual tuple fields) — add if needed + +--- + +## Acceptance Criteria + +- [x] Tuple structs render as `struct Name(Type1, Type2);` +- [x] Unit structs render as `struct Name;` (no braces) +- [x] Derives, attributes, doc comments work on tuple structs +- [x] Generic tuple structs render `struct Name(T);` +- [x] Existing named-field struct tests unaffected +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T059-static-declaration.md b/docs/backlog/tasks/T059-static-declaration.md new file mode 100644 index 000000000..15b31613a --- /dev/null +++ b/docs/backlog/tasks/T059-static-declaration.md @@ -0,0 +1,72 @@ +# T059 — StaticDeclaration Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T059 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T014 (TypeAlias + ConstDeclaration) | +| **Blocks** | — | + +--- + +## Description + +Rust has `static` items in addition to `const` items. Statics have a fixed memory address and can be mutable (`static mut`), while consts are inlined. The package has `ConstDeclaration` but no `StaticDeclaration`. + +```rust +static COUNTER: AtomicUsize = AtomicUsize::new(0); +static mut BUFFER: Vec = Vec::new(); +``` + +--- + +## Proposed API + +```tsx + + {code`AtomicUsize::new(0)`} + + + + {code`Vec::new()`} + +``` + +### Props + +```typescript +interface StaticDeclarationProps { + name: string; + refkey?: Refkey; + pub?: boolean; + pub_crate?: boolean; + mutable?: boolean; // static mut + type: Children; + children?: Children; // Initializer +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/static-declaration.tsx` +- `static` and `static mut` items +- Visibility, type annotation, initializer +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `value` renders `static X: T = value;` +- [x] `mutable` prop renders `static mut X: T = value;` +- [x] Visibility props work +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T060-await-expression.md b/docs/backlog/tasks/T060-await-expression.md new file mode 100644 index 000000000..4bb3f0ffe --- /dev/null +++ b/docs/backlog/tasks/T060-await-expression.md @@ -0,0 +1,65 @@ +# T060 — AwaitExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T060 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +Rust uses postfix `.await` syntax for async operations. `FunctionDeclaration` supports the `async` prop, but there's no component for `.await` expressions in function bodies. + +```rust +let response = client.get(url).await?; +let data = response.json().await?; +``` + +--- + +## Proposed API + +```tsx +{code`client.get(url)`} +// Renders: client.get(url).await + +// With ? operator: +{code`client.get(url)`} +// Renders: client.get(url).await? +``` + +### Props + +```typescript +interface AwaitExpressionProps { + try?: boolean; // Append ? operator + children: Children; // The future expression +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/await-expression.tsx` +- Postfix `.await` rendering +- Optional `?` operator combination +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `expr` renders `expr.await` +- [x] `try` prop renders `expr.await?` +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T061-method-chain-expression.md b/docs/backlog/tasks/T061-method-chain-expression.md new file mode 100644 index 000000000..5660db46b --- /dev/null +++ b/docs/backlog/tasks/T061-method-chain-expression.md @@ -0,0 +1,112 @@ +# T061 — MethodChainExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T061 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T050 (FunctionCallExpression) | +| **Blocks** | — | + +--- + +## Description + +Method chaining is ubiquitous in idiomatic Rust, especially with iterators and builders: + +```rust +let result: Vec<_> = items + .iter() + .filter(|x| x.is_valid()) + .map(|x| x.value()) + .collect(); + +self.data + .remove(key) + .map(|entry| entry.value) + .ok_or(StoreError::NotFound) +``` + +This needs a component that composes well with `FunctionCallExpression` and `ClosureExpression`. + +--- + +## Proposed API + +```tsx + + + + {code`x.is_valid()`} + , + ]} + /> + + {code`x.value()`} + , + ]} + /> + "]} /> + +``` + +### Props + +```typescript +interface MethodChainExpressionProps { + receiver: Children; // Starting expression + children: Children; // Chain calls +} + +interface MethodChainCallProps { + name: string; // Method name + args?: Children[]; // Arguments + typeArgs?: Children[]; // Turbofish type arguments + await?: boolean; // .await before this call + try?: boolean; // ? after this call +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/method-chain-expression.tsx` +- Chained `.method()` calls with proper line-wrapping +- Turbofish syntax on individual calls +- `.await` and `?` on individual chain steps +- STC wrapper +- Unit tests + +--- + +## Precedent + +C# package has `AccessExpression` with `AccessExpression.Part` sub-components for chained member access, element access, and invocations. TypeScript has `MemberExpression` with a parts-based API. + +--- + +## Acceptance Criteria + +- [x] Chain renders with `.method()` calls +- [x] Long chains wrap with proper indentation +- [x] Turbofish works on individual calls +- [x] `.await` and `?` work on individual steps +- [x] STC wrapper exported + +--- + +## Completion Note + +Completed on 2026-03-12 during record-phase documentation updates. diff --git a/docs/backlog/tasks/T062-pub-super-visibility.md b/docs/backlog/tasks/T062-pub-super-visibility.md new file mode 100644 index 000000000..eeab2dd85 --- /dev/null +++ b/docs/backlog/tasks/T062-pub-super-visibility.md @@ -0,0 +1,59 @@ +# T062 — pub(super) Visibility + Visibility Prop Refactor + +| Field | Value | +| ------------------------- | ---------------------------------------------------------------------------- | +| **ID** | T062 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T011 (StructDeclaration), T012 (EnumDeclaration), T013 (FunctionDeclaration) | +| **Blocks** | — | + +--- + +## Description + +Currently all declaration components support `pub` and `pub(crate)` as separate boolean props, but `pub(super)` is missing. The `RustVisibility` type includes `"pub(super)"` but no component prop exposes it. + +Consider refactoring from separate boolean props to a single `visibility` prop: + +```typescript +// Current (boolean props): + + + +// Proposed (single prop): + + + +``` + +--- + +## Scope Included + +- Add `pub(super)` support to all declaration components +- Either add `pub_super` boolean prop (minimal change) or refactor to unified `visibility` prop (breaking change) +- Update Field, FunctionDeclaration, StructDeclaration, EnumDeclaration, ConstDeclaration, TypeAlias, etc. +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `pub(super)` visibility works on all declarations +- [x] Existing `pub` and `pub(crate)` behavior preserved +- [x] RustVisibility type fully utilized + +--- + +## Completion Notes + +- Added `pub_super?: boolean` support for visibility-bearing declaration/module/field components in `packages/rust/src/components`. +- Added shared visibility mapping helper with precedence: `pub > pub(crate) > pub(super) > private`. +- Updated symbol visibility assignments to use shared Rust visibility mapping. +- Expanded tests across declaration/function/struct/enum/trait/type alias/const/static/module-sourcefile paths to cover `pub(super)` and precedence while preserving existing behavior. diff --git a/docs/backlog/tasks/T063-associated-type.md b/docs/backlog/tasks/T063-associated-type.md new file mode 100644 index 000000000..76a3c9983 --- /dev/null +++ b/docs/backlog/tasks/T063-associated-type.md @@ -0,0 +1,90 @@ +# T063 — AssociatedType in Traits + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T063 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T019 (TraitDeclaration) | +| **Blocks** | — | + +--- + +## Description + +Rust traits can declare associated types, which are commonly used in the standard library (`Iterator::Item`, `Deref::Target`, etc.): + +```rust +trait Iterator { + type Item; + fn next(&mut self) -> Option; +} + +impl Iterator for Counter { + type Item = u32; + fn next(&mut self) -> Option { ... } +} +``` + +No component exists for `type Item;` inside a trait or `type Item = u32;` inside an impl block. + +--- + +## Proposed API + +```tsx +// In trait: + + + + + +// In impl: + + u32 + ... + + +// With bounds: + +``` + +### Props + +```typescript +interface AssociatedTypeProps { + name: string; + constraint?: Children; // Bounds (in trait declaration) + children?: Children; // Concrete type (in impl block) +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/associated-type.tsx` +- Abstract form `type Name;` (in traits) +- Abstract with bounds `type Name: Bound;` (in traits) +- Concrete form `type Name = Type;` (in impl blocks) +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `` in trait renders `type Item;` +- [x] `` renders `type Item: Clone;` +- [x] `u32` in impl renders `type Item = u32;` +- [x] STC wrapper exported + +## Completion Note + +- Completed on 2026-03-12. +- Implemented in key files: `packages/rust/src/components/associated-type.tsx`, `packages/rust/src/components/index.ts`, `packages/rust/src/components/stc/index.ts`, and `packages/rust/test/associated-types.test.tsx`. diff --git a/docs/backlog/tasks/T064-try-expression.md b/docs/backlog/tasks/T064-try-expression.md new file mode 100644 index 000000000..692a379e5 --- /dev/null +++ b/docs/backlog/tasks/T064-try-expression.md @@ -0,0 +1,65 @@ +# T064 — QuestionMarkOperator Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T064 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P2 — should-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +The `?` operator is Rust's primary error propagation mechanism, used on `Result` and `Option` values. It's one of the most distinctive and commonly-used Rust syntax features. + +```rust +let text = std::fs::read_to_string("file.txt")?; +let value = map.get("key").ok_or(Error::NotFound)?; +let parsed: i32 = input.parse()?; +``` + +--- + +## Proposed API + +```tsx +{code`std::fs::read_to_string("file.txt")`} +// Renders: std::fs::read_to_string("file.txt")? + +// Composable with FunctionCallExpression: + + + +``` + +### Props + +```typescript +interface TryExpressionProps { + children: Children; // The expression to apply ? to +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/try-expression.tsx` +- Postfix `?` rendering +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `expr` renders `expr?` +- [x] Composes with other expression components +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T065-unsafe-block.md b/docs/backlog/tasks/T065-unsafe-block.md new file mode 100644 index 000000000..1804897dc --- /dev/null +++ b/docs/backlog/tasks/T065-unsafe-block.md @@ -0,0 +1,62 @@ +# T065 — UnsafeBlock Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T065 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P3 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +`unsafe fn` is supported via `FunctionDeclaration`'s `unsafe` prop, but `unsafe` blocks (inline unsafe code regions) have no component. + +```rust +let ptr = &value as *const i32; +let val = unsafe { *ptr }; + +unsafe { + libc::free(ptr); +} +``` + +--- + +## Proposed API + +```tsx +{code`*ptr`} +// Renders: unsafe { *ptr } +``` + +### Props + +```typescript +interface UnsafeBlockProps { + children: Children; +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/unsafe-block.tsx` +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `body` renders `unsafe { body }` +- [x] Multi-line bodies properly indented +- [x] STC wrapper exported diff --git a/docs/backlog/tasks/T066-inner-attribute.md b/docs/backlog/tasks/T066-inner-attribute.md new file mode 100644 index 000000000..e00bd85ca --- /dev/null +++ b/docs/backlog/tasks/T066-inner-attribute.md @@ -0,0 +1,69 @@ +# T066 — InnerAttribute Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T066 | +| **Epic** | [E009](../epics/E009-language-feature-gaps.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P3 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T015 (Attribute) | +| **Blocks** | — | + +--- + +## Description + +Rust has inner attributes (`#![...]`) that apply to the enclosing item (module, crate, function body). The existing `Attribute` component only renders outer attributes (`#[...]`). + +```rust +#![allow(dead_code)] +#![feature(async_closure)] +#![cfg_attr(test, deny(warnings))] +``` + +--- + +## Proposed API + +```tsx + +// Renders: #![allow(dead_code)] + + +// Renders: #![feature(async_closure)] +``` + +### Props + +```typescript +interface InnerAttributeProps { + name: string; + args?: Children; +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/inner-attribute.tsx` +- Or extend existing `Attribute` component with `inner` boolean prop +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] Renders `#![name(args)]` +- [x] Works without args: `#![name]` +- [x] STC wrapper exported + +## Completion Note + +- Completed on 2026-03-12. +- Implemented `InnerAttribute` component, STC export, tests, and validation passed. diff --git a/docs/backlog/tasks/T067-block-expression.md b/docs/backlog/tasks/T067-block-expression.md new file mode 100644 index 000000000..de193da49 --- /dev/null +++ b/docs/backlog/tasks/T067-block-expression.md @@ -0,0 +1,74 @@ +# T067 — BlockExpression Component + +| Field | Value | +| ------------------------- | ---------------------------------------------- | +| **ID** | T067 | +| **Epic** | [E008](../epics/E008-expression-components.md) | +| **Type** | feature | +| **Status** | done | +| **Priority** | P3 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T009 (SourceFile / CrateDirectory) | +| **Blocks** | — | + +--- + +## Description + +In Rust, blocks are expressions that return the value of their last expression. This is used for complex initializations, scoped bindings, and anywhere an expression needs intermediate computation. + +```rust +let x = { + let a = compute(); + let b = transform(a); + a + b +}; +``` + +--- + +## Proposed API + +```tsx + + + {code`compute()`} + {code`transform(a)`} + {code`a + b`} + + +``` + +### Props + +```typescript +interface BlockExpressionProps { + children: Children; +} +``` + +--- + +## Scope Included + +- `packages/rust/src/components/block-expression.tsx` +- Renders `{ body }` with proper indentation +- Last expression returned (no semicolon on final line) +- STC wrapper +- Unit tests + +--- + +## Acceptance Criteria + +- [x] `body` renders `{ body }` +- [x] Properly indented +- [x] Composable with LetBinding and other components +- [x] STC wrapper exported + +## Completion Note + +- Completed on 2026-03-12. +- Implemented `BlockExpression`, STC export, and unit tests with block-expression return semantics. diff --git a/docs/backlog/tasks/T068-rust-example-externals-ts2742.md b/docs/backlog/tasks/T068-rust-example-externals-ts2742.md new file mode 100644 index 000000000..5a90925b4 --- /dev/null +++ b/docs/backlog/tasks/T068-rust-example-externals-ts2742.md @@ -0,0 +1,54 @@ +# T068 — Fix rust-example externals TS2742 Build Failure + +| Field | Value | +| ------------------------- | ---------------------------------- | +| **ID** | T068 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T053 (Update rust-example sample) | +| **Blocks** | — | + +--- + +## Description + +`pnpm --filter rust-example build` currently fails with a pre-existing TypeScript portability error (`TS2742`) rooted in `samples/rust-example/src/externals.ts`. + +This failure is unrelated to Rust package component correctness, but it prevents sample-build validation from passing cleanly. + +--- + +## Scope Included + +- Reproduce and capture the exact `TS2742` failure in `samples/rust-example/src/externals.ts` +- Refactor/export-type annotations in `externals.ts` to avoid non-portable inferred types +- Keep generated/output behavior unchanged +- Re-run `pnpm --filter rust-example build` to confirm the sample build succeeds + +--- + +## Scope Excluded + +- Feature work in `@alloy-js/rust` components +- Non-TS2742 sample refactors outside `externals.ts` unless strictly required for the fix + +--- + +## Acceptance Criteria + +- [x] `pnpm --filter rust-example build` succeeds without `TS2742` +- [x] `samples/rust-example/src/externals.ts` no longer relies on non-portable inferred exported types +- [x] No behavior change in sample generation flow + +--- + +## Evidence + +Observed during T053 RECORD phase on 2026-03-12: `pnpm --filter rust-example build` still fails with pre-existing `TS2742` in `samples/rust-example/src/externals.ts`. + +Resolved on 2026-03-12 by replacing inferred exported crate values in `samples/rust-example/src/externals.ts` with explicit descriptor constants (`as const`) and explicit exported crate type aliases (`CrateRef & SymbolCreator & ExternalCrate`). diff --git a/docs/backlog/tasks/T069-implblock-generic-forwarding.md b/docs/backlog/tasks/T069-implblock-generic-forwarding.md new file mode 100644 index 000000000..adac317f3 --- /dev/null +++ b/docs/backlog/tasks/T069-implblock-generic-forwarding.md @@ -0,0 +1,61 @@ +# T069 — ImplBlock Generic Parameter Forwarding + +| Field | Value | +| ------------------------- | --------------------------------------- | +| **ID** | T069 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P0 — critical | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T020 (ImplBlock), T017 (TypeParameters) | +| **Blocks** | — | + +--- + +## Description + +When a struct or enum has generic type parameters, the `ImplBlock` component does not forward those generic parameters to the `impl` line. For example, a `Store` struct produces `impl Store` instead of the correct `impl Store`. + +This causes compilation failure because Rust requires generic parameters to be declared on the `impl` block and applied to the target type. + +--- + +## Scope Included + +- Fix `ImplBlock` to forward generic type parameters from the target type +- Render `impl Store` when the target struct/enum has generics +- Handle trait impls: `impl Display for Store` +- Ensure where clauses are also forwarded correctly + +--- + +## Scope Excluded + +- Generic parameters introduced only on the impl block (not from the target type) +- Lifetime parameters on impl blocks (tracked separately if needed) + +--- + +## Acceptance Criteria + +- [x] `impl Store { ... }` renders correctly when `Store` has generic params `` +- [x] `impl Display for Store { ... }` renders correctly for trait impls +- [x] Where clauses from the target type are included on the impl block +- [x] Existing ImplBlock tests continue to pass +- [x] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. The sample `Store` struct's impl block renders without generic parameters, causing `cargo check` to fail. + +Resolved on 2026-03-12 by: + +- Inferring target type parameters from `NamedTypeSymbol.typeParameters` in `ImplBlock` +- Registering named type parameter symbols for `StructDeclaration` and `EnumDeclaration` +- Rendering inferred parameters in both `impl<...>` and target type position +- Adding coverage in `packages/rust/test/impl.test.tsx` for struct, trait impl, enum, and where-clause scenarios diff --git a/docs/backlog/tasks/T070-implblock-trait-use-import.md b/docs/backlog/tasks/T070-implblock-trait-use-import.md new file mode 100644 index 000000000..e659d9140 --- /dev/null +++ b/docs/backlog/tasks/T070-implblock-trait-use-import.md @@ -0,0 +1,64 @@ +# T070 — ImplBlock Trait Name Missing Use Import + +| Field | Value | +| ------------------------- | --------------------------------------------- | +| **ID** | T070 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P0 — critical | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T020 (ImplBlock), T022 (Reference resolution) | +| **Blocks** | — | + +--- + +## Description + +When an `ImplBlock` references a trait (e.g., `impl Display for MyType`), the trait name is rendered in the `impl` line but no corresponding `use` statement is generated. For external traits like `std::fmt::Display`, this means the generated code compiles only if the user manually adds `use std::fmt::Display;`. + +The trait reference in the impl block header should trigger the same import resolution as `Reference` does for type annotations. + +--- + +## Scope Included + +- Ensure that the trait name in `ImplBlock` triggers `use` statement generation via the reference/import system +- Handle both local and external (cross-crate) trait references +- Handle both `impl Trait for Type` and blanket impl patterns + +--- + +## Scope Excluded + +- Auto-importing method signatures from the trait (trait method body generation) +- Supertraits import resolution (separate concern) + +--- + +## Acceptance Criteria + +- [x] `impl Display for MyType` generates `use std::fmt::Display;` when `Display` is from `std::fmt` +- [x] Local trait impls (same crate) generate appropriate `use crate::...` paths +- [x] No duplicate `use` statements when the trait is already imported for other reasons +- [x] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. The `impl Display for StoreError` block renders the trait name but no `use std::fmt::Display;` is generated. + +--- + +## Completion Notes + +- `ImplBlock` trait refkeys now render through `Reference`, so trait names in `impl Trait for Type` participate in normal import resolution. +- Added `packages/rust/test/t070-trait-import.test.tsx` coverage for: + - external trait imports, + - same-crate trait imports, + - duplicate-import prevention, + - blanket impl with generic type parameters. +- Validation: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` (pass). diff --git a/docs/backlog/tasks/T071-prelude-type-shadowing.md b/docs/backlog/tasks/T071-prelude-type-shadowing.md new file mode 100644 index 000000000..9b4e6b2c6 --- /dev/null +++ b/docs/backlog/tasks/T071-prelude-type-shadowing.md @@ -0,0 +1,53 @@ +# T071 — Prelude Type Shadowing Causes Missing Imports + +| Field | Value | +| ------------------------- | ---------------------------------- | +| **ID** | T071 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T022 (Reference resolution) | +| **Blocks** | — | + +--- + +## Description + +When a crate defines a custom type alias that shadows a Rust prelude type (e.g., `type Result = std::result::Result`), the reference resolution system incorrectly skips generating a `use` import for the custom alias. It treats the name as a prelude type and assumes it's already in scope. + +This causes compilation failure when other modules reference `crate::error::Result` — the import is suppressed because `Result` is a prelude name, but the custom definition is needed. + +--- + +## Scope Included + +- Detect when a local type shadows a prelude name +- Generate `use crate::...::ShadowedType;` even when the name matches a prelude type +- Ensure that actual prelude types (not shadowed) still skip import generation + +--- + +## Scope Excluded + +- Renaming or aliasing strategies (e.g., `use crate::error::Result as ErrorResult`) +- Prelude trait method shadowing + +--- + +## Acceptance Criteria + +- [ ] `use crate::error::Result;` is generated when referencing a custom `Result` type alias +- [ ] Standard prelude types (`Option`, `Vec`, `String`, etc.) still skip import when not shadowed +- [ ] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. The `traits-module.tsx` had to use raw `crate::error::Result` paths as a workaround because the import system suppressed the `use` for shadowed `Result`. + +Fixed on 2026-03-12: The `ref()` function in `reference.ts` now checks whether a prelude-named symbol is declared in the same crate. If so, it treats it as a user-defined shadow and generates the `use` import normally. Only truly external/unresolved prelude types skip import generation. diff --git a/docs/backlog/tasks/T072-cargo-toml-lib-path.md b/docs/backlog/tasks/T072-cargo-toml-lib-path.md new file mode 100644 index 000000000..866b9d87a --- /dev/null +++ b/docs/backlog/tasks/T072-cargo-toml-lib-path.md @@ -0,0 +1,55 @@ +# T072 — CrateDirectory Cargo.toml Missing [lib] Path + +| Field | Value | +| ------------------------- | ----------------------------------------------------- | +| **ID** | T072 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P1 — must-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T030 (CargoTomlFile), T038 (CrateDirectory crateType) | +| **Blocks** | — | + +--- + +## Description + +When `CrateDirectory` generates a `Cargo.toml`, it does not include a `[lib]` section with `path = "lib.rs"`. Cargo defaults to `src/lib.rs`, but `CrateDirectory` places `lib.rs` at the crate root (alongside `Cargo.toml`), so Cargo cannot find the crate entry point without an explicit path. + +--- + +## Scope Included + +- Add `[lib]` section to generated `Cargo.toml` with `path = "lib.rs"` when crate type is `lib` +- Add `[[bin]]` section with `path = "main.rs"` when crate type is `bin` +- Respect the `crateType` prop from T038 + +--- + +## Scope Excluded + +- Support for multiple binary targets +- Custom lib/bin names (use defaults based on package name) +- Workspace-level Cargo.toml + +--- + +## Acceptance Criteria + +- [x] `Cargo.toml` includes `[lib]\npath = "lib.rs"` for library crates +- [x] `Cargo.toml` includes `[[bin]]\nname = "..."\npath = "main.rs"` for binary crates +- [x] `cargo check` can locate the crate root without manual edits +- [x] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. Generated crate fails `cargo check` because Cargo looks for `src/lib.rs` but the file is at `./lib.rs`. + +## Completion Note + +2026-03-12: Completed. `CargoTomlFile` now emits `[lib]` and `[[bin]]` target sections (including explicit `path` entries), and tests were added to verify generated `Cargo.toml` output for both crate types. diff --git a/docs/backlog/tasks/T073-reference-inline-line-breaks.md b/docs/backlog/tasks/T073-reference-inline-line-breaks.md new file mode 100644 index 000000000..936da14fc --- /dev/null +++ b/docs/backlog/tasks/T073-reference-inline-line-breaks.md @@ -0,0 +1,59 @@ +# T073 — Reference Inline Rendering Inserts Unexpected Line Breaks + +| Field | Value | +| ------------------------- | ---------------------------------------------------- | +| **ID** | T073 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P2 — nice-to-have | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | Yes | +| **Dependencies** | T010 (Reference basics), T022 (Reference resolution) | +| **Blocks** | — | + +--- + +## Description + +The `Reference` component sometimes inserts unexpected line breaks when rendered inline within block-level contexts (e.g., inside function bodies, struct field type annotations). This produces formatting issues where a type reference wraps to a new line unexpectedly. + +The issue appears to be related to how the Reference component interacts with the Alloy rendering engine's whitespace and indentation handling in block scope contexts. + +--- + +## Scope Included + +- Investigate and fix the rendering behavior of `Reference` in inline/block contexts +- Ensure References render on the same line as their surrounding code +- Add test cases for inline Reference rendering in various contexts + +--- + +## Scope Excluded + +- Multi-line generic type References (e.g., `HashMap<\n K,\n V\n>`) — those may legitimately wrap +- Reference resolution logic (only rendering/whitespace is in scope) + +--- + +## Acceptance Criteria + +- [x] `field: HashMap` renders on a single line (no unexpected breaks) +- [x] `fn foo() -> Result` renders on a single line +- [x] References inside expressions (e.g., `let x: Type = ...`) render inline +- [x] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. Some References in block contexts produced unexpected line breaks in the generated output. + +--- + +## Completion Notes + +- 2026-03-12: Completed. Inline `Reference` rendering was stabilized across declaration/expression contexts and test coverage was updated accordingly. +- Gotcha: ensure `DocComment`/`ModuleDocComment` emit a trailing line break so the next declaration does not concatenate onto the doc-comment line. diff --git a/docs/backlog/tasks/T074-empty-dependencies-section.md b/docs/backlog/tasks/T074-empty-dependencies-section.md new file mode 100644 index 000000000..b2ba1b215 --- /dev/null +++ b/docs/backlog/tasks/T074-empty-dependencies-section.md @@ -0,0 +1,54 @@ +# T074 — Cargo.toml Renders Empty [dependencies] Section + +| Field | Value | +| ------------------------- | ------------------------------------------------ | +| **ID** | T074 | +| **Epic** | [E007](../epics/E007-bug-fixes.md) | +| **Type** | bug | +| **Status** | done | +| **Priority** | P3 — cosmetic | +| **Owner Role** | AI coding agent | +| **AI Executable** | Yes | +| **Human Review Required** | No | +| **Dependencies** | T030 (CargoTomlFile), T031 (Dependency tracking) | +| **Blocks** | — | + +--- + +## Description + +When a crate has no external dependencies, `CargoTomlFile` still renders an empty `[dependencies]` section in the generated `Cargo.toml`. While this is technically valid TOML and does not cause compilation errors, it is not idiomatic — Rust projects typically omit the section entirely when there are no dependencies. + +--- + +## Scope Included + +- Suppress `[dependencies]` section when there are no external crate dependencies +- Keep rendering it (with entries) when dependencies exist + +--- + +## Scope Excluded + +- `[dev-dependencies]` or `[build-dependencies]` sections +- Workspace dependency inheritance + +--- + +## Acceptance Criteria + +- [ ] No `[dependencies]` header appears in Cargo.toml when dependency list is empty +- [ ] `[dependencies]` with entries renders correctly when dependencies exist +- [ ] `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` passes + +--- + +## Evidence + +Discovered during rust-example review on 2026-03-12. Generated Cargo.toml includes `[dependencies]` with no entries. + +--- + +## Completion Note + +2026-03-12: Completed. `CargoTomlFile` now omits empty `[dependencies]` sections, and tests were updated to verify omission when no dependencies are present. diff --git a/docs/language-packages/rust/01-core-understanding.md b/docs/language-packages/rust/01-core-understanding.md new file mode 100644 index 000000000..b9a93d839 --- /dev/null +++ b/docs/language-packages/rust/01-core-understanding.md @@ -0,0 +1,772 @@ +# 1. Objective + +This document captures a deep, evidence-based understanding of the **Alloy core** (`@alloy-js/core`) framework — its architecture, abstractions, rendering pipeline, symbol system, extension surfaces, and testing patterns. + +**Purpose:** To provide a future AI agent (or human) with enough grounded knowledge of core to design and implement a new Rust language package (`@alloy-js/rust`) without misplacing responsibilities between core and language-specific code. + +**Scope:** Descriptive, not prescriptive. This document does not propose Rust-specific design. It catalogs what core provides, how existing language packages consume it, and what invariants must be respected. + +--- + +# 2. What Alloy Appears to Be + +Alloy is a **reactive, component-based code generation framework** written in TypeScript. It uses JSX (not React — a custom JSX runtime) to declaratively describe the structure of generated source code. + +**Generation model in plain language:** + +1. A developer authors a tree of JSX components that describes the desired output: files, directories, declarations, expressions, statements. +2. Components compose hierarchically. Core components handle structural concerns (files, scopes, formatting). Language-specific components handle syntax (classes, functions, imports). +3. Alloy renders this component tree reactively — using Vue's `@vue/reactivity` under the hood — producing an in-memory tree of formatted text. +4. A binder tracks declared symbols and resolves cross-references (e.g., an import needed because module A references a symbol in module B). +5. The framework prints the text tree through a Prettier-based formatter and writes the result to disk. + +**Key evidence:** + +- `packages/core/src/render.ts` — orchestrates the 4-phase pipeline (component tree → rendered text tree → document tree → file output). +- `packages/core/src/binder.ts` — symbol resolution engine. +- `packages/core/src/jsx-runtime.ts` — custom JSX runtime (not React). +- Existing language packages (`typescript`, `python`, `go`, `java`, `csharp`) all follow this model. + +--- + +# 3. Core Architectural Model + +## 3.1 Rendering Model + +Alloy renders in **four sequential phases**: + +### Phase 1 — Component Tree Building + +JSX templates produce a tree of `Child` nodes. A `Child` can be a string, number, boolean, function (reactive computation), `ComponentCreator`, `Ref`, `Refkey`, intrinsic element, or `CustomContext`. This phase is driven by user code. + +### Phase 2 — Rendering (`renderTree`) + +`render.ts:renderTree()` walks the component tree. For each node: + +- **Strings/numbers** are added directly to a `RenderedTextTree`. +- **Components** are called inside a reactive `effect()`, so they automatically re-render when dependencies change. +- **Functions** (nullary) are wrapped in effects that call them and insert results. +- **Intrinsic elements** (e.g., ``, ``, ``) are converted to `PrintHook` objects carrying Prettier formatting semantics. +- **Refkeys** encountered as children are converted to `Reference` components on the fly. + +The output is a `RenderedTextTree`: a recursive array of `(string | RenderedTextTree | PrintHook)[]`. + +### Phase 3 — Output Extraction & Printing (`sourceFilesForTree` + `printTree`) + +The rendered tree is traversed. Context metadata on each node identifies directories (`OutputDirectory`) and files (`ContentOutputFile`, `CopyOutputFile`). For each file, `printTree()` converts the `RenderedTextTree` into a Prettier `Doc` and calls `prettier.printDocToString()` to produce a formatted string. + +### Phase 4 — File System Writing (`writeOutput`) + +`write-output.ts:writeOutput()` uses the `AlloyHost` abstraction to write files and directories to disk. + +**Key files:** `packages/core/src/render.ts`, `packages/core/src/write-output.ts`, `packages/core/src/print-hook.ts`. + +## 3.2 Component / Composition Model + +Components are plain functions `(props: TProps) => Children`. They are not classes. They receive props (which may be reactive getters) and return children. + +**Composition tools:** + +- **`stc(Component)`** (Stateful Template Component) — wraps a component with a fluent API: `stc(MyComp)({ prop: v }).code\`...\``. Enables chaining `.code()`, `.text()`, `.children()`. Defined in `packages/core/src/stc.ts`. +- **`sti(name)`** (Stateful Template Intrinsic) — same fluent API for intrinsic elements. Defined in `packages/core/src/sti.ts`. +- **`mergeProps()`, `splitProps()`, `defaultProps()`** — reactive-aware prop utilities in `packages/core/src/props-combinators.ts`. +- **`code` and `text` template tags** — `code\`...\``preserves indentation structure and newlines;`text\`...\``collapses whitespace like JSX. Defined in`packages/core/src/code.ts`. +- **`mapJoin()`, `join()`** — list rendering with configurable joiners/enders. Defined in `packages/core/src/utils.tsx`. + +**Built-in components** (in `packages/core/src/components/`): + +| Category | Components | +| ---------------- | ----------------------------------------------------------------- | +| **Structure** | `Output`, `SourceDirectory`, `SourceFile`, `Scope`, `MemberScope` | +| **Declarations** | `Declaration`, `MemberDeclaration` | +| **References** | `Name`, `MemberName`, `AccessExpression`, `ReferenceOrContent` | +| **Formatting** | `Block`, `Indent`, `StatementList`, `Prose`, `Wrap` | +| **Files** | `TemplateFile`, `UpdateFile`, `AppendFile`, `CopyFile` | +| **Control flow** | `Show`, `For`, `Switch`/`Match`, `List` | + +## 3.3 Context / Providers / Hooks + +Alloy implements a provider-based context system modeled on Vue's composition API. + +**`createContext(defaultValue?, name?)`** creates a `ComponentContext` with a `.Provider` component. **`useContext(context)`** retrieves the value from the nearest ancestor Provider. Contexts are symbol-keyed and stored on the reactive owner chain. + +**Key contexts provided by core** (defined in `packages/core/src/context/`): + +| Context | Set by | Purpose | +| -------------------------- | ------------------- | -------------------------------- | +| `BinderContext` | `Output` | Access to the `Binder` instance | +| `ScopeContext` | `Scope` | Current symbol scope | +| `DeclarationContext` | `Declaration` | Current symbol being declared | +| `MemberDeclarationContext` | `MemberDeclaration` | Current member being declared | +| `SourceFileContext` | `SourceFile` | Current output file metadata | +| `SourceDirectoryContext` | `SourceDirectory` | Current output directory | +| `NamePolicyContext` | `Output` | Active naming convention policy | +| `FormatOptions` | `Output` | Formatting rules (indent, width) | + +**Taps** (`packages/core/src/tap.ts`): A mechanism for parent components to capture values from children. `createTap(tapper, handler?)` returns a component that, when rendered, calls `tapper` to capture a context value. Specialized variants: `createDeclarationTap`, `createMemberTap`, `createScopeTap`, `createSourceFileTap`. + +**Content slots** (`packages/core/src/content-slot.tsx`): `createContentSlot()` returns a component that tracks whether it has any content, with `.WhenEmpty` and `.WhenHasContent` sub-components for conditional rendering. + +## 3.4 Document / File Generation Model + +File structure is declared via components: + +```tsx + + + + {code`console.log("hello");`} + + + +``` + +- **`Output`** — root component; sets up the `Binder`, `NamePolicy`, and `FormatOptions` contexts. +- **`SourceDirectory`** — represents a directory; provides `SourceDirectoryContext`. +- **`SourceFile`** — represents a file; provides `SourceFileContext` including `path`, `filetype`, and a `reference` component for generating references to symbols in this file. + +The `filetype` on `SourceFile` is a string identifier. Language packages use it to control file extensions and formatting behavior. The `reference` prop is a component that language packages provide to render references to symbols (e.g., generating import statements). + +**Output types** (from `render.ts`): + +- `OutputDirectory` — `{ kind: "directory", path, contents: (OutputDirectory | OutputFile)[] }` +- `ContentOutputFile` — `{ kind: "file", path, filetype, contents: string }` +- `CopyOutputFile` — `{ kind: "copy-file", path, sourcePath }` + +## 3.5 Symbol / Reference Model + +Alloy has a comprehensive symbol system for tracking named entities and resolving cross-references. + +### Symbols (`OutputSymbol`) + +An abstract base class (`packages/core/src/symbols/output-symbol.ts`). Key reactive properties: + +- `name` — display name (may be transformed by name policy) +- `originalName` — the name as requested +- `refkeys` — array of `Refkey` identifiers +- `spaces` — which `OutputSpace` instances contain this symbol +- `memberSpaces` — scoped containers for member symbols (keyed by strings like `"static"`, `"instance"`) +- `type` — optional type symbol (if set and `isTyped`, member lookups delegate to this type) +- `aliasTarget` — if set, this symbol is an alias +- `isTransient` — if true, symbol cannot be directly referenced; used for combining into other symbols +- `metadata` — arbitrary reactive metadata bag +- `namePolicy` — optional per-symbol naming function + +Language packages subclass this. For example, `TSOutputSymbol` adds `export`, `default`, `tsFlags`. `CSharpSymbol` adds `accessibility`, `isOverride`, `isAbstract`, etc. + +### Scopes (`OutputScope`) + +An abstract base class (`packages/core/src/symbols/output-scope.ts`). Scopes form a tree: + +- `parent` — parent scope (undefined for root) +- `children` — set of child scopes +- `ownerSymbol` — if set, this is a **member scope** that exposes the owner's members instead of its own declaration spaces + +Each scope subclass declares `static readonly declarationSpaces: string[]`. For example, `BasicScope` declares `["symbols"]`; TypeScript scopes declare `["values", "types"]`. + +### Spaces (`OutputSpace`) + +Two kinds (`packages/core/src/symbols/output-space.ts`): + +- **`OutputDeclarationSpace`** — symbols declared within a scope. +- **`OutputMemberSpace`** — member symbols of a particular symbol. + +Both are backed by `SymbolTable` (`packages/core/src/symbols/symbol-table.ts`), a reactive set with name/refkey indexing and automatic name deconfliction. + +### Refkeys (`Refkey`) + +The identity system for symbols (`packages/core/src/refkey.ts`): + +- **`SymbolRefkey`** — `{ key: string }`, deterministic from arguments. +- **`Namekey`** — a SymbolRefkey with a `name` property and optional naming flags. +- **`MemberRefkey`** — `{ base: Refkey, member: Refkey | string }`, for nested member access. +- `refkey(...args)` — creates a deterministic refkey; same args → same object (cached). +- `namekey(name, options?)` — creates a namekey. +- `memberRefkey(base, ...members)` — creates a chained member refkey. + +## 3.6 Scope and Name Resolution Model + +### Binder (`packages/core/src/binder.ts`) + +The `Binder` is the central resolution engine. Created by `createOutputBinder(options)` and provided via `BinderContext`. + +**Resolution:** `resolveDeclarationByKey(currentScope, refkey, options?)` returns a reactive `Ref` containing: + +- `symbol` — the resolved symbol +- `lexicalDeclaration` — the top-level symbol (if resolved through member path) +- `memberPath` — symbols traversed from lexical declaration to target +- `commonScope` — deepest scope containing both reference and declaration +- `pathUp` — scopes from reference scope up to common scope +- `pathDown` — scopes from common scope down to declaration scope +- `fullSymbolPath`, `fullReferencePath` — complete scope chains to root + +This information tells language packages how to generate qualified references, whether imports are needed, etc. + +**Symbol lifecycle managed by binder:** + +1. `notifySymbolCreated(symbol)` — registers symbol by refkeys, notifies waiters. +2. `notifySymbolDeleted(symbol)` — unregisters symbol. +3. Waiting refs: if a symbol is referenced before it's declared, the binder creates a `shallowRef` that resolves later. + +**Name conflict resolution:** Binder accepts an optional `nameConflictResolver` callback. Default behavior: renames conflicting symbols with `_2`, `_3` suffixes. + +**Member resolution:** An optional `MemberResolver` callback receives `(owner, member, context)` and can implement language-specific visibility/access rules. + +### Name Policies (`packages/core/src/name-policy.ts`) + +`createNamePolicy(namer)` creates a policy that transforms symbol names based on element type. The `namer` function receives `(name: string, element: T) => string`. Policies are provided via `NamePolicyContext` and applied when symbols are created. + +## 3.7 Formatting / Printing Model + +Alloy uses **Prettier's document IR** for output formatting. Intrinsic JSX elements map to Prettier builders: + +| Intrinsic Element | Prettier Builder | Purpose | +| ------------------------- | ----------------- | ---------------------------------- | +| `` | `group()` | Try single line; break if too wide | +| `` | `indent()` | Increase indent level | +| `` | `dedent()` | Decrease indent level | +| `` / `
` | `line` | Space if fits, newline if broken | +| `` / `` | `hardline` | Always newline | +| `` / `` | `softline` | Newline only if group breaks | +| `` / `` | `literalline` | Always newline, ignore indent | +| `` | `fill()` | Paragraph-like wrapping | +| `` | `ifBreak()` | Conditional on group break state | +| `` | `indentIfBreak()` | Conditional indent | +| `` | `lineSuffix()` | Append to end of line | +| `` | `breakParent` | Force parent group to break | +| `` | `align()` | Align to column | +| `` | `markAsRoot()` | Set indent root | +| `` | `dedentToRoot()` | Dedent to root level | + +The `code` template tag automatically converts indentation into nested `` structures and newlines into `` elements. + +`printTree()` in `render.ts` walks the `RenderedTextTree`, converts strings (splitting on newlines with hardlines between) and `PrintHook` objects into Prettier Docs, then calls `prettier.printDocToString()`. + +**Format options** (`packages/core/src/context/format-options.ts`): Configurable `printWidth`, `tabWidth`, `useTabs`, `insertFinalNewLine`. + +## 3.8 Extension / Plugin Surfaces for Language Packages + +Language packages extend core by: + +1. **Subclassing `OutputSymbol`** with language-specific properties. +2. **Subclassing `OutputScope`** with language-specific declaration spaces. +3. **Defining `memberSpaces`** on symbol subclasses (e.g., `["static", "instance", "private-static", "private-instance"]` for TypeScript). +4. **Defining `declarationSpaces`** on scope subclasses (e.g., `["values", "types"]` for TypeScript/Go, `["symbols"]` for Python/Java). +5. **Implementing a `NamePolicy`** via `createNamePolicy()`. +6. **Building JSX components** that render language syntax using core's formatting intrinsics. +7. **Providing a `Reference` component** (set on `SourceFile`) that generates language-specific references (imports, qualified names, etc.). +8. **Providing external dependency descriptors** (e.g., `createPackage()` for TS, `createModule()` for Go/Python). + +--- + +# 4. Key Core Concepts + +## 4.1 `Children` / `Child` + +**What it means:** The universal type for component return values and JSX children. A `Child` is one of: string, number, boolean, null, undefined, `Ref`, `Refkey`, function `() => Children`, `ComponentCreator`, `IntrinsicElement`, `RenderableObject`, or `CustomContext`. `Children` is `Child | Children[]` (recursive). + +**Why it matters:** Every component must return `Children`. Understanding the valid child types is essential to writing components. + +**Key files:** `packages/core/src/runtime/component.ts` + +## 4.2 `ComponentCreator` + +**What it means:** The runtime representation of a component invocation — wraps a `Component` function with its props, optional tag, and source location. Created by `createComponent()`. + +**Why it matters:** This is what JSX expressions produce after compilation. The render loop dispatches on `ComponentCreator` to call the component function. + +**Key files:** `packages/core/src/runtime/component.ts`, `packages/core/src/jsx-runtime.ts` + +## 4.3 `RenderedTextTree` + +**What it means:** The intermediate tree after rendering: `(string | RenderedTextTree | PrintHook)[]`. Strings are literal text; nested arrays are subtrees; `PrintHook` objects carry formatting instructions. + +**Why it matters:** This is the bridge between the component/rendering phase and the printing phase. Language packages don't normally interact with it directly, but understanding it clarifies how formatting works. + +**Key files:** `packages/core/src/print-hook.ts`, `packages/core/src/render.ts` + +## 4.4 `OutputSymbol` and `OutputScope` + +**What it means:** Abstract base classes for all symbols and scopes. Symbols are named, referenceable entities. Scopes are containers that form a tree hierarchy. + +**Why it matters:** Every language package must subclass both. The choice of `declarationSpaces` and `memberSpaces` determines the structure of the symbol table. + +**Key files:** `packages/core/src/symbols/output-symbol.ts`, `packages/core/src/symbols/output-scope.ts` + +## 4.5 `SymbolTable` / `OutputSpace` + +**What it means:** Reactive collections of symbols with name/refkey indexing and automatic name deconfliction. `OutputDeclarationSpace` lives inside scopes; `OutputMemberSpace` lives inside symbols. + +**Why it matters:** Name conflicts are resolved here. The deconfliction strategy can be customized per binder. + +**Key files:** `packages/core/src/symbols/symbol-table.ts`, `packages/core/src/symbols/output-space.ts` + +## 4.6 `Refkey` + +**What it means:** A stable identity token for symbols. Can be a `SymbolRefkey` (keyed), `Namekey` (named + keyed), or `MemberRefkey` (nested member access). Same arguments produce the same refkey (cached). + +**Why it matters:** Refkeys are the primary mechanism for cross-file, cross-scope references. A user writes `refkey()` to create a stable ID, passes it to a `Declaration`, and later uses it to reference that symbol. + +**Key files:** `packages/core/src/refkey.ts` + +## 4.7 `Binder` and `ResolutionResult` + +**What it means:** The binder is the resolution engine that maps refkeys to symbols and computes scope paths between references and declarations. `ResolutionResult` contains the resolved symbol plus path information (pathUp, pathDown, commonScope, memberPath). + +**Why it matters:** Language packages use `ResolutionResult` to decide whether to emit an import statement, a qualified name, or a simple identifier. + +**Key files:** `packages/core/src/binder.ts` + +## 4.8 `NamePolicy` + +**What it means:** A pluggable naming convention system. `createNamePolicy(namer)` creates a policy parameterized by element type (e.g., `"class"`, `"function"`, `"variable"`). The policy transforms names (e.g., `camelCase`, `PascalCase`, `snake_case`). + +**Why it matters:** Every language package defines a name policy. Symbols opt in via element type and can opt out via `ignoreNamePolicy`. + +**Key files:** `packages/core/src/name-policy.ts` + +## 4.9 `code` and `text` Template Tags + +**What it means:** `code\`...\``is a template tag that preserves indentation and newlines, converting them to``and``structures.`text\`...\`` collapses whitespace like JSX. + +**Why it matters:** The `code` tag is the primary way to emit formatted code from within components. + +**Key files:** `packages/core/src/code.ts` + +## 4.10 Intrinsic Elements + +**What it means:** JSX elements like ``, ``, ``, etc. that map directly to Prettier formatting builders. They are not HTML elements. + +**Why it matters:** These are the low-level formatting primitives. The `code` tag generates them automatically, but components can use them directly for fine-grained control. + +**Key files:** `packages/core/src/runtime/intrinsic.ts`, `packages/core/src/render.ts` (intrinsic handling) + +## 4.11 Reactivity System + +**What it means:** Alloy uses `@vue/reactivity` for reactive state management. Core re-exports `ref`, `shallowRef`, `computed`, `reactive`, `effect`, `memo`, `watch`, `untrack`, `onCleanup`. All symbol/scope properties are reactive. + +**Why it matters:** Components re-render automatically when dependencies change. Symbol resolution is reactive — references update when symbols are created/moved. Language packages must be aware that props may be reactive getters. + +**Key files:** `packages/core/src/reactivity.ts` + +## 4.12 Context System + +**What it means:** `createContext(default?, name?)` creates a context; `.Provider` sets a value; `useContext(ctx)` reads it. Contexts are symbol-keyed and propagated through the reactive owner chain. + +**Why it matters:** Contexts are the primary mechanism for passing data down the component tree (binder, scope, source file info, format options, etc.). + +**Key files:** `packages/core/src/context.ts` + +## 4.13 Symbol Flow + +**What it means:** `takeSymbols()` marks a context as collecting symbols; `emitSymbol(symbol)` sends a symbol up to the nearest taking context. Symbols bubble up through contexts but stop at scope boundaries. + +**Why it matters:** Enables parent components to collect child-declared symbols (e.g., for a class collecting its members). + +**Key files:** `packages/core/src/symbols/symbol-flow.ts` + +## 4.14 `stc` / `sti` + +**What it means:** `stc(Component)` wraps a component with a fluent API for composition. Returns a callable creator with `.code()`, `.text()`, `.children()` chainable methods. `sti(name)` does the same for intrinsic elements. + +**Why it matters:** Language packages use `stc` extensively to create composable component APIs. + +**Key files:** `packages/core/src/stc.ts`, `packages/core/src/sti.ts` + +## 4.15 Host System + +**What it means:** `AlloyHost` / `AlloyFileInterface` abstract file system operations. Node.js and browser implementations exist. + +**Why it matters:** Enables Alloy to work in different environments. Language packages don't typically interact with this directly. + +**Key files:** `packages/core/src/host/alloy-host.ts`, `packages/core/src/host/alloy-host.browser.ts` + +## 4.16 Scheduler + +**What it means:** Manages batching and flushing of reactive effects. `queueJob()` adds work; `flushJobs()` runs all queued work synchronously. Supports async coordination via `trackPromise()`. + +**Why it matters:** Symbol name deconfliction and reactive updates are deferred to the scheduler. Tests must call `flushJobs()` after symbol mutations. + +**Key files:** `packages/core/src/scheduler.ts` + +## 4.17 Diagnostics + +**What it means:** `DiagnosticsCollector` collects structured `Diagnostic` messages (`{ id, message, severity, source?, componentStack? }`). Auto-captures render stack for context. `emitDiagnostic()` is the primary API. + +**Why it matters:** Language packages can emit warnings/errors with rich context about what component triggered the issue. + +**Key files:** `packages/core/src/diagnostics.ts` + +--- + +# 5. Core-to-Language Extension Surface + +## 5.1 Clearly Observed Extension Points + +These are directly exercised by all existing language packages: + +1. **`OutputSymbol` subclass** — Every language package defines a custom symbol class. + - TypeScript: `TSOutputSymbol` (`packages/typescript/src/symbols/ts-output-symbol.ts`) + - Python: `PythonOutputSymbol` (`packages/python/src/symbols/python-output-symbol.ts`) + - Go: `GoSymbol` (`packages/go/src/symbols/go-output-symbol.ts`) + - Java: `JavaOutputSymbol` (`packages/java/src/symbols/java-output-symbol.ts`) + - C#: `CSharpSymbol` (`packages/csharp/src/symbols/csharp-output-symbol.ts`) + +2. **`OutputScope` subclass hierarchy** — Multiple scope levels per language. + - TypeScript: `TSPackageScope → TSModuleScope → TSLexicalScope` + - Python: `PythonModuleScope → PythonLexicalScope` + - Go: `GoModuleScope → GoPackageScope → GoSourceFileScope → GoFunctionScope / GoNamedTypeScope → GoLexicalScope` + - Java: `JavaProjectScope → JavaPackageScope → JavaLexicalScope` + - C#: `CSharpNamespaceScope → CSharpSourceFileScope → CSharpClassScope / CSharpMethodScope → CSharpLexicalScope` + +3. **`static readonly declarationSpaces`** on scope classes: + - Dual namespace: `["values", "types"]` (TypeScript, Go) + - Single namespace: `["symbols"]` (Python, Java) + - Mixed: scope-dependent (C#) + +4. **`static readonly memberSpaces`** on symbol classes: + - TypeScript: `["static", "instance", "private-static", "private-instance"]` + - Python/Java: `["static", "instance"]` + - Go/C#: defined per scope type or symbol subclass, not centralized + +5. **`createNamePolicy()`** — All packages define a name policy. + - TypeScript: PascalCase for types, camelCase for values (`packages/typescript/src/name-policy.ts`) + - Python: PascalCase for classes, snake_case for functions/variables (`packages/python/src/name-policy.ts`) + - Go: no transformations, only reserved word handling (`packages/go/src/name-policy.ts`) + - Java: PascalCase for types, camelCase for values (`packages/java/src/name-policy.ts`) + - C#: PascalCase for public, camelCase for params, `_` prefix for private (`packages/csharp/src/name-policy.ts`) + +6. **Reference component** — Each package provides a component set on `SourceFile`'s `reference` prop that generates language-appropriate references (import statements, qualified names, etc.). + +7. **Symbol factory functions** — Each package exposes functions like `createFunctionSymbol()`, `createParameterSymbol()`, etc. that call core's `createSymbol(SymbolClass, name, spaces, options)`. + +8. **External dependency descriptors** — Each package provides a function to describe external libraries: + - TypeScript: `createPackage()` (`packages/typescript/src/create-package.ts`) + - Python: `createModule()` (`packages/python/src/create-module.ts`) + - Go: `createModule()` (`packages/go/src/create-module.ts`) + - Java: `createLibrary()` (`packages/java/src/create-library.ts`) + - C#: `createLibrary()` (`packages/csharp/src/create-library.ts`) + +9. **Custom contexts** — Language packages create their own contexts for language-specific state (e.g., `PackageContext`, `ModuleContext`, `NamespaceContext`). + +10. **JSX components for language syntax** — Each package provides components like `ClassDeclaration`, `FunctionDeclaration`, `InterfaceDeclaration`, `ImportStatement`, etc. + +## 5.2 Inferred Extension Patterns + +These patterns are consistent across packages but not formally documented as extension contracts: + +1. **Source directory layout** — A typical language package organizes as: + + ``` + src/ + ├── symbols/ # OutputSymbol subclass + factories + ├── scopes/ # OutputScope subclass hierarchy (or inline in symbols/) + ├── components/ # JSX components for language constructs + ├── context/ # Language-specific contexts + ├── builtins/ # Built-in type/library descriptors + ├── name-policy.ts # Naming conventions + ├── create-*.ts # External dependency descriptor factory + └── index.ts # Barrel export + ``` + +2. **Import statement component pattern** — Every language package has a component (often `ImportStatement` or `Reference`) that: + - Analyzes the `ResolutionResult` from the binder + - Determines if an import is needed (different file/module/package) + - Generates the language-appropriate import syntax + - Groups and sorts imports + +3. **Scope-to-component mapping** — Each scope level typically corresponds to a structural component (e.g., `PackageDirectory` → `PackageScope`, `SourceFile` → `ModuleScope`). + +4. **Symbol copying** — Language symbol subclasses implement `copy()` for instantiation patterns (generics, type aliases). The pattern calls `createSymbol()` with the same constructor and then `initializeCopy()`. + +5. **`useXScope()` hooks** — Language packages define convenience hooks like `useTSScope()`, `usePythonScope()`, `useGoScope()` that wrap `useContext(ScopeContext)` with type narrowing. + +--- + +# 6. Source File Inventory + +## 6.1 Core Package (`packages/core/src/`) + +### Rendering Pipeline + +| Path | Purpose | Key Exports | Relevance | +| ----------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| `render.ts` | Main render orchestrator | `render()`, `renderAsync()`, `renderTree()`, `sourceFilesForTree()`, `printTree()`, `OutputDirectory`, `OutputFile`, `ContentOutputFile`, `CopyOutputFile` | Core pipeline; language packages don't call directly but depend on its behavior | +| `write-output.ts` | File system writing | `writeOutput()` | Output phase | +| `print-hook.ts` | Print hook protocol | `PrintHook`, `isPrintHook()`, `RenderedTextTree` | Formatting bridge | +| `code.ts` | Template tags | `code`, `text` | Primary way to emit formatted code | +| `jsx-runtime.ts` | JSX runtime | `jsx`, `jsxs` | JSX compilation target | + +### Symbol System + +| Path | Purpose | Key Exports | Relevance | +| ----------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | +| `symbols/output-symbol.ts` | Base symbol class | `OutputSymbol`, `OutputSymbolOptions` | **Must subclass** for any language | +| `symbols/output-scope.ts` | Base scope class | `OutputScope`, `OutputScopeOptions` | **Must subclass** for any language | +| `symbols/output-space.ts` | Space types | `OutputDeclarationSpace`, `OutputMemberSpace`, `OutputSpace` | Containers for symbols | +| `symbols/symbol-table.ts` | Reactive symbol set | `SymbolTable` | Name/refkey indexing, deconfliction | +| `symbols/basic-symbol.ts` | Simple symbol | `BasicSymbol` | Reference implementation; `memberSpaces = ["static", "instance"]` | +| `symbols/basic-scope.ts` | Simple scope | `BasicScope` | Reference implementation; `declarationSpaces = ["symbols"]` | +| `symbols/symbol-flow.ts` | Symbol emission | `takeSymbols()`, `emitSymbol()`, `moveTakenMembersTo()`, `instantiateTakenMembersTo()` | Symbol collection across component boundaries | +| `symbols/symbol-slot.tsx` | Symbol collection component | `SymbolSlot`, `createSymbolSlot()` | Collecting emitted symbols | +| `symbols/decl.ts` | Declaration helper | `decl(namekey)` | Quick symbol declaration for BasicScope | +| `binder.ts` | Resolution engine | `createOutputBinder()`, `createScope()`, `createSymbol()`, `Binder`, `ResolutionResult`, `MemberResolver` | **Central API** for symbol management | +| `refkey.ts` | Identity system | `refkey()`, `namekey()`, `memberRefkey()`, `Refkey`, `SymbolRefkey`, `Namekey`, `MemberRefkey` | **Must use** for symbol identity | +| `name-policy.ts` | Naming conventions | `createNamePolicy()`, `NamePolicy`, `NamePolicyGetter` | **Must implement** for any language | +| `library-symbol-reference.ts` | External symbol bridge | `LibrarySymbolReference`, `TO_SYMBOL` | For library/builtin type references | + +### Component Infrastructure + +| Path | Purpose | Key Exports | Relevance | +| ---------------------------------- | ------------------- | ------------------- | ------------------------------------------------------------------ | +| `components/Output.tsx` | Root component | `Output` | Entry point; sets up binder, name policy, format options | +| `components/SourceFile.tsx` | File component | `SourceFile` | Must set `reference` prop to language-specific reference component | +| `components/SourceDirectory.tsx` | Directory component | `SourceDirectory` | File structure | +| `components/Declaration.tsx` | Symbol declaration | `Declaration` | Declares a symbol in current scope | +| `components/MemberDeclaration.tsx` | Member declaration | `MemberDeclaration` | Declares a member on current symbol | +| `components/Scope.tsx` | Scope creation | `Scope` | Creates a new scope level | +| `components/MemberScope.tsx` | Member scope | `MemberScope` | Creates a member scope for a symbol | +| `components/Block.tsx` | Code block | `Block` | `{ ... }` with indentation | +| `components/Indent.tsx` | Indentation | `Indent` | Indent children | +| `components/List.tsx` | List rendering | `List` | Maps arrays to joined output | +| `components/For.tsx` | Iteration | `For` | Collection iteration | +| `components/Show.tsx` | Conditional | `Show` | If/else rendering | +| `components/Name.tsx` | Symbol name | `Name` | Renders resolved symbol name | +| `components/AccessExpression.tsx` | Member access | `AccessExpression` | `obj.member` expressions | + +### Context System + +| Path | Purpose | Key Exports | Relevance | +| --------------------------- | ------------------- | ----------------------------------------------------- | ---------------------------------------- | +| `context.ts` | Context primitives | `createContext()`, `useContext()`, `ComponentContext` | **Must use** for language-specific state | +| `context/binder.ts` | Binder context | `BinderContext`, `useBinder()` | Access current binder | +| `context/scope.ts` | Scope context | `ScopeContext`, `useScope()` | Access current scope | +| `context/declaration.ts` | Declaration context | `DeclarationContext`, `useDeclaration()` | Access current declaration | +| `context/source-file.ts` | Source file context | `SourceFileContext`, `useSourceFile()` | Access current file metadata | +| `context/format-options.ts` | Format options | `FormatOptionsContext` | Formatting configuration | +| `context/name-policy.ts` | Name policy context | `NamePolicyContext`, `useNamePolicy()` | Access current name policy | + +### Composition & Utilities + +| Path | Purpose | Key Exports | Relevance | +| ----------------------- | ----------------- | -------------------------------------------------------------------------- | ----------------------------------------- | +| `stc.ts` | Component wrapper | `stc()` | Fluent API for component composition | +| `sti.ts` | Intrinsic wrapper | `sti()` | Fluent API for intrinsic composition | +| `props-combinators.ts` | Prop utilities | `mergeProps()`, `splitProps()`, `defaultProps()` | Reactive prop handling | +| `utils.tsx` | Common utilities | `mapJoin()`, `join()`, `children()`, `childrenArray()`, `traverseOutput()` | List rendering, output traversal | +| `content-slot.tsx` | Content tracking | `createContentSlot()` | Conditional rendering based on content | +| `tap.ts` | Context tapping | `createTap()`, `createDeclarationTap()`, etc. | Parent captures child context values | +| `resource.ts` | Async resources | `createResource()`, `createFileResource()` | Async data fetching | +| `reactive-union-set.ts` | Reactive set | `ReactiveUnionSet` | Aggregating symbols from multiple sources | + +### Runtime & Infrastructure + +| Path | Purpose | Key Exports | Relevance | +| ---------------------- | ------------------------ | -------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| `runtime/component.ts` | Component types | `Component`, `ComponentCreator`, `Child`, `Children`, `Props` | Type definitions for component system | +| `runtime/intrinsic.ts` | Formatting elements | Intrinsic element definitions | Prettier-mapped formatting primitives | +| `reactivity.ts` | Reactivity wrappers | `ref()`, `shallowRef()`, `computed()`, `effect()`, `memo()`, `watch()`, `untrack()`, `onCleanup()` | Vue reactivity with Alloy extensions | +| `scheduler.ts` | Job scheduler | `queueJob()`, `flushJobs()`, `flushJobsAsync()`, `scheduler()` | Batched reactive updates | +| `diagnostics.ts` | Diagnostic system | `DiagnosticsCollector`, `emitDiagnostic()`, `reportDiagnostics()` | Structured error/warning reporting | +| `render-stack.ts` | Component stack tracking | `pushStack()`, `popStack()`, `printRenderStack()` | Error diagnostics with component context | +| `host/alloy-host.ts` | File system abstraction | `AlloyHost`, `AlloyFileInterface` | Environment-portable I/O | + +## 6.2 Testing (`packages/core/testing/`) + +| Path | Purpose | Key Exports | +| ------------------------- | -------------------- | -------------------------------------------------------------------------------------- | +| `index.ts` | Testing barrel | Re-exports all testing utilities | +| `extend-expect.ts` | Vitest matchers | `toRenderTo()`, `toRenderToAsync()`, `toHaveDiagnostics()`, `toHaveDiagnosticsAsync()` | +| `render.ts` | Test rendering | `renderToString()`, `d` (dedent template tag), `dedent()`, `printTree()` | +| `create-test-wrapper.tsx` | Test wrapper factory | `createTestWrapper()` | + +--- + +# 7. Invariants and Constraints + +## 7.1 What Core Owns + +- **Rendering pipeline** — render, print, write phases are entirely core's responsibility. Language packages produce component trees; core renders them. +- **Symbol infrastructure** — `OutputSymbol`, `OutputScope`, `OutputSpace`, `SymbolTable`, `Binder` are core's domain. Language packages subclass but do not replace. +- **Refkey system** — identity and resolution is core-managed. Language packages use `refkey()`, `namekey()`, `memberRefkey()`. +- **Reactivity** — `@vue/reactivity` integration, scheduling, and effect management are core. +- **Formatting primitives** — intrinsic elements and the Prettier integration are core. +- **Context system** — `createContext()` / `useContext()` and the provider mechanism are core. + +## 7.2 What Language Packages Own + +- **Symbol semantics** — what constitutes a "class", "function", "interface", etc. +- **Scope semantics** — how many scope levels exist, what declaration spaces they have. +- **Member space definitions** — what member categories exist (e.g., static, instance, private). +- **Name policy** — how names are transformed per element type. +- **Reference rendering** — how to generate imports, qualified names, etc. for a given `ResolutionResult`. +- **Syntax components** — JSX components that emit language-specific syntax. +- **External dependency descriptors** — how to describe pre-existing libraries/packages. +- **Reserved word lists** — language-specific keyword conflicts. + +## 7.3 Assumptions About Rendering + +- Components are **functions**, not classes. +- Component props **may be reactive getters**. Do not destructure props; access them as `props.x`. +- The `code` template tag handles indentation automatically. Components should use it for multi-line code emission. +- Intrinsic elements are **not** HTML elements. They are Prettier formatting builders. +- The rendering is **reactive** — components re-render when their dependencies change. This is automatic and not something language packages need to manage directly. +- `<>` (fragments) are used instead of ``. + +## 7.4 Assumptions About Symbol Handling + +- Every language symbol class must extend `OutputSymbol` and define `static readonly memberSpaces`. +- Every language scope class must extend `OutputScope` and define `static readonly declarationSpaces`. +- Symbols are created via `createSymbol(Constructor, name, spaces, options)` — never directly instantiated. +- Scopes are created via `createScope(Constructor, ...args)` — never directly instantiated. +- A symbol's `name` may be transformed by its `namePolicy`. The original name is preserved in `originalName`. +- Refkeys must be created before use and should be stable (same args → same refkey). +- Name deconfliction is automatic (via `SymbolTable`) but can be customized via `nameConflictResolver`. +- Transient symbols cannot be referenced directly; their members must be moved to non-transient symbols. + +## 7.5 Assumptions About Output Structure + +- The output is always a tree of directories and files. +- `Output` is always the root component. +- `SourceFile` requires a `path` and `filetype`. +- `SourceFile` accepts a `reference` prop — a component that language packages must provide to render references to symbols declared in that file. +- File contents are formatted by Prettier using the configured format options. + +--- + +# 8. Testing Patterns in Core + +## 8.1 Framework + +Alloy uses **Vitest** for all testing. Tests are `.test.tsx` files using JSX. + +## 8.2 Custom Vitest Matchers + +Core provides custom matchers in `packages/core/testing/extend-expect.ts`: + +- **`toRenderTo(expected)`** — renders JSX and compares to expected string (single file) or `Record` (multi-file). +- **`toRenderToAsync(expected)`** — async variant. +- **`toHaveDiagnostics(expected)`** — validates emitted diagnostics. +- **`toHaveDiagnosticsAsync(expected)`** — async variant. + +These are enabled by importing `"@alloy-js/core/testing"` in test files. + +## 8.3 Test Utilities + +- **`d\`...\``** — template tag that dedents multi-line strings, removing common leading whitespace. Essential for readable expected output. +- **`renderToString(jsx)`** — convenience: render → printTree → string. +- **`toSourceText(children, options?, path?)`** — language package test utility (defined in each package's `test/utils.tsx`) that wraps children in appropriate `Output` + `SourceFile` and returns the rendered string. +- **`findFile(outputDir, path)`** — extracts a specific file from multi-file output. +- **`assertFileContents(outputDir, expected)`** — batch validation of multiple files. + +## 8.4 Testing Patterns + +**Pattern 1: Simple rendering assertion** + +```tsx +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; + +it("renders", () => { + expect().toRenderTo(d`expected output`); +}); +``` + +**Pattern 2: Multi-file with symbol references** + +```tsx +const key = refkey(); +expect( + + + + + + + + , +).toRenderTo({ "a.ts": "...", "b.ts": "..." }); +``` + +**Pattern 3: Symbol/scope unit tests** + +```tsx +const scope = createScope(BasicScope, "test", undefined, { binder }); +const symbol = createSymbol(BasicSymbol, "sym", undefined, { binder }); +flushJobs(); // Must flush after symbol mutations +expect(scope.symbols.symbolNames.has("sym")).toBe(true); +``` + +**Pattern 4: Language package test utility** + +```tsx +// In test/utils.tsx +export function toSourceText(children, options?, path?) { + // Wraps in Output + SourceFile + language-specific setup +} + +// In test file +it("renders class", () => { + const res = toSourceText(); + expect(res).toBe(d`class Foo {}`); +}); +``` + +## 8.5 Implications for New Language Packages + +- **Must import `"@alloy-js/core/testing"`** in every test file to get custom matchers. +- **Must define `test/utils.tsx`** with `toSourceText()` and related helpers. +- **Must call `flushJobs()`** after reactive symbol operations before assertions. +- **Use `d\`...\``** for all multi-line expected output to handle indentation. +- **Test cross-file references** to verify import generation works. +- **Test name policy** to verify naming conventions are applied correctly. + +--- + +# 9. Ambiguities / Open Questions + +1. **`filetype` semantics** — `SourceFile`'s `filetype` is a free-form string. It's unclear if core assigns any behavior to specific filetype values or if it's purely for language package use. Observed values: `"typescript"`, `"python"`, `"go"`, etc. + +2. **Member resolver contract** — The `MemberResolver` callback on the binder is optional and its usage varies across language packages. The exact contract for when it's called vs. when default member resolution is used needs verification per language. + +3. **`defaultDeclarationSpace`** — `BasicScope` defines `static readonly defaultDeclarationSpace = "symbols"`. It's unclear whether this is a formal core concept used by the binder or just a convention. + +4. **Transient symbol lifecycle** — The exact patterns for when transient symbols should be used, how their members are combined, and the typical component patterns around them are sparsely documented. The `SymbolSlot` component seems to be the primary consumer but usage examples in language packages are limited. + +5. **Dual build system** — Core has `dist/` (production) and `dist/dev/` (development) builds. The `"source"` export condition is used by Vitest. It's unclear if language packages need to replicate this exact structure or can simplify. + +6. **Language-specific `decl()` functions** — Core provides `decl()` for `BasicScope`, and the code comments note that language-specific `decl` functions are needed for other scopes. The pattern for implementing these is not standardized. + +7. **`getSymbolCreatorSymbol()` / descriptor system** — The mechanism for lazy symbol creation from external dependency descriptors varies between packages and is one of the less-documented extension surfaces. + +8. **Symbol `copy()` semantics** — All language symbols implement `copy()`, but the exact contract (what must be preserved, what can differ) varies and is not formally documented in core. + +--- + +# 10. Key Takeaways for Future Language Package Design + +1. **Subclass `OutputSymbol` and `OutputScope`** — This is non-negotiable. Define `memberSpaces` and `declarationSpaces` carefully; they determine the structure of the entire symbol table. + +2. **Decide declaration space strategy early** — TypeScript and Go use dual namespaces (`values` + `types`); Python and Java use a single namespace. Rust has types, values, macros, and lifetimes — the namespace model needs deliberate design. + +3. **Implement a `NamePolicy`** — Every language has naming conventions. Use `createNamePolicy()`. Define element types that map to your language's constructs. Handle reserved words. + +4. **Build a `Reference` component** — This is the most complex part of a language package. It must analyze `ResolutionResult` and generate the appropriate reference syntax (imports, `use` statements, qualified paths, etc.). + +5. **Use core's formatting intrinsics** — Don't reinvent formatting. Use `code\`...\``, ``, ``, ``, etc. The Prettier integration handles line wrapping and indentation. + +6. **Props may be reactive** — Never destructure props in component definitions. Access them as `props.x`. This is an Alloy invariant, not a React pattern. + +7. **Test with `toRenderTo()` and `d\`...\``** — Follow the established testing pattern. Import `"@alloy-js/core/testing"`, create a `test/utils.tsx` with `toSourceText()`. + +8. **Follow the established directory structure** — `symbols/`, `components/`, `context/`, `builtins/`, `name-policy.ts`, `create-*.ts`, `index.ts`. + +9. **Symbol factories over direct construction** — Always use `createSymbol()` and `createScope()` from the binder module. Never construct symbols or scopes directly. + +10. **External dependency descriptors are essential** — Users will need to reference `std` library types, `serde`, `tokio`, etc. Implement a `createCrate()` or similar factory early. + +11. **Study Go and C# packages closely** — Go (systems language with packages, dual namespaces, no classes) and C# (rich type system with namespaces, visibility modifiers) are likely the closest analogs for Rust's feature set. + +12. **Reactivity is automatic** — Don't try to manage re-rendering manually. Use `memo()` for expensive computations, `computed()` for derived values, and let the scheduler handle batching. + +13. **`flushJobs()` in tests** — After any reactive symbol operation in tests, call `flushJobs()` before making assertions. This is a common pitfall. diff --git a/docs/language-packages/rust/02-existing-language-patterns.md b/docs/language-packages/rust/02-existing-language-patterns.md new file mode 100644 index 000000000..ad15817e1 --- /dev/null +++ b/docs/language-packages/rust/02-existing-language-patterns.md @@ -0,0 +1,760 @@ +# 1. Objective + +This document compares four existing Alloy language packages — **TypeScript**, **Java**, **Python**, and **C#** — to extract stable, reusable patterns that will inform the design of a new **Rust** language package (`@alloy-js/rust`). + +It builds on the core understanding established in [`docs/language-packages/rust/01-core-understanding.md`](./01-core-understanding.md). The purpose here is **not** to restate core, but to identify: + +- The recurring "shape" of a language package +- Cross-cutting patterns every package follows +- Language-specific divergences that cannot be generalized +- The minimum viable surface a new package should provide + +This document is optimized for future AI agents that will design and implement `@alloy-js/rust`. + +--- + +# 2. Packages Compared + +| Language | Package Path | Approx. Source Files | Approx. Components | +| ---------- | ---------------------- | -------------------- | ------------------ | +| TypeScript | `packages/typescript/` | ~65 | ~43 | +| Java | `packages/java/` | ~35 | ~26 | +| Python | `packages/python/` | ~52 | ~33 | +| C# | `packages/csharp/` | ~238 | ~34 | + +--- + +# 3. Common Package Anatomy + +## 3.1 Package Layout + +All four packages share a remarkably consistent directory structure: + +``` +packages// +├── src/ +│ ├── index.ts # Barrel export (re-exports everything) +│ ├── name-policy.ts # Language-specific naming conventions +│ ├── name-conflict-resolver.ts # (TS, Python only) Custom deconfliction +│ ├── create-.ts # External dependency descriptor factory +│ ├── parameter-descriptor.ts # (TS, Python) Parameter metadata types +│ ├── utils.ts # Utility helpers +│ ├── symbols/ +│ │ ├── index.ts # Symbol barrel +│ │ ├── -output-symbol.ts # OutputSymbol subclass +│ │ ├── -*-scope.ts # OutputScope subclasses (1–6 files) +│ │ ├── factories.ts # Symbol creation functions +│ │ └── reference.ts # Reference resolution helper +│ ├── scopes/ # (C# only) Separate scope directory +│ ├── components/ +│ │ ├── index.ts # Component barrel +│ │ ├── stc/index.ts # STC wrappers for external use +│ │ ├── SourceFile.tsx # Language-specific source file +│ │ ├── Declaration.tsx # Base declaration component +│ │ ├── Reference.tsx # Symbol reference component +│ │ ├── ImportStatement.tsx # Import/using generation +│ │ └── ... # Language-specific declaration/expression/statement components +│ ├── context/ +│ │ └── type-ref-context.tsx # (TS, Python) Type reference tracking +│ └── builtins/ +│ └── .ts # Pre-defined external library descriptors +├── test/ +│ ├── utils.tsx # toSourceText(), findFile(), assertFileContents() +│ ├── vitest.setup.ts # Imports @alloy-js/core/testing +│ └── *.test.tsx # Test files per feature +├── package.json +├── tsconfig.json +└── vitest.config.ts +``` + +**Observed in:** All four packages follow this pattern. C# is the most complex (separate `scopes/` dir, ~80 builtin files). Java is the simplest. + +## 3.2 Public API Surface + +Every package's `index.ts` re-exports from: + +1. `./components/index.js` — all JSX components +2. `./symbols/index.js` — symbol class, scope classes, factories +3. `./name-policy.js` — `createNamePolicy()`, `useNamePolicy()` +4. `./create-.js` — external dependency factory +5. `./builtins/index.js` — pre-defined library descriptors +6. Additional: context exports, parameter descriptors, utilities + +**Pattern:** Packages are consumed as `import * as lang from "@alloy-js/"` and then used as ``, ``, etc. + +## 3.3 Component Organization + +Components consistently fall into these categories: + +| Category | Examples | Present In | +| ------------------------------- | ------------------------------------------------------------------------------------ | ------------ | +| **Source file** | `SourceFile` | All | +| **Package/module/project root** | `PackageDirectory`, `ProjectDirectory`, `ModuleDirectory` | All | +| **Declarations** | `ClassDeclaration`, `FunctionDeclaration`, `InterfaceDeclaration`, `EnumDeclaration` | All | +| **Members** | `Method`/`MethodDeclaration`, `Field`, `Property`, `Constructor` | All | +| **Expressions** | `FunctionCallExpression`, `MemberExpression`, `NewExpression`, `AccessExpression` | All | +| **Statements** | `IfStatement`, `SwitchStatement`, `TryStatement`, `VarDeclaration` | TS, C# | +| **References/imports** | `Reference`, `ImportStatement`/`Usings` | All | +| **Type references** | `TypeRefContext`, `TypeArguments`, `TypeParameters` | All | +| **Modifiers/annotations** | `Modifiers`, `Annotation`, `Attribute`, decorators | All | +| **Documentation** | `JSDoc`, `PyDoc`, `DocComment` | All | +| **Values/literals** | `Value`/`ValueExpression`, `Atom` | All | +| **Structural/scope** | `LexicalScope`, `MemberScope`, `BlockScope` | All | +| **Build system** | `PackageJsonFile`, `PomFile`, `CsProjFile` | TS, Java, C# | +| **STC wrappers** | `stc/index.ts` | All | + +## 3.4 Symbol / Reference Helpers + +Every package provides: + +1. **Symbol class** extending `OutputSymbol` with language-specific properties. +2. **Scope hierarchy** with 2–7 scope classes extending `OutputScope`. +3. **Factory functions** for creating symbols in appropriate spaces (e.g., `createFunctionSymbol()`, `createMethodSymbol()`). +4. **A `Reference` component** that resolves a refkey, emits the symbol, and adds imports. +5. **A `ref()` helper** (TS via `Reference`, Java explicitly, Python/C# via symbol emission) that the import system consumes. + +## 3.5 File / Module Abstractions + +Every package defines: + +- **A `SourceFile` component** that wraps core's `SourceFile`, creates a language-specific scope (module scope), and auto-generates imports. +- **A root directory component** that creates a package/project/module scope and optionally generates build files (package.json, pom.xml, .csproj). + +## 3.6 Declaration and Expression Modeling + +**Declarations** follow a consistent pattern: + +1. Accept `name`, `refkey`, optional doc/metadata props. +2. Create a symbol via a factory function. +3. Create a scope (lexical or member) for the body. +4. Render the language syntax with children inside the scope. + +**Expressions** are lighter: + +1. Accept a `target` or content prop and optional `args`. +2. Render the expression syntax. +3. May create transient symbols (e.g., `NewExpression` in TS). + +## 3.7 Formatting Utilities + +All packages use: + +- Core's `code` template tag for multi-line code emission. +- Core's intrinsic elements (``, ``, ``, etc.) via print hooks. +- Core's `mapJoin()` for list rendering with joiners. +- Core's `Block` component for `{ ... }` blocks (TS, Java, C#) or indented blocks (Python uses custom `PythonBlock`). + +## 3.8 Tests and Examples + +Every package has: + +- A `test/utils.tsx` with `toSourceText()` and related helpers. +- Tests organized per feature (one test file per component or concept). +- Cross-file reference tests validating import generation. + +--- + +# 4. Cross-Language Concept Matrix + +## 4.1 Source File / Module Representation + +| Concept | TypeScript | Java | Python | C# | +| -------------------- | -------------------------------- | --------------------------------------- | ------------------- | ----------------------------------------- | +| **File component** | `SourceFile` | `SourceFile` | `SourceFile` | `SourceFile` | +| **Scope created** | `TSModuleScope` | `JavaLexicalScope` | `PythonModuleScope` | `CSharpSourceFileScope` | +| **File extension** | `.ts` | `.java` | `.py` | `.cs` | +| **Module structure** | One module per file | One public class per file | One module per file | Namespace-based, multiple types per file | +| **Root component** | `PackageDirectory` | `ProjectDirectory` + `PackageDirectory` | None (flat modules) | `Namespace` (file-scoped or block-scoped) | +| **Build file** | `package.json` + `tsconfig.json` | `pom.xml` (Maven) | None | `.csproj` | + +**Shared principle:** Every package has a `SourceFile` component that wraps core's `SourceFile`, creates a language-specific scope, and handles import generation. + +**What varies:** The scope hierarchy depth, whether a root container is needed, and the associated build system. + +## 4.2 Imports / Usings / Package Declarations + +| Concept | TypeScript | Java | Python | C# | +| --------------------- | ----------------------------------------- | --------------------------------------------- | --------------------------------------------- | -------------------------------------------- | +| **Import syntax** | `import { X } from "path"` | `import pkg.Class;` | `from mod import X` | `using Namespace;` | +| **Type-only imports** | `import type { X }` | N/A (all imports are types) | `if TYPE_CHECKING:` block | N/A (using covers both) | +| **Import generation** | Automatic via `TSModuleScope.addImport()` | Automatic via `SourceFileContext.addImport()` | Automatic via `PythonModuleScope.addImport()` | Automatic via `CSharpSourceFileScope.usings` | +| **Sorting** | By module path | By package name | By module, type vs value | Alphabetical | +| **Grouping** | None (flat sorted) | None | Type imports separate from value imports | None | +| **Wildcard imports** | No | `import pkg.*` | No | No | +| **Import component** | `ImportStatement` / `ImportStatements` | `ImportStatement` / `ImportStatements` | `ImportStatement` / `ImportStatements` | `Usings` / `Using` | + +**Shared principle:** Imports are generated automatically by the reference resolution system. When a symbol from another scope is referenced, the module scope records the needed import and the SourceFile renders all accumulated imports. + +**What varies:** Syntax, type-only handling (TS and Python have special treatment), and grouping/sorting rules. + +## 4.3 Identifiers and Symbol References + +| Concept | TypeScript | Java | Python | C# | +| ----------------------- | -------------------------------------------- | ------------------------------- | ------------------------------------------------ | --------------------------------------------- | +| **Reference component** | `Reference` | `Reference` | `Reference` | `Reference` | +| **Resolution via** | `useBinder().resolve()` | `ref()` helper | `useBinder().resolve()` | `useBinder().resolve()` | +| **Import trigger** | `emitSymbol()` + `TSModuleScope.addImport()` | `SourceFileContext.addImport()` | `emitSymbol()` + `PythonModuleScope.addImport()` | Using added to `CSharpSourceFileScope.usings` | +| **Qualified names** | Relative paths (`./foo.js`) | `package.ClassName` | `module.symbol` | `Namespace.Type` | +| **Name rendering** | Symbol name (after policy) | Symbol name (after policy) | Symbol name (after policy) | Symbol name (after policy) | + +**Shared principle:** `Reference` components accept a `refkey`, resolve it via the binder, emit the symbol for import tracking, and render the symbol's name. + +## 4.4 Literals / Values + +| Concept | TypeScript | Java | Python | C# | +| ------------------- | ------------------ | ---------------- | -------------- | ------------------------- | +| **Component** | `ValueExpression` | `Value` | `Atom` | `VarDeclaration` (inline) | +| **String literals** | `"string"` | `"string"` | `"string"` | `"string"` | +| **Boolean** | `true`/`false` | `true`/`false` | `True`/`False` | `true`/`false` | +| **Null** | `null`/`undefined` | `null` | `None` | `null` | +| **Array/list** | `[a, b, c]` | N/A (no literal) | `[a, b, c]` | N/A (no literal) | +| **Object/dict** | `{ key: val }` | N/A | `{key: val}` | N/A | + +**Shared principle:** Each package has a component or utility that converts JavaScript values to language-appropriate literals. + +**What varies:** Null/boolean keywords, collection literal syntax, and what types have literal representations. + +## 4.5 Expressions + +| Concept | TypeScript | Java | Python | C# | +| -------------------- | -------------------------------------------- | ------------------- | -------------------------------------------- | ---------------------------------- | +| **Function call** | `FunctionCallExpression` | N/A (inline `code`) | `FunctionCallExpression` | `InvocationExpression` | +| **Member access** | `MemberExpression` + `MemberExpression.Part` | N/A (inline) | `MemberExpression` + `MemberExpression.Part` | `AccessExpression` + chained parts | +| **Constructor call** | `NewExpression` | `ObjectDeclaration` | `ClassInstantiation` | Inline `new` | +| **Arrow/lambda** | `ArrowFunction` | N/A | N/A | Expression body on `Method` | + +**Shared principle:** Expression components accept a `target` and `args`, render the call syntax. Member access supports chaining with parts. + +**What varies:** TypeScript and Python have the richest expression component sets. Java relies more on inline `code` template usage. C# has `AccessExpression` with a powerful part descriptor model. + +## 4.6 Statements + +| Concept | TypeScript | Java | Python | C# | +| ------------------ | ------------------------------------------------ | ------------------------ | --------------------- | --------------------------------------------- | +| **If/else** | `IfStatement` + `ElseIfClause` + `ElseClause` | N/A (inline) | N/A (inline) | `IfStatement` + `ElseIfClause` + `ElseClause` | +| **Switch/case** | `SwitchStatement` + `CaseClause` | N/A | N/A | N/A | +| **Try/catch** | `TryStatement` + `CatchClause` + `FinallyClause` | N/A | N/A | N/A | +| **Variable decl** | `VarDeclaration` | `Variable` | `VariableDeclaration` | `VarDeclaration` | +| **Statement list** | Via core `StatementList` | Via core `StatementList` | `StatementList` | Via core `StatementList` | + +**Shared principle:** TypeScript has the most complete statement coverage. Other packages rely on inline `code` for less common statements. + +**What varies:** Only TypeScript and C# provide dedicated statement components. Python and Java handle most statement-level constructs inline. + +## 4.7 Declarations + +| Concept | TypeScript | Java | Python | C# | +| --------------- | ---------------------- | --------------------- | -------------------------------------- | -------------------------------- | +| **Class** | `ClassDeclaration` | `Class` | `ClassDeclaration` | `ClassDeclaration` | +| **Interface** | `InterfaceDeclaration` | `Interface` | N/A (ABC) | `InterfaceDeclaration` | +| **Struct** | N/A | N/A | N/A | `StructDeclaration` | +| **Record** | N/A | N/A | N/A | `RecordDeclaration` | +| **Enum** | `EnumDeclaration` | `Enum` + `EnumMember` | `EnumDeclaration` (class & functional) | `EnumDeclaration` + `EnumMember` | +| **Function** | `FunctionDeclaration` | N/A (methods only) | `FunctionDeclaration` | N/A (methods only) | +| **Method** | Via `ClassMethod` | `Method` | `MethodDeclaration` + variants | `Method` | +| **Constructor** | Via class body | `Constructor` | `ConstructorDeclaration` | `Constructor` | +| **Type alias** | `TypeDeclaration` | N/A | N/A | N/A | +| **Variable** | `VarDeclaration` | `Variable` | `VariableDeclaration` | `VarDeclaration` + `Field` | +| **Namespace** | N/A (modules) | N/A (packages) | N/A (modules) | `Namespace` | +| **Property** | Via `InterfaceMember` | N/A | `PropertyDeclaration` | `Property` | +| **Dataclass** | N/A | N/A | `DataclassDeclaration` | N/A | + +**Shared principle:** All packages provide class, method, and variable declarations. They all create symbols, create scopes, and render the declaration syntax. + +**What varies:** Language-specific constructs (interfaces vs ABCs, structs, records, type aliases, namespaces, properties with get/set). + +## 4.8 Type References + +| Concept | TypeScript | Java | Python | C# | +| -------------------- | ------------------------ | ----------------------- | ---------------------- | ---------------------------------------------- | +| **Type context** | `TypeRefContext` | N/A (all refs implicit) | `TypeRefContext` | N/A (using covers both) | +| **Generic params** | `TypeParameters` | `TypeParameters` | N/A (limited generics) | `TypeParameters` | +| **Generic args** | Via inline | `TypeArguments` | `TypeArguments` | `TypeParameters` (with constraints) | +| **Type constraints** | Inline `extends` | `extends` in generics | N/A | `TypeParameterConstraints` (`where T : class`) | +| **Nullable** | `nullish` flag on symbol | N/A | N/A | `nullable` flag on symbol | + +**Shared principle:** TypeScript and Python have explicit type-reference contexts to distinguish type-only from value usage (for import optimization). Java and C# don't need this distinction. + +## 4.9 Functions / Methods + +| Concept | TypeScript | Java | Python | C# | +| ------------------- | --------------------------------------------- | ------------------------------- | --------------------------- | ----------------------------- | +| **Parameters** | `ParameterDescriptor[]` or `Parameters` child | `Record` | `ParameterDescriptor[]` | `ParameterProps[]` | +| **Return type** | `returnType` prop | `return` prop (defaults "void") | `returnType` prop | `returns` prop | +| **Async** | `async` boolean prop | N/A | `async` boolean prop | `async` boolean prop | +| **Type params** | `typeParameters` prop | `generics` prop | `typeParameters` prop | `typeParameters` prop | +| **Function body** | `children` | `children` | `children` (default `pass`) | `children` | +| **Expression body** | `ArrowFunction` | N/A | N/A | `expression` prop on `Method` | + +**Shared principle:** Functions/methods accept parameters, return type, optional type parameters, and a body. All create symbols and scopes. + +**What varies:** Parameter descriptor format (array of objects vs record of name→type), default bodies, async support, and expression body syntax. + +## 4.10 Classes / Interfaces / Structs + +| Concept | TypeScript | Java | Python | C# | +| ----------------- | -------------------------------------------------- | -------------------------------------------- | ------------------------------ | ------------------------------------------------- | +| **Inheritance** | `extends` (single) + `implements` (multiple) | `extends` (single) + `implements` (multiple) | `bases` (multiple) | `baseType` (single) + `interfaceTypes` (multiple) | +| **Member scope** | `TSMemberScope` | Via `JavaLexicalScope` | `PythonMemberScope` | `CSharpNamedTypeScope` / `CSharpClassScope` | +| **Member spaces** | static, instance, private-static, private-instance | static, instance | static, instance | Via `NamedTypeSymbol.members` | +| **Visibility** | `export`/`default` (module-level) | `public`/`protected`/`private` + modifiers | Convention (`_` prefix) | `public`/`protected`/`private`/`internal`/`file` | +| **Abstract** | N/A (use interfaces) | `abstract` modifier | `@abstractmethod` decorator | `abstract` modifier | +| **Static** | `static` keyword | `static` modifier | `@staticmethod`/`@classmethod` | `static` modifier | + +**Shared principle:** Class declarations create a type+value symbol, a member scope, and render the class syntax with members inside the scope. + +**What varies:** Inheritance model, visibility system, member space granularity, and abstract/static mechanisms. + +## 4.11 Visibility / Modifiers / Annotations / Decorators + +| Concept | TypeScript | Java | Python | C# | +| ---------------------- | ------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------- | +| **Access modifiers** | `export`/`default` | `public`/`protected`/`private`/`default` | Convention (`_` name prefix) | `public`/`protected`/`private`/`internal`/`file` | +| **Other modifiers** | `const`/`let`/`var`, `readonly` | `static`/`final`/`abstract`/`synchronized`/`native`/`volatile`/`transient`/`strictfp` | N/A | `static`/`sealed`/`abstract`/`virtual`/`override`/`extern`/`readonly` | +| **Annotations** | N/A | `Annotation` component | Decorators (inline `@`) | `Attribute` component | +| **Modifier component** | Inline keywords | `Modifiers` component | Inline decorators | `getAccessModifier()` + `makeModifiers()` utilities | + +**Shared principle:** Modifiers are rendered as keyword prefixes. Annotations/attributes/decorators have dedicated components or inline rendering. + +**What varies:** Every language has its own modifier vocabulary. The implementation ranges from boolean props (Java) to dedicated modifier components (C#) to inline text (Python). + +## 4.12 Comments / Doc Comments + +| Concept | TypeScript | Java | Python | C# | +| ----------------- | ----------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------- | ---------------------------------- | +| **Doc format** | JSDoc (`/** ... */`) | JSDoc (same) | Docstrings (`""" ... """`) | XML doc comments (`/// `) | +| **Doc component** | `JSDoc`, `JSDocComment`, `JSDocParam`, `JSDocExample` | N/A (uses TS-style JSDoc) | `PyDoc`, `FunctionDoc`, `ClassDoc`, `ModuleDoc`, `PropertyDoc` + 6 more | `DocComment`, `FromMarkdown` | +| **Line comments** | `SingleLineCommentBlock` (`//`) | N/A (inline) | `SimpleCommentBlock` (`#`) | Region (`#region`) | +| **Doc on decl** | `doc` prop | `doc` prop (limited) | `doc` prop | `doc` prop | + +**Shared principle:** Declarations accept a `doc` prop that renders language-appropriate documentation. + +**What varies:** Doc format (JSDoc vs docstrings vs XML), complexity (Python has the most elaborate doc system), and comment syntax. + +## 4.13 Formatting / Whitespace / Delimiters + +| Concept | TypeScript | Java | Python | C# | +| ------------------------- | --------------- | --------------- | ------------------------ | --------------- | +| **Block delimiters** | `{ ... }` | `{ ... }` | `:` + indentation | `{ ... }` | +| **Statement terminator** | `;` | `;` | Newline | `;` | +| **Block component** | Core `Block` | Core `Block` | `PythonBlock` (custom) | Core `Block` | +| **Default print width** | 80 | 80 | 80 | 120 | +| **Default tab width** | 2 | 2 | 4 | 4 | +| **Statement list joiner** | `\n` (hardline) | `\n` (hardline) | `\n\n` (double hardline) | `\n` (hardline) | + +**Shared principle:** All packages use core's formatting primitives. Brace-delimited languages use core's `Block`; Python defines a custom block for colon+indent. + +**What varies:** Python is the only whitespace-significant language, requiring a custom block component. C# uses wider formatting defaults. + +## 4.14 Test Strategy + +| Concept | TypeScript | Java | Python | C# | +| -------------------- | ------------------------ | --------------------------------------- | ------------------------ | ------------------------ | +| **Test count** | 37 files | 13 files | 26 files | ~33 files | +| **Test location** | `test/` | `test/` | `test/` | `src/` (colocated) | +| **Test utility** | `test/utils.tsx` | `test/utils.tsx` | `test/utils.tsx` | `test/utils.tsx` (some) | +| **Key helper** | `toSourceText()` | `testRender()` + `assertFileContents()` | `toSourceText()` | `TestNamespace` wrapper | +| **Multi-file tests** | Yes (imports, barrels) | Yes (packages, Maven) | Yes (modules, imports) | Yes (namespaces, usings) | +| **Setup import** | `@alloy-js/core/testing` | `@alloy-js/core/testing` | `@alloy-js/core/testing` | `@alloy-js/core/testing` | + +**Shared principle:** All packages use Vitest, import `@alloy-js/core/testing`, and use `toRenderTo()` / `d` template tag. Each has a `toSourceText()` or equivalent helper that wraps content in the appropriate language context. + +--- + +# 5. Reusable Patterns + +These patterns are consistently used across all four packages and should be adopted by any new language package: + +## 5.1 Symbol Subclass Pattern + +Every package creates a single `OutputSymbol` class extending `OutputSymbol`. Language-specific properties are added (e.g., `export`, `accessibility`, `module`). The class defines `static readonly memberSpaces`. + +## 5.2 Scope Hierarchy Pattern + +Every package defines 2–7 scope classes extending `OutputScope`. The hierarchy typically mirrors the language's nesting model: package/module → file → class/function → block. Each scope declares `static readonly declarationSpaces`. + +## 5.3 Factory Function Pattern + +Symbol creation is always done via factory functions (not direct construction). These functions: + +1. Get the current scope via `useScope()` or a typed variant. +2. Apply name policy via the binder's `createSymbol()`. +3. Return the created symbol. + +## 5.4 SourceFile-Creates-ModuleScope Pattern + +Every package's `SourceFile` component creates a language-specific scope (module/file scope) that tracks imports. The scope's `addImport()` method is called automatically when symbols from other modules are referenced. + +## 5.5 Reference-Triggers-Import Pattern + +The `Reference` component resolves a refkey, emits the symbol via `emitSymbol()`, and the module scope's import tracking records the needed import. The `SourceFile` then renders all accumulated imports at the top of the file. + +## 5.6 Name Policy Pattern + +Every package defines a `createNamePolicy()` function using core's `createNamePolicy()`. It maps element type strings to naming transformations. Reserved words are handled with suffix `_` or prefix `_`. + +## 5.7 External Dependency Descriptor Pattern + +Every package provides a `create()` factory (e.g., `createPackage`, `createModule`, `createLibrary`) that takes a descriptor object mapping paths/names to exported symbols. This creates lazy refkeys that resolve to symbols on demand. + +## 5.8 Builtin Library Pattern + +Every package pre-defines descriptors for common standard library types (Node.js stdlib, java.util, Python abc/enum/dataclasses, System namespace). These are created using the package's own `create()` factory. + +## 5.9 STC Wrapper Pattern + +Every package provides an `stc/index.ts` that wraps key components with `stc()` for fluent API usage from external consumers. + +## 5.10 Test Utility Pattern + +Every package defines a `test/utils.tsx` with: + +- `toSourceText(children, options?, path?)` — wraps in Output + SourceFile context and renders. +- Optionally: `findFile()`, `assertFileContents()`, `testRender()`. + +## 5.11 Declaration Component Pattern + +Every declaration component (class, function, interface, etc.) follows: + +1. Accept `name`, `refkey`, `doc`, language-specific modifier props. +2. Call a symbol factory to create a symbol. +3. Wrap children in a `Declaration` + scope. +4. Render the language syntax using `code` template and core intrinsics. + +## 5.12 Modifier Props Pattern + +Languages with visibility modifiers (Java, C#) use boolean props (`public`, `private`, `abstract`, etc.) on declaration components. A utility function collects the true props and renders them as a prefix string. + +--- + +# 6. Divergent or Language-Specific Patterns + +These patterns vary significantly and should **not** be over-generalized when designing Rust: + +## 6.1 Declaration Space Strategy + +- **Dual namespace** (TS, Go): `["values", "types"]` — same name can exist as both type and value. +- **Single namespace** (Python, Java): `["symbols"]` — one name, one thing. +- **Mixed** (C#): Method scopes have `["local-variables", "parameters", "type-parameters"]`; named type scopes delegate to member spaces. + +**Implication for Rust:** Rust has separate namespaces for types, values, and macros. This will likely require a multi-space strategy, possibly `["types", "values", "macros"]`. + +## 6.2 Member Space Strategy + +- **TS**: 4 spaces (`static`, `instance`, `private-static`, `private-instance`). +- **Python/Java**: 2 spaces (`static`, `instance`). +- **C#**: Uses a single `"members"` space on `NamedTypeSymbol` instead of multiple spaces. + +**Implication for Rust:** Rust structs have fields (not static/instance). Traits have methods. Enums have variants. The member space strategy needs to match Rust's data model. + +## 6.3 Import Mechanics + +- **TS**: Relative path imports, `import { X } from "./path.js"`. +- **Java**: Package-qualified imports, `import pkg.Class;`. +- **Python**: Module imports with `TYPE_CHECKING` blocks for type-only. +- **C#**: Namespace-based `using` directives. + +**Implication for Rust:** Rust uses `use crate::module::Symbol;` with `pub use` for re-exports, `use super::` for parent module, and crate-level `use` for external crates. + +## 6.4 Visibility Model + +- **TS**: Module-level `export`/`default` only. No class-level visibility. +- **Java**: `public`/`protected`/`private`/`default` on every declaration. +- **Python**: Convention (`_` prefix for private). +- **C#**: `public`/`protected`/`private`/`internal`/`file` with rich modifier combinations. + +**Implication for Rust:** Rust uses `pub`, `pub(crate)`, `pub(super)`, `pub(in path)`, and default private. This is path-scoped visibility, different from all existing packages. + +## 6.5 Whitespace-Significant Syntax + +Python is the only package that defines a custom `PythonBlock` for colon+indent blocks. All others use core's `Block` for `{ ... }`. + +**Implication for Rust:** Rust uses `{ ... }` blocks, so core's `Block` should work directly. + +## 6.6 Documentation Format + +Each language has a unique doc format. Python's is the most complex (~24KB PyDoc file with 12+ sub-components). TS uses JSDoc. Java reuses JSDoc. C# uses XML doc comments. + +**Implication for Rust:** Rust uses `///` doc comments with markdown content. This is closer to C#'s approach (line-based doc comments) but with markdown instead of XML. + +## 6.7 Build System Integration + +- **TS**: `package.json` + `tsconfig.json` generation. +- **Java**: `pom.xml` (Maven) with full dependency/plugin/resource configuration. +- **C#**: `.csproj` XML file. +- **Python**: None (no build file). + +**Implication for Rust:** Rust uses `Cargo.toml`. A `CargoTomlFile` component would be needed. + +## 6.8 Scope Complexity + +- **Java**: 3 scopes (Project → Package → Lexical). +- **Python**: 3 scopes (Module → Lexical → Member). +- **TS**: 4 scopes (Package → Module → Lexical → Member). +- **C#**: 7+ scopes (Namespace → SourceFile → Class → Method → Lexical → Named Type → Member). + +**Implication for Rust:** Rust's scope model includes crate → module → function → block, plus impl blocks and trait definitions. Moderate complexity. + +--- + +# 7. Implied Minimum Viable Surface for a New Language Package + +Based on the existing packages, a new language package should provide at minimum: + +## 7.1 Symbol System + +- [ ] `OutputSymbol` class extending `OutputSymbol` +- [ ] Define `static readonly memberSpaces` appropriate to language +- [ ] Implement `copy()` method for symbol cloning +- [ ] Language-specific properties on the symbol class + +## 7.2 Scope Hierarchy + +- [ ] At least 2 scope classes (module/file scope + lexical/block scope) +- [ ] Define `static readonly declarationSpaces` on each scope +- [ ] Scope that tracks imports (typically the module/file scope) +- [ ] Member scope for type declarations + +## 7.3 Name Policy + +- [ ] `createNamePolicy()` function +- [ ] Element type enumeration covering all declaration kinds +- [ ] Reserved word list with conflict resolution + +## 7.4 Core Components + +- [ ] `SourceFile` — wraps core SourceFile, creates module scope, renders imports +- [ ] `Declaration` — base declaration wrapper +- [ ] `Reference` — resolves refkey, emits symbol, renders name +- [ ] `ImportStatement` — renders language-specific import syntax + +## 7.5 Declaration Components + +- [ ] At least: function, struct/class, enum, variable/constant +- [ ] Each must: accept name+refkey, create symbol, create scope, render syntax + +## 7.6 Type System + +- [ ] Generic/type parameter support (if language has generics) +- [ ] Type reference context (if language distinguishes type vs value imports) + +## 7.7 External Dependencies + +- [ ] `create()` factory for describing external libraries +- [ ] At least one builtin descriptor (standard library) + +## 7.8 Modifiers/Visibility + +- [ ] Visibility modifier rendering (if applicable) +- [ ] Other modifier support (async, static, etc.) + +## 7.9 Documentation + +- [ ] Doc comment component +- [ ] `doc` prop on declaration components + +## 7.10 Root Structure + +- [ ] Root directory/package component (creates top-level scope) +- [ ] Build file generation (if applicable) + +## 7.11 Test Infrastructure + +- [ ] `test/utils.tsx` with `toSourceText()` +- [ ] Tests for each component +- [ ] Cross-file reference tests + +## 7.12 STC Wrappers + +- [ ] `stc/index.ts` wrapping key components + +--- + +# 8. Testing Patterns Across Language Packages + +## 8.1 Common Test Infrastructure + +All packages use: + +- **Vitest** as the test framework. +- **`@alloy-js/core/testing`** imported in setup for custom matchers. +- **`d\`...\``** template tag for dedenting expected output. +- **`toRenderTo(expected)`** matcher for comparing rendered output. + +## 8.2 Test Utility File + +Every package defines `test/utils.tsx` (or equivalent) with: + +```tsx +// Pattern: wrap content in language context and render to string +export function toSourceText(children: Children, options?: {...}): string { + return renderToString( + + + + {children} + + + + ); +} +``` + +Variants include: + +- `toSourceTextMultiple()` — for multi-file tests +- `testRender()` — returns the full output tree for manual inspection +- `findFile(output, path)` — extracts a specific file +- `assertFileContents(output, expected)` — validates multiple files at once + +## 8.3 Test Organization + +Tests are organized by feature, typically one file per component or concept: + +| Test Category | What It Tests | Example File | +| ------------------------- | ----------------------------------------------- | ------------------------------------------------- | +| **Basic declarations** | Simple rendering of each declaration type | `class.test.tsx`, `function-declaration.test.tsx` | +| **Modifiers** | Access modifiers, abstract, static, etc. | `class.test.tsx` (describe blocks) | +| **Cross-file references** | Import generation when referencing across files | `imports.test.tsx`, `reference.test.tsx` | +| **Name policy** | Naming convention transformations | `namepolicies.test.tsx` | +| **External dependencies** | External library descriptor usage | `externals.test.tsx` | +| **Values/literals** | Literal rendering | `values.test.tsx` | +| **Expressions** | Function calls, member access, etc. | `function-call-expression.test.tsx` | +| **Build files** | package.json, pom.xml, .csproj | `maven.test.tsx`, `barrel.test.tsx` | + +## 8.4 Test Patterns by Complexity + +**Level 1 — Single component, no symbols:** + +```tsx +it("renders basic function", () => { + const res = toSourceText(); + expect(res).toBe(d`fn foo() {}`); +}); +``` + +**Level 2 — Component with modifiers/options:** + +```tsx +it("renders public async function", () => { + const res = toSourceText( + , + ); + expect(res).toBe(d`pub async fn foo() {}`); +}); +``` + +**Level 3 — Cross-file references (multi-file):** + +```tsx +it("generates imports across files", () => { + const key = refkey(); + expect( + + + + + {code`let x = ${key}();`} + , + ).toRenderTo({ + "a.rs": d`pub fn helper() {}`, + "b.rs": d`use crate::a::helper;\n\nlet x = helper();`, + }); +}); +``` + +**Level 4 — External dependency with imports:** + +```tsx +it("uses external crate types", () => { + const serde = createCrate({ name: "serde", descriptor: { ... } }); + const res = toSourceText( + + ... + + ); + expect(res).toContain("use serde::Serialize;"); +}); +``` + +## 8.5 What a New Package Should Emulate + +1. **Create `test/utils.tsx`** with `toSourceText()` wrapping content in `Output` + `SourceFile`. +2. **Write one test file per component** (e.g., `struct.test.tsx`, `function.test.tsx`, `enum.test.tsx`). +3. **Test cross-file references** — this validates the entire import pipeline. +4. **Test name policy** — verify naming conventions are applied. +5. **Test modifiers** — verify visibility and other modifiers render correctly. +6. **Always import** `"@alloy-js/core/testing"` in setup or each test file. +7. **Use `d\`...\``** for all expected output. + +--- + +# 9. Gaps, Inconsistencies, and Risks + +## 9.1 Inconsistencies Between Packages + +1. **Test location**: C# colocates tests in `src/` next to components; all others use a separate `test/` directory. C# also doesn't have a unified test utility file. + +2. **Declaration space naming**: TS and Go use `"values"` and `"types"`; Python and Java use `"symbols"`; C# uses `"local-variables"`, `"parameters"`, `"type-parameters"`. There's no standard vocabulary. + +3. **Member space strategy**: TS uses 4 member spaces with visibility encoded in the space name. C# uses a single `"members"` space with visibility as a symbol property. Python/Java use 2 spaces. No clear "recommended" approach. + +4. **Import tracking mechanism**: TS and Python use scope-level `addImport()` methods. Java uses a `SourceFileContext.addImport()` callback. C# adds to a `usings` set on the source file scope. The mechanism works similarly but the API surface differs. + +5. **Expression/statement coverage**: TypeScript has the most complete set of expression and statement components. Java has almost none (relying on `code` template). Python is in between. C# added `IfStatement` and `VarDeclaration`. + +6. **Parameter representation**: TS and Python use `ParameterDescriptor[]` arrays. Java uses `Record` (name→type map). C# uses `ParameterProps[]`. No standard parameter interface. + +7. **STC wrapper completeness**: STC wrappers vary in how many components they expose. Some packages wrap nearly everything; others wrap only key components. + +## 9.2 Under-Documented Areas + +1. **Symbol `copy()` contract**: All packages implement it, but there's no documented contract for what must be preserved vs. what can differ. + +2. **SymbolCreator protocol**: The lazy symbol creation pattern used by external dependency descriptors is complex and varies between packages. No clear documentation. + +3. **Member resolver usage**: Only some packages use the binder's `MemberResolver` callback. The conditions under which it's needed are unclear. + +4. **Transient symbols**: Used in TS (e.g., `NewExpression` creates transient symbols with instantiated members) but not consistently documented. Other packages don't seem to use them. + +## 9.3 Risks for Rust + +1. **Rust's module system** is more complex than any existing package. Rust has crates, modules (which can be files or inline `mod { }`), `use` paths with `self`/`super`/`crate`, re-exports with `pub use`, and glob imports. This will require careful scope hierarchy design. + +2. **Rust's trait system** is unique — traits can be implemented on any type (including external types via extension traits). `impl Trait for Type` blocks need a component model that doesn't directly map to existing class/interface patterns. + +3. **Rust's lifetime and borrow system** has no precedent in existing packages. Lifetimes on type parameters, function signatures, and struct fields may need a dedicated representation. + +4. **Rust's macro system** (`macro_rules!`, proc macros, derive macros) has no analog in existing packages. Derive macros (like `#[derive(Debug, Clone)]`) map loosely to C#'s attributes/Java's annotations but with code generation semantics. + +5. **Rust's ownership model** means parameters have move/borrow/mutable-borrow semantics (`T`, `&T`, `&mut T`). This is richer than any existing parameter descriptor. + +--- + +# 10. Takeaways for Designing Rust + +1. **Follow the established package shape.** The directory structure, barrel exports, symbol/scope/component/context/builtins organization is proven across 4 languages. Don't deviate without reason. + +2. **Design the scope hierarchy to match Rust's module system.** Likely: `RustCrateScope` → `RustModuleScope` → `RustFunctionScope` → `RustLexicalScope`, plus `RustImplScope` and `RustTraitScope`. + +3. **Use multi-space declaration strategy.** Rust distinguishes types, values, and macros. Consider `declarationSpaces = ["types", "values", "macros"]` on module scopes. + +4. **Keep member spaces simple.** Rust structs have fields (no static/instance distinction). Impl blocks have methods. Consider a single `"members"` space (like C#) or `["fields", "methods"]`. + +5. **Model `use` statements carefully.** Rust's import system is path-based (`use crate::foo::Bar;`). The module scope needs an `addUse()` method that records use paths, and the `SourceFile` renders them. Study the TS and Python import implementations closely. + +6. **Handle visibility as a symbol property.** Rust's `pub`/`pub(crate)`/`pub(super)`/`pub(in path)` maps best to a visibility property on `RustOutputSymbol` (like C#'s `accessibility`), not separate member spaces (like TS). + +7. **Build a `Cargo.toml` component.** Following the pattern of `PackageJsonFile`, `PomFile`, and `CsProjFile`. Include `[dependencies]`, `[dev-dependencies]`, `[features]`, and `edition`. + +8. **Use `///` doc comments.** Rust's doc comments are markdown-based `///` lines. A `DocComment` component that wraps markdown content in `///` prefix lines is the right approach (simpler than Python's elaborate docstring system). + +9. **Start with core declarations.** Following the precedent of all packages, prioritize: `StructDeclaration`, `EnumDeclaration`, `FunctionDeclaration`, `TraitDeclaration`, `ImplBlock`, `ModuleDeclaration`, `TypeAlias`. + +10. **Derive macros as attributes.** `#[derive(Debug, Clone, Serialize)]` maps to C#'s `Attribute` component pattern. Create a `DeriveAttribute` component. + +11. **External crate descriptors via `createCrate()`.** Following `createPackage()`/`createModule()`/`createLibrary()`, create a `createCrate()` factory for describing external crate APIs (serde, tokio, etc.). + +12. **Test cross-module `use` generation first.** The import system is always the most complex part of a language package. Validate it early with multi-file tests. + +13. **Study Go and C# most closely.** Go (packages, no classes, dual namespaces) and C# (rich type system, namespaces, visibility modifiers, attributes) are the closest analogs for Rust's feature set. diff --git a/docs/language-packages/rust/03-rust-design-notes.md b/docs/language-packages/rust/03-rust-design-notes.md new file mode 100644 index 000000000..86193bce7 --- /dev/null +++ b/docs/language-packages/rust/03-rust-design-notes.md @@ -0,0 +1,973 @@ +# 1. Objective + +This document defines the implementation design for `@alloy-js/rust`, a new Alloy language package for generating Rust source code. + +**Purpose:** To bridge the gap between the abstract patterns documented in [`01-core-understanding.md`](./01-core-understanding.md) and [`02-existing-language-patterns.md`](./02-existing-language-patterns.md) and a concrete, buildable plan for Rust support. + +**How it feeds the PRD:** A subsequent planning agent will convert this design brief into a prioritized backlog of implementation tasks. This document provides the "what and how" — the PRD will add "in what order and to what acceptance criteria." + +**Constraints:** + +- This is a design brief, not a task backlog. +- Rust language semantics are stated precisely, not hand-waved. +- All Alloy architecture decisions are grounded in repository evidence. + +--- + +# 2. Target Language Constraints + +## 2.1 File / Module / Crate Model + +Rust's code organization is hierarchical with three levels: + +1. **Crate** — the top-level compilation unit. A crate is either a binary (`main.rs`) or a library (`lib.rs`). A crate corresponds to a Cargo package. +2. **Module** — a namespace within a crate. Modules can be: + - **File-based:** `src/foo.rs` defines module `foo`. Submodules go in `src/foo/` with a `mod.rs` or via `src/foo.rs` + `src/foo/bar.rs`. + - **Inline:** `mod foo { ... }` declares a module inside a file. + - Modules must be explicitly declared with `mod foo;` in the parent. +3. **Items** — declarations within a module: functions, structs, enums, traits, impls, type aliases, constants, statics, macros. + +**Key constraints for Alloy:** + +- Every `.rs` file is a module. The file path determines the module path. +- The crate root (`lib.rs` or `main.rs`) must declare all top-level modules with `mod` statements. +- Submodules must be declared by their parent module. +- Module declarations (`mod foo;`) and `use` statements are separate concerns. + +## 2.2 Import / Use Semantics + +Rust's `use` statement brings names into scope: + +```rust +use std::collections::HashMap; // Bring a single type +use std::io::{Read, Write}; // Bring multiple from same path +use crate::models::User; // From current crate +use super::utils::helper; // From parent module +use self::submod::Thing; // From child module +``` + +**Path roots:** + +- `crate::` — absolute path from crate root. +- `super::` — parent module. +- `self::` — current module. +- Bare name (e.g., `std::`) — external crate or prelude. + +**Re-exports:** `pub use crate::internal::Type;` makes `Type` available from the re-exporting module. + +**Glob imports:** `use std::collections::*;` (generally discouraged in generated code). + +**Key constraints for Alloy:** + +- Import paths are `::` separated, not `/` or `.`. +- Imports from the same crate use `crate::`, not the crate name. +- The import system must distinguish: same-module (no import needed), same-crate-different-module (`use crate::...`), external-crate (`use ::...`). +- Multiple imports from the same path should be grouped: `use std::io::{Read, Write};`. + +## 2.3 Declaration Forms + +Rust items (declarations) at module level: + +| Item | Syntax | Notes | +| ---------------- | ---------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| **Function** | `fn name(params) -> ReturnType { body }` | Can be `pub`, `async`, `const`, `unsafe`, `extern` | +| **Struct** | `struct Name { fields }` or `struct Name(types);` (tuple) or `struct Name;` (unit) | Can be `pub`, fields can have individual visibility | +| **Enum** | `enum Name { Variant1, Variant2(T), Variant3 { field: T } }` | Variants can be unit, tuple, or struct-like | +| **Trait** | `trait Name { methods/associated types }` | Can have default method implementations | +| **Impl block** | `impl Type { methods }` or `impl Trait for Type { methods }` | Not a declaration in the symbol sense — it adds methods to an existing type | +| **Type alias** | `type Name = OtherType;` | `pub type` for re-exports | +| **Constant** | `const NAME: Type = value;` | Must be compile-time evaluable | +| **Static** | `static NAME: Type = value;` | Global variable, can be `mut` | +| **Module** | `mod name;` or `mod name { ... }` | File-backed or inline | +| **Use** | `use path::to::item;` | Import statement | +| **Extern crate** | `extern crate name;` | Rarely needed in edition 2021+ | + +## 2.4 Expression and Statement Model + +Rust is expression-oriented. Most constructs are expressions: + +- `if/else` returns a value: `let x = if cond { a } else { b };` +- `match` returns a value: `let x = match val { ... };` +- Block expressions: `{ stmt; stmt; expr }` — last expression is the return value. +- `loop`, `while`, `for` are expressions (with `break value`). + +**Statements:** + +- `let` bindings: `let x: Type = value;` or `let mut x = value;` +- Expression statements: `expr;` (expression followed by semicolon) +- Item declarations (at statement position) + +**Key constraints for Alloy:** + +- Semicolons are significant: `expr` (expression, returns value) vs `expr;` (statement, discards value). +- The `let` keyword is always required for variable binding. +- Pattern matching is pervasive (`let (a, b) = tuple;`, `if let Some(x) = opt { ... }`). + +## 2.5 Type System Concerns + +**Generics:** `fn foo(x: T)`, `struct Foo { field: T }`. + +**Lifetimes:** `fn foo<'a>(x: &'a str) -> &'a str`, `struct Foo<'a> { s: &'a str }`. Lifetimes annotate borrow durations. They appear in: + +- Function signatures +- Struct/enum definitions +- Impl blocks +- Trait definitions +- Type aliases + +**Trait bounds:** `T: Display + Clone`, `where T: Display + Clone`. + +**Associated types:** `type Output;` in traits, `type Output = i32;` in impls. + +**Reference types:** `&T` (shared borrow), `&mut T` (mutable borrow), `Box`, `Rc`, `Arc`. + +**Key constraints for Alloy:** + +- Lifetimes are a first-class concern in signatures. A `ParameterDescriptor` must support `&'a T`, `&'a mut T`, etc. +- Generic parameters can be types (`T`) or lifetimes (`'a`). They share the `<>` syntax. +- Trait bounds appear both inline (`T: Bound`) and in `where` clauses. + +## 2.6 Comments / Doc Comments + +```rust +// Line comment +/* Block comment */ +/// Doc comment (applies to the following item) — rendered as markdown +//! Inner doc comment (applies to the enclosing item, e.g., module-level) +``` + +Doc comments support markdown, code examples (with ` ``` `), and attribute annotations like `# Examples`, `# Panics`, `# Safety`, `# Errors`. + +**Key constraints for Alloy:** + +- `///` is the standard doc comment prefix. Each line is a separate `///` line. +- `//!` is used at the top of files for module/crate documentation. +- Doc comments are line-based, not block-based (unlike JSDoc or Python docstrings). + +## 2.7 Formatting Conventions + +Rust has an official formatter: `rustfmt`. Conventions: + +- **Indent:** 4 spaces. +- **Line width:** 100 characters (rustfmt default). +- **Braces:** Opening brace on same line, closing brace on own line. +- **Trailing commas:** In multi-line struct fields, enum variants, function parameters. +- **Use statement ordering:** `std` → external crates → `crate::` → `self::` → `super::`. +- **Blank lines:** Between top-level items. + +## 2.8 Visibility / Modifier Rules + +| Visibility | Syntax | Meaning | +| ----------------- | -------------- | ------------------------------------------------------- | +| Private (default) | _(no keyword)_ | Visible only within the current module and its children | +| Public | `pub` | Visible everywhere | +| Crate-public | `pub(crate)` | Visible within the current crate | +| Super-public | `pub(super)` | Visible to the parent module | +| Path-restricted | `pub(in path)` | Visible within the specified module path | + +**Other modifiers:** + +- `async` — async function +- `unsafe` — unsafe block/function +- `const` — const function/context +- `extern "C"` — FFI linkage +- `mut` — mutable binding/reference + +**Key constraints for Alloy:** + +- Visibility is path-scoped, not just keyword-based. The `pub(in crate::foo)` form requires access to the module path. +- Default visibility is private. This is unlike Java/C# where the default varies by context. +- `pub` on struct fields is independent of `pub` on the struct itself. + +## 2.9 Special Constraints and Idioms + +**Derive macros:** `#[derive(Debug, Clone, PartialEq, Serialize)]` is extremely common. These are attributes that generate trait implementations at compile time. + +**Attributes:** `#[attr]` (outer) and `#![attr]` (inner). Used for: + +- `#[derive(...)]` — derive trait impls +- `#[cfg(...)]` — conditional compilation +- `#[allow(...)]`, `#[warn(...)]`, `#[deny(...)]` — lint control +- `#[serde(rename_all = "camelCase")]` — crate-specific attributes +- `#[test]`, `#[bench]` — test markers + +**Impl blocks are not declarations.** They are _extensions_ that add methods to an existing type. A struct can have multiple impl blocks, and trait impls are separate from inherent impls. This has no direct analog in any existing Alloy language package. + +**Pattern matching** (`match`, `if let`, `let...else`) is pervasive but complex. For MVP, basic `match` support is sufficient. + +**Error handling:** `Result` with `?` operator. Not a structural concern for code generation. + +**Closures:** `|x, y| x + y` or `move |x| { ... }`. Common in Rust but can be rendered with `code` template. + +--- + +# 3. Mapping Alloy Concepts to Rust + +## 3.1 OutputSymbol → RustOutputSymbol + +**Maps cleanly** with adaptations. + +Rust symbols need: + +- `visibility: RustVisibility` — `"pub" | "pub(crate)" | "pub(super)" | "pub(in )" | undefined` (private). Modeled as a property on the symbol (like C#'s `accessibility`), not as separate member spaces. +- `symbolKind: RustSymbolKind` — `"function" | "struct" | "enum" | "trait" | "type-alias" | "const" | "static" | "module" | "field" | "variant" | "method" | "associated-type" | "parameter" | "lifetime" | "type-parameter"`. +- `isAsync: boolean` — for async functions. +- `isUnsafe: boolean` — for unsafe functions/traits. +- `isConst: boolean` — for const functions/items. + +**Member spaces:** `["members"]` (single space, like C#). Rust has no static/instance distinction — all struct fields are the same kind, and methods are in impl blocks (not in the struct definition). + +**Inference:** The C# approach of a single `"members"` space with metadata properties is the best fit for Rust. + +## 3.2 OutputScope → RustScope Hierarchy + +**Needs adaptation.** Rust's scope model is: + +| Alloy Scope | Rust Concept | Declaration Spaces | Notes | +| ------------------- | --------------- | ------------------------------------------------------ | ----------------------------------------------------------- | +| `RustCrateScope` | Crate root | `["types", "values"]` | Top-level scope. Tracks `mod` declarations. | +| `RustModuleScope` | Module (file) | `["types", "values"]` | Tracks `use` imports. Creates `mod` declarations in parent. | +| `RustImplScope` | `impl` block | — (member scope) | Owner is the target type symbol. | +| `RustTraitScope` | `trait` body | — (member scope) | Owner is the trait symbol. | +| `RustFunctionScope` | Function body | `["parameters", "type-parameters", "local-variables"]` | Parameters include lifetimes. | +| `RustLexicalScope` | Block `{ ... }` | `["local-variables"]` | For inner blocks. | + +**Key design decisions:** + +1. **`RustModuleScope` must track `use` imports** (like Go's `GoSourceFileScope.imports` and TS's `TSModuleScope.importedSymbols`). An `addUse(targetSymbol, targetPackage)` method records needed imports. + +2. **`RustCrateScope` must track `mod` declarations** — the crate root needs to know which modules exist to generate `mod foo;` statements. This is unique to Rust (Go packages don't need explicit module declarations). + +3. **Dual declaration spaces** on module/crate scopes: `["types", "values"]`, matching Go's pattern. Macros are deferred beyond MVP — they rarely conflict with type/value names in code generation contexts and can be added to a `"macros"` space later if needed. + +**Inference:** The Go package's approach (`GoModuleScope` → `GoPackageScope` → `GoSourceFileScope`) is the closest analog but needs the extra `mod` declaration tracking. + +## 3.3 Binder / Reference Resolution + +**Maps cleanly** with adaptations. + +The `Reference` component must: + +1. Resolve the refkey via the binder. +2. Determine the relationship: same-module, same-crate-different-module, external-crate. +3. If different module: add a `use` entry to the current module scope. +4. Render the symbol name. + +**Use path construction:** Given a `ResolutionResult`, the reference component needs to: + +- Walk `pathDown` to build the module path from the common scope to the declaration. +- Prefix with `crate::` for same-crate references, or the crate name for external crates. +- Handle `super::` for parent module references (optimization over `crate::full::path`). + +**Grouping:** Multiple `use` items from the same path should be grouped: `use std::io::{Read, Write};`. This is a rendering concern handled by the `UseStatement` component. + +**Evidence:** Go's `ref()` function in `packages/go/src/symbols/reference.ts` follows a very similar pattern — resolve, check export visibility, add import, build qualified name. Rust's will differ in path syntax (`::` vs `.`) and visibility rules (`pub` vs uppercase first letter). + +## 3.4 NamePolicy + +**Maps cleanly.** + +Rust naming conventions (enforced by `clippy` lints): + +| Element | Convention | Example | +| --------------------------------- | ------------------------------------ | -------------------- | +| `type`, `struct`, `enum`, `trait` | `PascalCase` (UpperCamelCase) | `MyStruct` | +| `function`, `method` | `snake_case` | `my_function` | +| `variable`, `parameter` | `snake_case` | `my_var` | +| `constant`, `static` | `SCREAMING_SNAKE_CASE` | `MAX_SIZE` | +| `module`, `crate` | `snake_case` | `my_module` | +| `lifetime` | `'lowercase` | `'a`, `'static` | +| `type-parameter` | `PascalCase` (usually single letter) | `T`, `E`, `Item` | +| `enum-variant` | `PascalCase` | `Some`, `None`, `Ok` | +| `field` | `snake_case` | `my_field` | + +**Reserved words:** `as`, `async`, `await`, `break`, `const`, `continue`, `crate`, `dyn`, `else`, `enum`, `extern`, `false`, `fn`, `for`, `if`, `impl`, `in`, `let`, `loop`, `match`, `mod`, `move`, `mut`, `pub`, `ref`, `return`, `self`, `Self`, `static`, `struct`, `super`, `trait`, `true`, `type`, `unsafe`, `use`, `where`, `while`, `yield`. (37 keywords) + +Raw identifier syntax (`r#keyword`) is Rust's escape for reserved words — e.g., `r#type` to use `type` as an identifier. + +## 3.5 Formatting Intrinsics + +**Maps cleanly.** Rust uses `{ ... }` blocks like TypeScript, Java, and C#. Core's `Block` component works directly. Core's intrinsic elements (``, ``, ``) provide all needed formatting. + +**Format options:** + +- `printWidth: 100` (rustfmt default) +- `tabWidth: 4` (Rust convention) +- `useTabs: false` + +## 3.6 Impl Blocks + +**Needs a package-specific abstraction.** No existing Alloy language package has a concept analogous to Rust's `impl` blocks. + +An `ImplBlock` component would: + +1. Accept a target type (by refkey or inline) and optional trait. +2. Create a member scope on the target type symbol. +3. Render `impl [Trait for] Type { ... }`. +4. Children are methods, associated types, etc. + +**Key difference from classes:** In Java/TS/C#, methods are declared _inside_ the class declaration. In Rust, methods are declared in _separate_ impl blocks. A struct can have multiple impl blocks. + +**Proposed component:** + +```tsx + + + {code`Self { field: 0 }`} + + + + + + {code`write!(f, "...")`} + + +``` + +**Self receiver design decision (resolved):** Methods inside `ImplBlock` or `TraitDeclaration` default to `receiver="&self"`. Standalone functions default to `receiver="none"`. The `receiver` prop accepts `"&self" | "&mut self" | "self" | "none"` and overrides the default in any context. `receiver="none"` inside an impl block creates an associated function (e.g., `fn new()`). + +## 3.7 Traits + +**Needs adaptation.** Traits are closest to TypeScript interfaces / C# interfaces, but with default implementations and associated types. + +A `TraitDeclaration` component would: + +1. Create a type symbol. +2. Create a member scope (for method signatures and associated types). +3. Render `trait Name { ... }`. +4. Support `where` clauses for bound constraints. + +## 3.8 Enums + +**Needs adaptation.** Rust enums are algebraic data types (sum types), far richer than C#/Java/TS enums: + +```rust +enum Shape { + Circle(f64), // Tuple variant + Rectangle { width: f64, height: f64 }, // Struct variant + Point, // Unit variant +} +``` + +An `EnumDeclaration` component needs variant sub-components: + +- `EnumVariant` — unit variant +- `EnumVariant` with `fields` — tuple or struct variant + +## 3.9 Attributes / Derive + +**Maps to C#'s Attribute pattern** with Rust-specific syntax. + +```rust +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Config { ... } +``` + +An `Attribute` component renders `#[name(args)]`. A `DeriveAttribute` convenience component renders `#[derive(Trait1, Trait2, ...)]`. + +## 3.10 External Crate Descriptors + +**Maps to the existing `createPackage`/`createModule`/`createLibrary` pattern.** + +A `createCrate()` factory would describe external crate APIs using the `REFKEYABLE` + `TO_SYMBOL` protocol with per-binder caching via `WeakMap` (the same pattern used by Go's `createModule()` and C#'s `createLibrary()`): + +```typescript +const serde = createCrate({ + name: "serde", + version: "1.0", + descriptor: { + ".": { named: ["Serialize", "Deserialize", "Serializer", "Deserializer"] }, + json: { named: ["to_string", "from_str", "Value"] }, + }, +}); +``` + +The descriptor maps module paths within the crate to exported symbols. When referenced, the system adds `use serde::Serialize;` and records the crate dependency for `Cargo.toml`. + +## 3.11 Build File (Cargo.toml) + +**Maps to the `PackageJsonFile`/`PomFile`/`CsProjFile` pattern.** + +A `CargoTomlFile` component generates the TOML manifest: + +```toml +[package] +name = "my-crate" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +tokio = { version = "1", features = ["full"] } + +[dev-dependencies] +# ... +``` + +The crate scope tracks dependencies (added when external crate symbols are referenced) and renders them in `Cargo.toml`. + +## 3.12 Lifetimes + +**No precedent in existing packages.** Lifetimes are a Rust-specific concept. + +For MVP, lifetimes can be handled as: + +- String parameters in generic parameter lists: `<'a, T: 'a>`. +- Inline strings in type annotations: `&'a str`. + +A dedicated `Lifetime` component or descriptor is deferred beyond MVP. The `code` template tag can render lifetime annotations inline. + +## 3.13 Mod Declarations + +**Unique to Rust.** When a crate has file-based modules, the crate root (or parent module) must declare them: + +```rust +// In lib.rs +mod models; +mod utils; +pub mod api; +``` + +The `SourceFile` component for `lib.rs` / `main.rs` must auto-generate `mod` statements for all child modules. This is similar to TypeScript's barrel files but mandatory. + +**Design:** The `RustCrateScope` (or `RustModuleScope` for submodules) tracks child modules and renders `mod` declarations automatically. + +## 3.14 Prelude Types + +Rust's standard prelude automatically imports certain types into every module. References to these types should **not** generate `use` statements. + +**Prelude type list (Rust 2021 edition):** +`bool`, `char`, `f32`, `f64`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`, `u64`, `u128`, `usize`, `str`, `Option`, `Some`, `None`, `Result`, `Ok`, `Err`, `Vec`, `String`, `ToString`, `Box`, `Clone`, `Copy`, `Default`, `Drop`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`, `Iterator`, `IntoIterator`, `ExactSizeIterator`, `DoubleEndedIterator`, `From`, `Into`, `TryFrom`, `TryInto`, `AsRef`, `AsMut`, `Send`, `Sync`, `Sized`, `Unpin`, `ToOwned`, `Fn`, `FnMut`, `FnOnce`. + +This list is maintained as a `PRELUDE_TYPES: Set` constant in the reference resolution module. When a symbol's name matches a prelude type, no `use` statement is generated. + +## 3.15 Name Conflict Resolver + +Rust needs a custom name conflict resolver (not core's default). When imported symbols (`use` aliases) conflict with local declarations, the imported symbol should be renamed — not the local declaration. This follows TypeScript's pattern where `LocalImportSymbol` flagged symbols are renamed first. + +The resolver should: + +1. Keep local declarations unchanged. +2. Rename `use`-imported symbols with `_2`, `_3` suffixes on conflict. +3. Treat symbols created as local `use` aliases as import symbols (e.g., alias symbols from `addUse`), so deconfliction targets imports before declarations. + +--- + +# 4. Proposed Package Shape + +## 4.1 Directory Layout + +``` +packages/rust/ +├── src/ +│ ├── index.ts # Barrel export +│ ├── name-policy.ts # Rust naming conventions +│ ├── name-conflict-resolver.ts # Custom name conflict handling +│ ├── create-crate.ts # External crate descriptor factory +│ ├── parameter-descriptor.ts # Parameter metadata (name, type, ref, mut, lifetime) +│ ├── utils.ts # Utility helpers +│ ├── symbols/ +│ │ ├── index.ts # Symbol barrel +│ │ ├── rust-output-symbol.ts # RustOutputSymbol class +│ │ ├── named-type-symbol.ts # NamedTypeSymbol (struct, enum, trait) +│ │ ├── function-symbol.ts # FunctionSymbol (fn, method) +│ │ ├── factories.ts # Symbol creation functions +│ │ └── reference.tsx # Reference resolution + use generation +│ ├── scopes/ +│ │ ├── index.ts # Scope barrel + type alias + hooks +│ │ ├── rust-crate-scope.ts # RustCrateScope (mod tracking, deps) +│ │ ├── rust-module-scope.ts # RustModuleScope (use tracking) +│ │ ├── rust-function-scope.ts # RustFunctionScope +│ │ ├── rust-lexical-scope.ts # RustLexicalScope +│ │ ├── rust-impl-scope.ts # RustImplScope (member scope) +│ │ └── rust-trait-scope.ts # RustTraitScope (member scope) +│ ├── components/ +│ │ ├── index.ts # Component barrel +│ │ ├── stc/index.ts # STC wrappers +│ │ ├── source-file.tsx # Rust source file (.rs) +│ │ ├── crate-directory.tsx # Crate root (creates CrateScope, Cargo.toml) +│ │ ├── module-directory.tsx # Module directory (submodules) +│ │ ├── cargo-toml-file.tsx # Cargo.toml generation +│ │ ├── declaration.tsx # Base declaration component +│ │ ├── reference.tsx # Symbol reference rendering +│ │ ├── use-statement.tsx # use path::to::item; rendering +│ │ ├── struct-declaration.tsx # struct Name { fields } +│ │ ├── enum-declaration.tsx # enum Name { variants } +│ │ ├── function-declaration.tsx # fn name(params) -> Type { body } +│ │ ├── trait-declaration.tsx # trait Name { ... } +│ │ ├── impl-block.tsx # impl [Trait for] Type { ... } +│ │ ├── type-alias.tsx # type Name = Type; +│ │ ├── const-declaration.tsx # const NAME: Type = value; +│ │ ├── mod-declaration.tsx # mod name; or mod name { ... } +│ │ ├── attribute.tsx # #[attr(args)] +│ │ ├── doc-comment.tsx # /// doc comment +│ │ ├── parameters.tsx # (param: Type, ...) rendering +│ │ ├── type-parameters.tsx # <'a, T: Bound> rendering +│ │ ├── where-clause.tsx # where T: Display + Clone +│ │ └── value.tsx # Literal rendering +│ ├── context/ +│ │ ├── index.ts +│ │ └── crate-context.tsx # Crate metadata context +│ └── builtins/ +│ ├── index.ts +│ └── std.ts # std crate descriptor (Option, Result, Vec, String, etc.) +├── test/ +│ ├── utils.tsx # toSourceText(), findFile(), etc. +│ ├── vitest.setup.ts +│ ├── struct.test.tsx +│ ├── enum.test.tsx +│ ├── function.test.tsx +│ ├── trait.test.tsx +│ ├── impl.test.tsx +│ ├── imports.test.tsx +│ ├── reference.test.tsx +│ ├── name-policy.test.tsx +│ ├── attributes.test.tsx +│ ├── cargo-toml.test.tsx +│ └── module-structure.test.tsx +├── package.json +├── tsconfig.json +└── vitest.config.ts +``` + +## 4.2 Public API Shape + +```typescript +// packages/rust/src/index.ts +export * from "./builtins/index.js"; +export * from "./components/index.js"; +export * from "./context/index.js"; +export * from "./create-crate.js"; +export * from "./name-policy.js"; +export * from "./parameter-descriptor.js"; +export * from "./symbols/index.js"; +export * from "./utils.js"; +``` + +**Consumer usage:** + +```tsx +import * as rust from "@alloy-js/rust"; + + + + + + + + + + +; +``` + +## 4.3 Major Internal Areas of Responsibility + +1. **Symbol system** (`symbols/`): `RustOutputSymbol`, `NamedTypeSymbol`, `FunctionSymbol`, scope hierarchy, factory functions. +2. **Reference resolution** (`symbols/reference.tsx`): Resolves refkeys, determines use paths, tracks imports. +3. **Components** (`components/`): Declaration, expression, and structural components. +4. **Use statement generation** (`components/UseStatement.tsx`): Renders `use` statements with grouping and tree syntax. +5. **Module structure** (`components/CrateDirectory.tsx`, `ModuleDirectory.tsx`, `SourceFile.tsx`): File/module hierarchy with automatic `mod` declarations. +6. **Build file** (`components/CargoTomlFile.tsx`): `Cargo.toml` generation with dependency tracking. +7. **External crate descriptors** (`create-crate.ts`): Factory for describing external crate APIs. + +## 4.4 Relationship to Alloy Core + +The package depends on `@alloy-js/core` and uses: + +- `OutputSymbol`, `OutputScope` (subclassing) +- `createSymbol()`, `createScope()` (factories) +- `createNamePolicy()` (naming) +- `createContext()`, `useContext()` (context system) +- `Binder`, `resolve()` (reference resolution) +- `refkey()`, `namekey()`, `memberRefkey()` (identity) +- `code`, `text` (template tags) +- `stc()` (component wrappers) +- `mapJoin()`, `join()` (list utilities) +- `Output`, `SourceFile`, `SourceDirectory`, `Declaration`, `Scope`, `Block`, `Indent` (core components) +- Intrinsic elements (``, ``, ``, etc.) + +--- + +# 5. MVP Scope + +The MVP should enable generation of a complete, compilable Rust crate with basic types, functions, traits, and imports. + +## 5.1 In Scope + +### File / Module Generation + +- `CrateDirectory` — creates crate root with `Cargo.toml`. +- `SourceFile` — creates `.rs` files with module scope. +- `ModuleDirectory` — creates subdirectory-based modules. +- Automatic `mod` declaration generation in parent modules. +- `CargoTomlFile` — generates `Cargo.toml` with metadata and dependencies. + +### Imports / Use Statements + +- Automatic `use` statement generation when referencing cross-module symbols. +- `use crate::path::Item;` for same-crate references. +- `use ::Item;` for external crate references. +- `use path::{Item1, Item2};` grouping for multiple imports from same path. +- Import sorting: `std` → external → `crate::`. + +### Identifiers and References + +- `Reference` component resolving refkeys to symbol names. +- Name policy with `snake_case`, `PascalCase`, `SCREAMING_SNAKE_CASE` transformations. +- Reserved word handling with `r#` prefix. +- `RustOutputSymbol` with visibility property. + +### Declarations + +- `StructDeclaration` — named structs with fields (field-level visibility). +- `EnumDeclaration` — enums with unit, tuple, and struct variants. +- `FunctionDeclaration` — functions with parameters, return type, async, pub. +- `TraitDeclaration` — traits with method signatures and default implementations. +- `ImplBlock` — inherent impl and trait impl blocks. +- `TypeAlias` — `type Name = Type;`. +- `ConstDeclaration` — `const NAME: Type = value;`. + +### Type System (Basic) + +- `TypeParameters` — `` with basic bounds (`T: Display`). +- `WhereClause` — `where T: Display + Clone`. +- Inline type annotations via `code` template. + +### Attributes + +- `Attribute` — `#[name(args)]`. +- `DeriveAttribute` — `#[derive(Trait1, Trait2)]`. + +### Comments + +- `DocComment` — `///` doc comments. +- `ModuleDocComment` — `//!` inner doc comments (for file headers). + +### Formatting + +- 4-space indent, 100-char width. +- Trailing commas in multi-line contexts. +- Blank lines between top-level items. + +### External Dependencies + +- `createCrate()` factory for external crate descriptors. +- `std` builtin descriptor covering: `Option`, `Result`, `Vec`, `String`, `Box`, `HashMap`, `HashSet`, `io::{Read, Write}`, `fmt::Display`, `fmt::Debug`, `clone::Clone`, `default::Default`. + +### Tests + +- `test/utils.tsx` with `toSourceText()`. +- Tests for each declaration component. +- Cross-module reference tests. +- Name policy tests. +- Cargo.toml tests. + +## 5.2 What MVP Produces + +A compilable Rust crate with: + +- Multiple modules in a directory hierarchy. +- Structs, enums, traits, and functions with correct syntax. +- Automatic `use` statements for cross-module and cross-crate references. +- `#[derive(...)]` attributes. +- `Cargo.toml` with dependencies. +- Doc comments. +- Correct Rust naming conventions. + +--- + +# 6. Deferred / Out-of-Scope Features + +| Feature | Why Deferred | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------- | +| **Lifetimes** (`'a` annotations) | Complex, Rust-specific. Can be rendered inline with `code` template for now. | +| **Pattern matching** (`match` expressions with complex patterns) | Rich expression syntax. Basic `match` can be inline. | +| **Closures** (`\|x\| expr`) | Can be rendered inline with `code` template. | +| **Async/await expressions** | `async fn` is in MVP; complex async expressions (`async { }`, `.await`) are deferred. | +| **Unsafe blocks/functions** | `unsafe fn` modifier is in MVP; full unsafe block semantics are deferred. | +| **Macro definitions** (`macro_rules!`) | Complex, rarely needed in code generation. | +| **Proc macro crates** | Separate crate type, advanced use case. | +| **Tuple structs** (`struct Foo(T1, T2)`) | Less common. Named structs are MVP. | +| **Unit structs** (`struct Foo;`) | Rare in code generation. | +| **Static declarations** | Less common than `const`. | +| **`pub(in path)` visibility** | Rare. `pub`, `pub(crate)`, and private cover most cases. | +| **Re-exports** (`pub use`) | Advanced module organization. | +| **Glob imports** (`use path::*`) | Generally discouraged. | +| **Conditional compilation** (`#[cfg(...)]`) | Advanced feature. | +| **Feature flags** in `Cargo.toml` | Advanced dependency management. | +| **Workspace support** | Multi-crate projects. | +| **`impl Trait` in function signatures** | Can be rendered inline. | +| **`dyn Trait` / trait objects** | Can be rendered inline. | +| **Associated types** in traits | Can be rendered inline for now. | +| **Complex `where` clauses** | Basic `where` is MVP; higher-ranked trait bounds are deferred. | +| **If/else, loop, for, while expressions** | Imperative control flow. Can use `code` template. | +| **Let bindings with patterns** | Can use `code` template. | + +--- + +# 7. Golden Scenarios + +## 7.1 Basic Struct with Impl + +**Input (Alloy JSX):** + +```tsx + + + + + + + + + + {code`Self { x, y }`} + + + + {code`((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt()`} + + + + +``` + +**Expected output (`lib.rs`):** + +```rust +#[derive(Debug, Clone)] +pub struct Point { + pub x: f64, + pub y: f64, +} + +impl Point { + pub fn new(x: f64, y: f64) -> Self { + Self { x, y } + } + + fn distance(&self, other: &Point) -> f64 { + ((self.x - other.x).powi(2) + (self.y - other.y).powi(2)).sqrt() + } +} +``` + +## 7.2 Multi-Module Crate with Imports + +**Input (Alloy JSX):** + +```tsx +const userRefkey = refkey(); +const greetRefkey = refkey(); + + + + + + + + + + + + + + + + ), + }, + ]} + > + {code`println!("Hello, {}! Age: {}", user.name, user.age);`} + + +; +``` + +**Expected output:** + +`lib.rs`: + +```rust +pub mod greet; +pub mod models; +``` + +`models/mod.rs`: + +```rust +pub mod user; +``` + +`models/user.rs`: + +```rust +#[derive(Debug)] +pub struct User { + pub name: String, + pub age: u32, +} +``` + +`greet.rs`: + +```rust +use crate::models::user::User; + +pub fn greet(user: User) { + println!("Hello, {}! Age: {}", user.name, user.age); +} +``` + +## 7.3 Trait and Impl + +**Expected output:** + +```rust +pub trait Greetable { + fn greeting(&self) -> String; + + fn greet(&self) { + println!("{}", self.greeting()); + } +} + +impl Greetable for User { + fn greeting(&self) -> String { + format!("Hello, {}!", self.name) + } +} +``` + +## 7.4 Enum with Variants + +**Expected output:** + +```rust +#[derive(Debug, Clone)] +pub enum Shape { + Circle(f64), + Rectangle { + width: f64, + height: f64, + }, + Point, +} +``` + +## 7.5 Cargo.toml + +**Expected output:** + +```toml +[package] +name = "myapp" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0", features = ["derive"] } +tokio = { version = "1", features = ["full"] } +``` + +--- + +# 8. Risks and Open Design Questions + +## 8.1 Design Questions + +1. **How should `mod` declarations be auto-generated?** Options: + - (a) `SourceFile` for `lib.rs`/`main.rs` auto-scans child modules from the scope and renders `mod` declarations. _(Recommended — similar to TS barrel files.)_ + - (b) User explicitly places `` components. + - (c) Hybrid: auto-generate by default, allow manual override. + +2. **How should `self` parameters be represented?** Options: + - (a) Magic first parameter: `{ name: "&self" }` or `{ name: "&mut self" }`. + - (b) Dedicated `receiver` prop on `FunctionDeclaration`: `receiver="&self"`. + - (c) Auto-injected when inside an `ImplBlock` (like Python auto-injects `self`). _(Recommended — least verbose for users.)_ + +3. **Should `ImplBlock` create a member scope on the target type?** If yes, methods declared inside an impl block would be discoverable as members of the type. This enables `memberRefkey(typeRefkey, "method_name")` references. _(Recommended: yes.)_ + +4. **How should field-level visibility work?** Struct fields in Rust have independent visibility from the struct. Options: + - (a) `pub` prop on `Field` component. _(Recommended — mirrors Rust syntax.)_ + - (b) Visibility property on the field symbol. + - (c) Both (prop sets symbol property). + +5. **How should `use` statement grouping work?** Rust idiom groups by path tree: + + ```rust + use std::io::{self, Read, Write}; + ``` + + Options: + - (a) Always flatten: one `use` per item. Simple to implement. + - (b) Group by common prefix: `use path::{A, B};`. More idiomatic. _(Recommended for MVP.)_ + +6. **Should lifetimes be modeled as symbols?** Options: + - (a) Yes — `LifetimeSymbol` in a `"lifetimes"` declaration space. Enables refkey-based lifetime references. + - (b) No — lifetimes are inline strings for MVP. _(Recommended for MVP.)_ + +## 8.2 Risks + +1. **Module declaration complexity.** Rust requires explicit `mod` declarations. Auto-generating these from the file tree is non-trivial: the order matters, visibility must be correct, and conditional modules (`#[cfg]`) exist. MVP should handle the simple case. + +2. **Impl block multiplicity.** A type can have many impl blocks. The member scope model must support this without symbol conflicts. Each impl block appends to the type's member space. + +3. **Use path construction.** Building correct `use` paths requires understanding the full module tree. Paths like `crate::a::b::Symbol` must be computed from the resolution result. Edge cases include re-exports and `self`/`super` paths. + +4. **External crate dependency tracking.** When a symbol from an external crate is referenced, the crate must appear in `Cargo.toml`'s `[dependencies]`. The `RustCrateScope` must track this (similar to `JavaProjectScope.addDependency()`). + +5. **No existing test for Rust output.** Unlike TS/Python/Java/C# which can be validated by their respective compilers/linters, Rust output validation would require `cargo check`. For MVP, tests will compare rendered strings. + +6. **Tuple struct and unit struct variants.** These are syntactically different from named structs but share the same `struct` keyword. The MVP defers these, but the symbol model should not preclude them. + +--- + +# 9. Recommendation + +**Implement `@alloy-js/rust` as a standard Alloy language package following the established 4-package pattern (TS/Java/Python/C#), with these Rust-specific adaptations:** + +1. **Symbol model:** `RustOutputSymbol` with `visibility` property (not member-space-encoded). Single `"members"` member space. `NamedTypeSymbol` and `FunctionSymbol` subclasses. + +2. **Scope hierarchy:** `RustCrateScope` → `RustModuleScope` → `RustFunctionScope` → `RustLexicalScope`, plus `RustImplScope` and `RustTraitScope` as member scopes. Module scopes track `use` imports. Crate scope tracks `mod` declarations and external dependencies. + +3. **Declaration spaces:** `["types", "values", "macros"]` on module/crate scopes. `["parameters", "type-parameters", "local-variables"]` on function scopes. + +4. **Core components (MVP):** `CrateDirectory`, `SourceFile`, `ModuleDirectory`, `CargoTomlFile`, `StructDeclaration`, `EnumDeclaration`, `FunctionDeclaration`, `TraitDeclaration`, `ImplBlock`, `TypeAlias`, `ConstDeclaration`, `Attribute`, `DeriveAttribute`, `DocComment`, `Reference`, `UseStatement`. + +5. **Import system:** Auto-generate `use` statements with path grouping (`use path::{A, B};`). Sort by std → external → crate. + +6. **Module system:** Auto-generate `mod` declarations in parent modules/crate root. + +7. **External dependencies:** `createCrate()` factory with `std` builtin. Auto-track crate dependencies for `Cargo.toml`. + +8. **Defer:** Lifetimes, closures, complex pattern matching, macro definitions, proc macros, tuple/unit structs, `pub(in path)`, re-exports, conditional compilation. + +This approach is conservative, proven by existing packages, and delivers a useful Rust code generator at MVP that can be extended incrementally. diff --git a/docs/language-packages/rust/04-rust-prd.md b/docs/language-packages/rust/04-rust-prd.md new file mode 100644 index 000000000..13b8300e9 --- /dev/null +++ b/docs/language-packages/rust/04-rust-prd.md @@ -0,0 +1,765 @@ +# 1. Title + +**PRD: Alloy Language Package for Rust (`@alloy-js/rust`)** + +--- + +# 2. Objective + +Add Rust language support to the Alloy code generation framework by implementing `@alloy-js/rust` as a new language package within the Alloy monorepo. The package will enable developers to declaratively generate correct, idiomatic, and compilable Rust source code using Alloy's JSX-based component model. + +--- + +# 3. Background and Context + +Alloy is a reactive, component-based code generation framework. It uses a custom JSX runtime (not React) with Vue-based reactivity to render component trees into formatted source code. Core provides: a rendering pipeline, a symbol/scope/reference resolution system (the binder), formatting via Prettier intrinsics, and structural components (`Output`, `SourceFile`, `SourceDirectory`, `Declaration`, `Scope`, `Block`). + +Four language packages already exist: TypeScript, Java, Python, and C#. Each follows a consistent architecture: + +1. **Symbol subclass** — extends `OutputSymbol` with language-specific properties. +2. **Scope hierarchy** — extends `OutputScope` with language-specific declaration/member spaces. +3. **Name policy** — transforms identifiers to language conventions via `createNamePolicy()`. +4. **Components** — JSX components for declarations (class, function, enum), references, imports, and structural elements. +5. **External dependency descriptors** — `createPackage()`/`createModule()`/`createLibrary()` for describing external libraries. +6. **Build file generation** — `package.json`, `pom.xml`, `.csproj` generation. +7. **Test utilities** — `toSourceText()` helper and per-component test files. + +Full analysis is available in: + +- [`01-core-understanding.md`](./01-core-understanding.md) — core architecture deep dive. +- [`02-existing-language-patterns.md`](./02-existing-language-patterns.md) — cross-language comparison. +- [`03-rust-design-notes.md`](./03-rust-design-notes.md) — Rust-specific design decisions. + +--- + +# 4. Problem Statement + +Alloy currently has no support for Rust code generation. Developers building tools, SDKs, or code generators that target Rust must hand-roll string templates or use ad-hoc approaches. This leads to: + +- Inconsistent formatting and naming. +- Manual import (`use`) management. +- No cross-module reference tracking. +- No automatic `Cargo.toml` dependency management. +- No integration with Alloy's reactive rendering or symbol resolution. + +A first-class `@alloy-js/rust` package would bring Rust to parity with the existing language packages, enabling declarative, component-based Rust code generation with automatic imports, name policies, and build file management. + +--- + +# 5. Goals + +1. **G1:** Implement a Rust language package following established Alloy patterns (consistent with TS/Java/Python/C# packages). +2. **G2:** Support generation of compilable Rust crates with multiple modules, structs, enums, functions, traits, and impl blocks. +3. **G3:** Automatically generate `use` statements when symbols are referenced across modules or crates. +4. **G4:** Automatically generate `mod` declarations in parent modules / crate root. +5. **G5:** Generate `Cargo.toml` with metadata and tracked dependencies. +6. **G6:** Apply Rust naming conventions (`snake_case`, `PascalCase`, `SCREAMING_SNAKE_CASE`) via name policy. +7. **G7:** Support `#[derive(...)]` and general attributes. +8. **G8:** Support `///` doc comments on declarations. +9. **G9:** Provide an external crate descriptor system (`createCrate()`) with `std` builtins. +10. **G10:** Comprehensive test coverage with golden output tests. + +--- + +# 6. Non-Goals + +The following are explicitly **out of scope** for the MVP: + +- **Lifetime annotations** (`'a`) — can be rendered inline via `code` template. +- **Closures** (`|x| expr`) — render inline. +- **Pattern matching** (complex `match` arms) — render inline. +- **Macro definitions** (`macro_rules!`, proc macros). +- **Tuple structs** (`struct Foo(T1, T2)`) and **unit structs** (`struct Foo;`). +- **`pub(in path)` visibility** — `pub`, `pub(crate)`, `pub(super)`, and private are sufficient. +- **`pub use` re-exports** and **glob imports** (`use path::*`). +- **Conditional compilation** (`#[cfg(...)]`). +- **Cargo workspace support** (multi-crate projects). +- **`impl Trait`** and **`dyn Trait`** syntax — render inline. +- **Associated types** in traits — render inline. +- **Control flow statements** (if/else, for, loop, while) — render inline via `code`. +- **Static declarations** (`static NAME: Type`). +- **Feature flags** in `Cargo.toml`. + +--- + +# 7. Users / Stakeholders + +| Role | Description | +| -------------------------------- | ---------------------------------------------------------------------------------------------- | +| **SDK/API generator developers** | Primary users building tools that output Rust code (e.g., API clients, data model generators). | +| **Alloy framework maintainers** | Maintain and review the package for consistency with core and other language packages. | +| **Code generation tool authors** | Use `@alloy-js/rust` as a library to produce Rust source files programmatically. | + +--- + +# 8. Scope of MVP + +The MVP delivers a fully functional `@alloy-js/rust` package that generates **compilable Rust crates** with: + +- Multi-module file structure with automatic `mod` declarations. +- Named structs, enums (with all variant kinds), functions, traits, impl blocks, type aliases, and constants. +- Automatic `use` statement generation with path grouping. +- `#[derive(...)]` and custom attributes. +- `///` doc comments. +- `Cargo.toml` with metadata and auto-tracked dependencies. +- Rust naming conventions applied via name policy. +- `std` library builtins. +- Comprehensive test suite. + +--- + +# 9. Functional Requirements + +## FR-1: Package Scaffolding and Public API + +**FR-1.1:** The package is located at `packages/rust/` in the monorepo with `package.json`, `tsconfig.json`, and `vitest.config.ts`. + +**FR-1.2:** The package name is `@alloy-js/rust`. It depends on `@alloy-js/core`. + +**FR-1.3:** `src/index.ts` barrel-exports from: `./builtins`, `./components`, `./context`, `./create-crate`, `./name-policy`, `./parameter-descriptor`, `./symbols`, `./utils`. + +**FR-1.4:** The package is consumable as `import * as rust from "@alloy-js/rust"` with components accessed as ``, ``, etc. + +**FR-1.5:** `src/components/stc/index.ts` provides STC wrappers for key components. + +## FR-2: Symbol System + +**FR-2.1:** `RustOutputSymbol` extends `OutputSymbol` with properties: + +- `visibility: "pub" | "pub(crate)" | "pub(super)" | undefined` (undefined = private). +- `symbolKind: "function" | "struct" | "enum" | "trait" | "type-alias" | "const" | "static" | "module" | "field" | "variant" | "method" | "associated-type" | "parameter" | "type-parameter"`. +- `isAsync: boolean`. +- `isUnsafe: boolean`. +- `isConst: boolean`. +- `static readonly memberSpaces = ["members"]`. + +**FR-2.2:** `NamedTypeSymbol` extends `RustOutputSymbol` with: + +- `typeKind: "struct" | "enum" | "trait" | "type-alias"`. +- `static readonly memberSpaces = ["members", "type-parameters"]`. + +**FR-2.3:** `FunctionSymbol` extends `RustOutputSymbol` with: + +- `receiverType?: Children` (for method receiver: `&self`, `&mut self`, `self`). + +**FR-2.4:** All symbols implement `copy()` following the pattern in `BasicSymbol` and existing language packages. + +## FR-3: Scope Hierarchy + +**FR-3.1:** `RustCrateScope` extends `OutputScope`: + +- `declarationSpaces = ["types", "values"]`. +- Tracks child module declarations (for auto-generating `mod` statements). +- Tracks external crate dependencies (for `Cargo.toml`). +- Has `addChildModule(name, visibility)` and `addDependency(crate)` methods. + +**FR-3.2:** `RustModuleScope` extends `OutputScope`: + +- `declarationSpaces = ["types", "values"]`. +- Tracks `use` imports via `addUse(targetSymbol, sourcePath)`. +- Tracks child module declarations (for submodule `mod` statements). +- Has `imports` getter returning tracked use records. + +**FR-3.3:** `RustFunctionScope` extends `OutputScope`: + +- `declarationSpaces = ["parameters", "type-parameters", "local-variables"]`. + +**FR-3.4:** `RustLexicalScope` extends `OutputScope`: + +- `declarationSpaces = ["local-variables"]`. + +**FR-3.5:** `RustImplScope` is a member scope (has `ownerSymbol`) for impl blocks. Methods declared inside are added to the target type's `"members"` space. + +**FR-3.6:** `RustTraitScope` is a member scope for trait bodies. + +## FR-4: Name Policy + +**FR-4.1:** `createRustNamePolicy()` returns a `NamePolicy` where `RustElements` includes: `"function"`, `"method"`, `"struct"`, `"enum"`, `"enum-variant"`, `"trait"`, `"type-alias"`, `"type-parameter"`, `"field"`, `"variable"`, `"parameter"`, `"constant"`, `"module"`. + +**FR-4.2:** Naming transformations: + +- `PascalCase`: struct, enum, enum-variant, trait, type-alias, type-parameter. +- `snake_case`: function, method, field, variable, parameter, module. +- `SCREAMING_SNAKE_CASE`: constant. + +**FR-4.3:** Reserved word handling: 37 Rust keywords. Conflicting names are prefixed with `r#` (e.g., `r#type`). + +**FR-4.4:** Exported via `createRustNamePolicy()` and `useRustNamePolicy()`. + +## FR-5: File / Module / Crate Structure + +**FR-5.1:** `CrateDirectory` component: + +- Props: `name: string`, `version?: string` (default `"0.1.0"`), `edition?: string` (default `"2021"`), `children: Children`. +- Creates `RustCrateScope`. +- Renders `Cargo.toml` via `CargoTomlFile`. +- Renders `src/` subdirectory containing source files. +- Provides crate context to descendants. + +**FR-5.2:** `SourceFile` component: + +- Props: `path: string`, `children?: Children`, `header?: Children`, `headerComment?: string`. +- Creates `RustModuleScope`. +- Wraps core `SourceFile` with `filetype="rust"` and the Rust `Reference` component. +- Auto-generates `use` statements from collected imports. +- For crate root files (`lib.rs`, `main.rs`): auto-generates `mod` declarations for child modules. + +**FR-5.3:** `ModuleDirectory` component: + +- Props: `path: string`, `children?: Children`. +- Creates a `SourceDirectory` with a corresponding module scope. +- Generates `mod.rs` if submodule files exist. +- Registers itself as a child module in the parent scope. + +**FR-5.4:** `CargoTomlFile` component: + +- Props: `name: string`, `version: string`, `edition: string`, `dependencies?: Record`. +- Renders `[package]` section with name, version, edition. +- Renders `[dependencies]` section from crate scope's tracked dependencies + explicit props. +- `CrateDependency` type: `string` (version only) or `{ version: string, features?: string[] }`. + +**FR-5.5:** Automatic `mod` declarations: When `SourceFile` renders a crate root or module root, it queries the scope for registered child modules and renders `[pub] mod ;` for each, sorted alphabetically. + +## FR-6: Import / Use Handling + +**FR-6.1:** When a `Reference` component resolves a symbol from a different module within the same crate, `use crate::path::to::Symbol;` is added to the current module scope's import records. + +**FR-6.2:** When a `Reference` resolves a symbol from an external crate, `use ::path::to::Symbol;` is added and the crate dependency is recorded on the `RustCrateScope`. + +**FR-6.3:** Symbols within the same module do not generate `use` statements. + +**FR-6.4:** `UseStatement` component groups multiple imports from the same path: `use path::{Item1, Item2};`. + +**FR-6.5:** Import ordering: `std::` → external crate → `crate::` (sorted alphabetically within each group, blank line between groups). + +**FR-6.6:** `UseStatements` component renders all accumulated imports for a source file. + +## FR-7: Identifiers and References + +**FR-7.1:** `Reference` component accepts `refkey: Refkey`, resolves via the binder, and renders the symbol's name. + +**FR-7.2:** Reference resolution triggers import tracking (FR-6.1, FR-6.2). + +**FR-7.3:** `memberRefkey(base, member)` works for accessing struct fields, enum variants, and impl methods. + +## FR-8: Declarations + +**FR-8.1: `StructDeclaration`** + +- Props: `name`, `refkey?`, `pub?`, `pub_crate?`, `derives?: (string | Refkey)[]`, `attributes?: AttributeProp[]`, `doc?: Children`, `typeParameters?: TypeParameterProp[]`, `whereClause?: Children`, `children: Children`. +- Creates a `NamedTypeSymbol` with `typeKind: "struct"`. +- Creates a member scope for fields. +- Renders: `[attributes]\n[pub] struct Name [where ...] {\n fields\n}`. + +**FR-8.2: `Field`** (child of `StructDeclaration`) + +- Props: `name`, `type: Children`, `refkey?`, `pub?`, `pub_crate?`, `doc?: Children`. +- Creates a member symbol in the struct's member space. +- Renders: `[pub] name: type,`. + +**FR-8.3: `EnumDeclaration`** + +- Props: `name`, `refkey?`, `pub?`, `pub_crate?`, `derives?: (string | Refkey)[]`, `attributes?: AttributeProp[]`, `doc?: Children`, `typeParameters?: TypeParameterProp[]`, `children: Children`. +- Creates a `NamedTypeSymbol` with `typeKind: "enum"`. +- Renders: `[attributes]\n[pub] enum Name {\n variants\n}`. + +**FR-8.4: `EnumVariant`** (child of `EnumDeclaration`) + +- Props: `name`, `refkey?`, `doc?: Children`. +- For unit variant: `name,`. +- For tuple variant: `fields?: Children[]` → `name(Type1, Type2),`. +- For struct variant: `children: Children` → `name {\n fields\n},`. +- Creates a member symbol in the enum's member space. + +**FR-8.5: `FunctionDeclaration`** + +- Props: `name`, `refkey?`, `pub?`, `pub_crate?`, `async?`, `unsafe?`, `const?`, `parameters?: ParameterDescriptor[]`, `returnType?: Children`, `typeParameters?: TypeParameterProp[]`, `whereClause?: Children`, `doc?: Children`, `children?: Children`. +- Creates a `FunctionSymbol`. +- When inside an `ImplBlock`, auto-injects `&self` as first parameter (configurable via `receiver` prop: `"&self" | "&mut self" | "self" | "none"`). +- Renders: `[pub] [async] [unsafe] [const] fn name(params) [-> ReturnType] [where ...] {\n body\n}`. +- Empty body renders `{}`. + +**FR-8.6: `TraitDeclaration`** + +- Props: `name`, `refkey?`, `pub?`, `typeParameters?: TypeParameterProp[]`, `supertraits?: Children[]`, `whereClause?: Children`, `doc?: Children`, `children: Children`. +- Creates a `NamedTypeSymbol` with `typeKind: "trait"`. +- Creates `RustTraitScope` for body. +- Renders: `[pub] trait Name[: Supertrait1 + Supertrait2] [where ...] {\n methods\n}`. + +**FR-8.7: `ImplBlock`** + +- Props: `type: Refkey | Children`, `trait?: Refkey | Children`, `typeParameters?: TypeParameterProp[]`, `whereClause?: Children`, `children: Children`. +- Creates `RustImplScope` with the target type as owner. +- Methods declared inside are added to the target type's `"members"` space. +- Renders: `impl [Trait for] Type [where ...] {\n methods\n}`. + +**FR-8.8: `TypeAlias`** + +- Props: `name`, `refkey?`, `pub?`, `typeParameters?: TypeParameterProp[]`, `children: Children`. +- Creates a symbol with `symbolKind: "type-alias"`. +- Renders: `[pub] type Name = children;`. + +**FR-8.9: `ConstDeclaration`** + +- Props: `name`, `refkey?`, `pub?`, `type: Children`, `children: Children`. +- Creates a symbol with `symbolKind: "const"`. +- Renders: `[pub] const NAME: Type = value;`. + +## FR-9: Type Parameters and Where Clauses + +**FR-9.1:** `TypeParameters` component renders `<'a, T: Bound, U>` from a `TypeParameterProp[]` array. Each prop: `{ name: string, constraint?: Children }`. + +**FR-9.2:** `WhereClause` component renders `where T: Display + Clone, U: Debug` from children or structured props. + +## FR-10: Attributes + +**FR-10.1:** `Attribute` component: + +- Props: `name: string | Refkey`, `args?: Children`. +- Renders: `#[name]` or `#[name(args)]`. + +**FR-10.2:** `DeriveAttribute` component: + +- Props: `traits: (string | Refkey)[]`. +- Renders: `#[derive(Trait1, Trait2, ...)]`. +- When a trait is a `Refkey`, resolves the symbol name and tracks the crate dependency. + +**FR-10.3:** Attributes are rendered on the line before the item they decorate. + +## FR-11: Comments + +**FR-11.1:** `DocComment` component: + +- Props: `children: Children`. +- Renders each line prefixed with `/// `. +- Supports multi-line content. + +**FR-11.2:** `ModuleDocComment` component: + +- Props: `children: Children`. +- Renders each line prefixed with `//! `. +- Used at the top of module files. + +**FR-11.3:** Declaration components accept a `doc` prop that renders a `DocComment` before the declaration. + +## FR-12: Parameter Descriptors + +**FR-12.1:** `ParameterDescriptor` interface: + +```typescript +interface ParameterDescriptor { + name: string; + type?: Children; + mutable?: boolean; // mut binding + refType?: "&" | "&mut"; // reference type +} +``` + +**FR-12.2:** `Parameters` component renders `(param1: Type, param2: &mut Type)` from a `ParameterDescriptor[]`. + +**FR-12.3:** Special receiver parameters: `&self`, `&mut self`, `self` are handled by a `receiver` prop on `FunctionDeclaration`, not as regular parameters. + +## FR-13: Formatting + +**FR-13.1:** Default format options: `printWidth: 100`, `tabWidth: 4`, `useTabs: false`. + +**FR-13.2:** Trailing commas in multi-line struct fields, enum variants, and function parameters. + +**FR-13.3:** Blank lines between top-level items (functions, structs, enums, impl blocks). + +**FR-13.4:** `Block` component from core used for `{ ... }` syntax. + +## FR-14: External Crate Descriptors + +**FR-14.1:** `createCrate()` factory function: + +```typescript +function createCrate(props: { + name: string; + version: string; + features?: string[]; + descriptor: T; +}): CrateRefkeys & SymbolCreator; +``` + +**FR-14.2:** Descriptor maps module paths to named exports: + +```typescript +{ ".": { named: ["Symbol1", "Symbol2"] }, "submod": { named: ["Symbol3"] } } +``` + +**FR-14.3:** When a symbol from an external crate is referenced: + +- A `use ::Symbol;` is added. +- The crate is added to `RustCrateScope.dependencies`. +- `Cargo.toml` renders the dependency. + +**FR-14.4:** `std` builtin crate descriptor provided in `src/builtins/std.ts` covering at minimum: + +- `Option`, `Some`, `None`, `Result`, `Ok`, `Err` +- `Vec`, `String`, `Box`, `Rc`, `Arc` +- `HashMap`, `HashSet`, `BTreeMap`, `BTreeSet` +- `fmt::Display`, `fmt::Debug`, `fmt::Formatter` +- `io::Read`, `io::Write`, `io::Result` +- `clone::Clone`, `default::Default`, `convert::{From, Into}` + +## FR-15: Value / Literal Rendering + +**FR-15.1:** `Value` component converts JavaScript values to Rust literals: + +- `string` → `"string"` +- `number` (integer) → `42` +- `number` (float) → `42.0` +- `boolean` → `true` / `false` +- `null`/`undefined` → `None` +- Arrays → `vec![a, b, c]` + +## FR-16: Tests + +**FR-16.1:** `test/utils.tsx` provides: + +- `toSourceText(children, options?)` — wraps in `Output` + `CrateDirectory` + `SourceFile` and renders. +- `toSourceTextMultiple(children)` — multi-file variant. +- `findFile(output, path)` — extracts a file from output. +- `assertFileContents(output, expected)` — batch validation. + +**FR-16.2:** Minimum test files: + +- `struct.test.tsx` — struct declaration, fields, visibility, derives. +- `enum.test.tsx` — enum with all variant kinds. +- `function.test.tsx` — function with params, return type, async, pub. +- `trait.test.tsx` — trait declaration with methods and default impls. +- `impl.test.tsx` — inherent impl and trait impl. +- `imports.test.tsx` — cross-module `use` generation. +- `reference.test.tsx` — refkey resolution. +- `name-policy.test.tsx` — naming convention transformations. +- `attributes.test.tsx` — derive and custom attributes. +- `cargo-toml.test.tsx` — Cargo.toml generation. +- `module-structure.test.tsx` — multi-module crate with `mod` declarations. + +--- + +# 10. Non-Functional Requirements + +**NFR-1: Consistency** — The package follows the same architecture as existing Alloy language packages. Directory layout, barrel exports, symbol/scope patterns, component conventions, and test patterns must match. + +**NFR-2: Maintainability** — Each component is in its own file. Symbols, scopes, and components are cleanly separated. No circular dependencies between modules. + +**NFR-3: Public API clarity** — The package is consumed as `import * as rust from "@alloy-js/rust"`. Components use descriptive names (`StructDeclaration`, not `Struct`). Props interfaces are exported and documented. + +**NFR-4: Testability** — Every component is independently testable via `toSourceText()`. Cross-cutting concerns (imports, mod declarations) are testable via multi-file renders. + +**NFR-5: Incremental extensibility** — Deferred features (lifetimes, closures, pattern matching) can be added without breaking changes. The symbol model must not preclude future extensions. + +**NFR-6: Predictable rendering** — Output matches `rustfmt` conventions (4-space indent, 100-char width, trailing commas). Generated code should pass `cargo check` without modification. + +**NFR-7: No breaking core changes** — The package uses only the public API of `@alloy-js/core`. No changes to core are required. + +--- + +# 11. Proposed Package / Module Architecture + +``` +packages/rust/ +├── src/ +│ ├── index.ts # Barrel: re-exports all public API +│ ├── name-policy.ts # createRustNamePolicy(), useRustNamePolicy() +│ ├── create-crate.ts # createCrate() factory +│ ├── parameter-descriptor.ts # ParameterDescriptor interface +│ ├── utils.ts # Shared helpers +│ ├── symbols/ +│ │ ├── index.ts # Symbol barrel +│ │ ├── rust-output-symbol.ts # RustOutputSymbol (base class) +│ │ ├── named-type-symbol.ts # NamedTypeSymbol (struct, enum, trait) +│ │ ├── function-symbol.ts # FunctionSymbol +│ │ ├── factories.ts # createStructSymbol, createFunctionSymbol, etc. +│ │ └── reference.tsx # ref() resolution + use generation +│ ├── scopes/ +│ │ ├── index.ts # Scope barrel + type alias + hooks +│ │ ├── rust-crate-scope.ts # RustCrateScope (mod tracking, dependencies) +│ │ ├── rust-module-scope.ts # RustModuleScope (use tracking) +│ │ ├── rust-function-scope.ts # RustFunctionScope +│ │ ├── rust-lexical-scope.ts # RustLexicalScope +│ │ ├── rust-impl-scope.ts # RustImplScope (member scope) +│ │ └── rust-trait-scope.ts # RustTraitScope (member scope) +│ ├── components/ +│ │ ├── index.ts # Component barrel +│ │ ├── stc/index.ts # STC wrappers +│ │ ├── source-file.tsx # SourceFile (wraps core SourceFile) +│ │ ├── crate-directory.tsx # CrateDirectory (crate root) +│ │ ├── module-directory.tsx # ModuleDirectory (submodule dirs) +│ │ ├── cargo-toml-file.tsx # CargoTomlFile +│ │ ├── declaration.tsx # Base Declaration wrapper +│ │ ├── reference.tsx # Reference component +│ │ ├── use-statement.tsx # UseStatement / UseStatements +│ │ ├── struct-declaration.tsx # StructDeclaration + Field +│ │ ├── enum-declaration.tsx # EnumDeclaration + EnumVariant +│ │ ├── function-declaration.tsx # FunctionDeclaration +│ │ ├── trait-declaration.tsx # TraitDeclaration +│ │ ├── impl-block.tsx # ImplBlock +│ │ ├── type-alias.tsx # TypeAlias +│ │ ├── const-declaration.tsx # ConstDeclaration +│ │ ├── mod-declaration.tsx # ModDeclaration +│ │ ├── attribute.tsx # Attribute + DeriveAttribute +│ │ ├── doc-comment.tsx # DocComment + ModuleDocComment +│ │ ├── parameters.tsx # Parameters rendering +│ │ ├── type-parameters.tsx # TypeParameters + WhereClause +│ │ └── value.tsx # Value / literal rendering +│ ├── context/ +│ │ ├── index.ts +│ │ └── crate-context.tsx # CrateContext (crate metadata) +│ └── builtins/ +│ ├── index.ts +│ └── std.ts # std crate descriptor +├── test/ +│ ├── utils.tsx +│ ├── vitest.setup.ts +│ └── *.test.tsx # Per FR-16.2 +├── package.json +├── tsconfig.json +└── vitest.config.ts +``` + +**Dependency graph (internal):** + +``` +index.ts + └── components/index.ts + ├── source-file.tsx → symbols/rust-module-scope.ts, reference.tsx + ├── crate-directory.tsx → symbols/rust-crate-scope.ts, cargo-toml-file.tsx + ├── struct-declaration.tsx → symbols/factories.ts, declaration.tsx + ├── reference.tsx → symbols/reference.tsx + └── ... + └── symbols/index.ts + ├── rust-output-symbol.ts → @alloy-js/core (OutputSymbol) + ├── factories.ts → rust-output-symbol.ts, scopes.ts + └── reference.tsx → @alloy-js/core (resolve, useBinder) + └── name-policy.ts → @alloy-js/core (createNamePolicy) + └── create-crate.ts → symbols/, @alloy-js/core (SymbolCreator) + └── builtins/std.ts → create-crate.ts +``` + +--- + +# 12. Milestones / Phases + +## Phase 1: Foundation + +**Goal:** Package scaffold, symbol system, scope hierarchy, name policy. + +**Deliverables:** + +- `package.json`, `tsconfig.json`, `vitest.config.ts` configured. +- `RustOutputSymbol`, `NamedTypeSymbol`, `FunctionSymbol` classes. +- All 6 scope classes (`RustCrateScope`, `RustModuleScope`, `RustFunctionScope`, `RustLexicalScope`, `RustImplScope`, `RustTraitScope`). +- `createRustNamePolicy()` with all element types and reserved words. +- Symbol factory functions (`createStructSymbol`, `createEnumSymbol`, `createFunctionSymbol`, etc.). +- `test/utils.tsx` with `toSourceText()`. +- `name-policy.test.tsx` passing. + +**Verification:** Name policy tests pass. Symbols and scopes can be created without errors. + +## Phase 2: Core Components + +**Goal:** Basic declaration components render correct Rust syntax. + +**Deliverables:** + +- `SourceFile`, `CrateDirectory`, `Declaration`, `Reference`. +- `StructDeclaration` + `Field`. +- `EnumDeclaration` + `EnumVariant`. +- `FunctionDeclaration` + `Parameters`. +- `TypeAlias`, `ConstDeclaration`. +- `TypeParameters`, `WhereClause`. +- `Attribute`, `DeriveAttribute`. +- `DocComment`, `ModuleDocComment`. +- `Value`. + +**Verification:** `struct.test.tsx`, `enum.test.tsx`, `function.test.tsx`, `attributes.test.tsx` passing. Single-file renders produce correct Rust syntax. + +## Phase 3: Traits and Impl Blocks + +**Goal:** Trait declarations and impl blocks work correctly. + +**Deliverables:** + +- `TraitDeclaration` with method signatures and default impls. +- `ImplBlock` for inherent and trait implementations. +- `FunctionDeclaration` handles `receiver` prop (self parameter) inside impl blocks. + +**Verification:** `trait.test.tsx`, `impl.test.tsx` passing. Trait + impl combinations render correctly. + +## Phase 4: Module System and Imports + +**Goal:** Multi-module crates with automatic `use` and `mod` generation. + +**Deliverables:** + +- `ModuleDirectory` component. +- `UseStatement` / `UseStatements` with path grouping and sorting. +- `RustModuleScope.addUse()` and import tracking. +- `RustCrateScope.addChildModule()` and `mod` declaration rendering. +- Auto-generation of `mod` declarations in crate root and parent modules. + +**Verification:** `imports.test.tsx`, `reference.test.tsx`, `module-structure.test.tsx` passing. Cross-module references generate correct `use` statements and `mod` declarations. + +## Phase 5: External Dependencies and Build File + +**Goal:** External crate support and `Cargo.toml` generation. + +**Deliverables:** + +- `createCrate()` factory. +- `std` builtin descriptor. +- `CargoTomlFile` component. +- Dependency tracking on `RustCrateScope`. +- External crate references trigger `use` + dependency recording. + +**Verification:** `cargo-toml.test.tsx`, external crate reference tests passing. `Cargo.toml` includes tracked dependencies. + +## Phase 6: Polish and Integration + +**Goal:** Final cleanup, STC wrappers, documentation, full test suite green. + +**Deliverables:** + +- STC wrappers for all key components. +- Barrel exports verified complete. +- All test files passing. +- Edge case coverage (empty structs, empty enums, empty functions, etc.). + +**Verification:** Full test suite green. All acceptance criteria met. + +--- + +# 13. Acceptance Criteria + +The MVP is complete when **all** of the following are true: + +1. **AC-1:** `packages/rust/` exists with correct `package.json` (name `@alloy-js/rust`, depends on `@alloy-js/core`). +2. **AC-2:** `import * as rust from "@alloy-js/rust"` works and exposes all components, symbols, and utilities. +3. **AC-3:** A multi-module Rust crate can be generated with `CrateDirectory` → `SourceFile` → declaration components. +4. **AC-4:** `StructDeclaration` renders named structs with `pub` fields, `#[derive(...)]`, doc comments, and type parameters. +5. **AC-5:** `EnumDeclaration` renders enums with unit, tuple, and struct variants. +6. **AC-6:** `FunctionDeclaration` renders functions with parameters, return types, `pub`, `async`, `const`, and `unsafe` modifiers. +7. **AC-7:** `TraitDeclaration` renders traits with method signatures, default implementations, and supertraits. +8. **AC-8:** `ImplBlock` renders both inherent impl and `impl Trait for Type` blocks. +9. **AC-9:** Cross-module symbol references auto-generate correct `use crate::path::Symbol;` statements. +10. **AC-10:** External crate symbol references auto-generate `use ::Symbol;` and record the dependency. +11. **AC-11:** `Cargo.toml` is generated with `[package]` metadata and `[dependencies]` from tracked crate dependencies. +12. **AC-12:** `mod` declarations are auto-generated in crate root / parent modules for all child modules. +13. **AC-13:** Name policy transforms names correctly: `PascalCase` for types, `snake_case` for functions/fields, `SCREAMING_SNAKE_CASE` for constants, `r#` prefix for reserved words. +14. **AC-14:** Format options default to 4-space indent, 100-char width. +15. **AC-15:** All test files listed in FR-16.2 exist and pass. +16. **AC-16:** Golden scenario outputs (struct+impl, multi-module crate, trait+impl, enum, Cargo.toml) match expected Rust syntax. + +--- + +# 14. Test Strategy + +## 14.1 Unit Tests + +Each component gets a dedicated test file. Tests use `toSourceText()` to render a single component and compare against expected output via `toRenderTo()` + `d` template tag. + +**Coverage per component:** + +- Basic rendering (minimal props). +- All modifier combinations (`pub`, `async`, `const`, `unsafe`). +- With and without type parameters. +- With and without doc comments. +- With and without attributes. +- Edge cases (empty body, no fields, etc.). + +## 14.2 Golden Output Tests + +Multi-file tests that render a complete crate and validate all files: + +1. **Struct + Impl scenario** — single file with struct, derives, impl block with methods. +2. **Multi-module scenario** — crate root + 2 submodules with cross-module references. +3. **Trait + Impl scenario** — trait declaration + impl for a struct. +4. **Enum scenario** — enum with all variant kinds. +5. **External crate scenario** — references to serde traits with auto-generated `use` + `Cargo.toml` dependencies. + +Each golden test validates the exact string output of every generated file. + +## 14.3 Scenario Coverage + +| Scenario | Tests | +| -------------------- | --------------------------------------------------------------------- | +| Symbol creation | Factory functions create correct symbol types with correct properties | +| Name policy | All element types transform correctly; reserved words get `r#` | +| Single-file struct | Struct with fields, derives, visibility, doc comments | +| Single-file enum | Enum with unit, tuple, struct variants | +| Single-file function | Function with params, return type, async, modifiers | +| Trait | Trait with methods, default impls, supertraits | +| Impl block | Inherent impl, trait impl, self receiver | +| Cross-module import | Symbol in module A referenced in module B generates `use` | +| External crate | External crate symbol generates `use` + dependency tracking | +| Cargo.toml | Metadata + dependencies rendered correctly | +| Module structure | `mod` declarations auto-generated in parent modules | +| Attributes | `#[derive(...)]`, custom attributes | +| Type parameters | ``, where clauses | +| Constants | `const NAME: Type = value;` | + +## 14.4 Regression Coverage + +Tests should be added for any bug found during development. Key regression areas: + +- Import deduplication (same symbol referenced twice should produce one `use`). +- `use` path grouping (`use path::{A, B}` not two separate `use` statements). +- `mod` declaration ordering and visibility. +- Name conflicts across modules. +- Reserved word handling in different positions (type name, field name, parameter name). + +--- + +# 15. Risks and Mitigations + +| Risk | Impact | Mitigation | +| ------------------------------------------------------------------ | ------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Module `mod` declaration auto-generation is complex** | Incorrect `mod` declarations break compilation | Implement `mod` generation as a separate concern with dedicated tests. Start with simple cases (flat module tree) then handle nesting. | +| **`use` path construction from `ResolutionResult` is error-prone** | Wrong import paths | Study Go's `ref()` implementation closely. Unit test every path scenario: same module, sibling module, nested module, parent module, external crate. | +| **Impl blocks adding to existing type's member space** | Symbol conflicts if multiple impl blocks for same type | Each impl block appends to the type's `"members"` space. Use name deconfliction (inherent from `SymbolTable`). Test with multiple impl blocks. | +| **`Cargo.toml` TOML formatting** | Incorrect TOML syntax | Use simple string-based rendering. TOML is straightforward for the sections we need. Test against expected TOML strings. | +| **Rust's `r#` reserved word syntax is unusual** | Other packages use `_` suffix; agents may forget `r#` | Implement in name policy from Phase 1. Test explicitly. | +| **No `cargo check` validation in CI** | Generated code may have subtle syntax errors | Rely on string comparison tests with carefully validated golden outputs. Consider adding optional `cargo check` integration test later. | + +--- + +# 16. Open Questions + +_Questions 1, 3, 4, 5, and 6 have been resolved. Remaining open questions:_ + +1. ~~**Self receiver auto-injection**~~ **RESOLVED:** Methods inside `ImplBlock` or `TraitDeclaration` default to `receiver="&self"`. Standalone functions default to `receiver="none"`. Overridable via prop. `receiver="none"` inside an impl block creates an associated function. + +2. **How should `mod` declarations handle visibility?** + - In Rust, `mod foo;` makes the module private; `pub mod foo;` makes it public. + - **Decision:** `ModuleDirectory` accepts a `pub` prop (default false). The parent SourceFile renders `pub mod name;` or `mod name;` accordingly. + - Status: Resolved. + +3. ~~**`use` tree syntax vs flat**~~ **RESOLVED:** MVP uses flat `use` statements (`use path::A; use path::B;`) for simplicity. Tree grouping (`use path::{A, B};`) is a post-MVP polish item. Flat syntax is valid Rust and `rustfmt` can group if desired. + +4. ~~**Prelude handling**~~ **RESOLVED:** Yes, maintain a `PRELUDE_TYPES` set. Prelude types skip `use` generation. Full list defined in design doc section 3.14. + +5. ~~**Crate types**~~ **RESOLVED:** `CrateDirectory` supports both via `crateType` prop (`"lib"` | `"bin"`, default `"lib"`). + +6. ~~**Self receiver modeling**~~ **RESOLVED:** Dedicated `receiver` prop on `FunctionDeclaration`. + +**Remaining unresolved:** + +7. **Should generated code include trailing commas in all multi-line contexts?** + - Rust convention allows it. `rustfmt` adds them. Recommend: yes. + - Low risk — can be changed later. + +--- + +# 17. Recommended Next Step + +**Convert this PRD into a phased implementation backlog** (`05-rust-backlog.md`) with: + +- Individual work items derived from each functional requirement. +- Dependencies between items. +- Clear acceptance criteria per item. +- Test requirements per item. + +Then begin implementation starting with **Phase 1: Foundation** — package scaffold, symbol system, scope hierarchy, and name policy. diff --git a/docs/language-packages/rust/06-review.md b/docs/language-packages/rust/06-review.md new file mode 100644 index 000000000..5c6decee8 --- /dev/null +++ b/docs/language-packages/rust/06-review.md @@ -0,0 +1,225 @@ +# Planning Review: @alloy-js/rust + +**Reviewer role:** Skeptical senior architect +**Documents reviewed:** 01-core-understanding.md, 02-existing-language-patterns.md, 03-rust-design-notes.md, 04-rust-prd.md, docs/backlog/\* +**Verified against:** Actual repository code in packages/go, packages/csharp, packages/typescript, packages/python, packages/core + +--- + +# 1. Overall Assessment + +The planning set is **thorough, well-structured, and largely accurate**. The 4-document pipeline (core understanding → patterns → design → PRD) builds knowledge incrementally, and the backlog is detailed enough for autonomous AI agent execution. However, cross-referencing the docs against the actual repository reveals **several factual inaccuracies, missing infrastructure tasks, and architectural assumptions that need correction** before implementation begins. + +The most significant issues are: (1) the scopes directory structure is wrong in the proposed layout, (2) several build/infrastructure tasks are missing from the backlog, (3) the "triple declaration space" design for Rust modules may be over-engineered, and (4) the SymbolCreator protocol claim is incorrect. + +--- + +# 2. Strengths + +1. **Grounded in repository evidence.** The core understanding doc (01) correctly identifies the rendering pipeline, symbol system, and context model. File paths are accurate. + +2. **Cross-language comparison is excellent.** The patterns doc (02) accurately catalogs the common anatomy and divergences. The concept matrix is detailed and useful. + +3. **Rust language semantics are precise.** The design doc (03) correctly describes Rust's module system, visibility rules, `use` semantics, impl blocks, and naming conventions. No significant Rust-language errors found. + +4. **Backlog is execution-ready.** 35 tasks with dependencies, acceptance criteria, context files, and validation commands. The agent execution rules are well-thought-out. + +5. **MVP scope is well-bounded.** The deferred features list (lifetimes, closures, macros, etc.) is appropriate. The golden scenarios are realistic. + +6. **Impl blocks are correctly identified as novel.** The plan acknowledges this has no precedent in Alloy and needs careful design. + +7. **Phase ordering respects dependencies.** The critical path (T001→T003→T005→T006→T010→T022) is correct. + +--- + +# 3. Gaps + +## 3.1 Missing Infrastructure Tasks + +**G1: `api-extractor.json` is not in the backlog.** +Every existing language package (Go, TS, Java, Python, C#) has an `api-extractor.json` that extends `../../api-extractor.base.json`. The Go build script is `"build": "alloy build --with-dev && pnpm run generate-docs"`. The backlog's T001 (package scaffold) does not mention this. +_Evidence:_ `packages/go/api-extractor.json`, `packages/go/package.json` scripts. + +**G2: The `alloy build` tool is not mentioned.** +Go uses `alloy build --with-dev` (not `tsc`). The CLI is at `packages/cli/`. The build script and `--with-dev` flag produce both `dist/` and `dist/dev/` directories. T001 says "Copy package.json from Go" but doesn't explicitly call out the build tool. +_Evidence:_ `packages/go/package.json` scripts section. + +**G3: The `#imports` hash pattern is missing.** +Go's `package.json` includes `"imports": { "#test/*": "./test/*", "#components/*": { ... } }` for path aliases. This is used alongside `tsconfig.json` paths. The Rust package will need the same. +_Evidence:_ `packages/go/package.json` imports field. + +**G4: No task for `tsdoc.json`.** +Some packages have `tsdoc.json`. While not all do, the api-extractor integration may require it. + +**G5: The `prepack` script is missing from T001.** +Go has `"prepack": "node ../../scripts/strip-dev-exports.js"` which strips `"source"` conditions on publish. This is needed for correctness. +_Evidence:_ `packages/go/package.json`. + +## 3.2 Missing Design Details + +**G6: Prelude type list is undefined.** +The design says "maintain a PRELUDE*TYPES set" for skipping imports, but neither the design doc nor any task defines the exact list. Rust's prelude includes: `Option`, `Some`, `None`, `Result`, `Ok`, `Err`, `Vec`, `String`, `ToString`, `Box`, `Clone`, `Copy`, `Default`, `Drop`, `Eq`, `PartialEq`, `Ord`, `PartialOrd`, `Iterator`, `IntoIterator`, `From`, `Into`, `AsRef`, `AsMut`, `Send`, `Sync`, `Sized`, `Unpin`, `bool`, `char`, `i8`–`i128`, `u8`–`u128`, `f32`, `f64`, `str`, `usize`, `isize`. This is a non-trivial list. +\_Impact:* T022 (reference resolution) and T029 (std builtins) need this, but it's not specified. + +**G7: No name conflict resolver task.** +The backlog says "use default from core" for name conflicts. But TypeScript has a custom `tsNameConflictResolver` that prioritizes non-import symbols. Rust will likely need similar logic — imported symbols (via `use`) should be renamed before local declarations. No task exists for this. +_Evidence:_ `packages/typescript/src/name-conflict-resolver.ts`. + +**G8: `SourceFile` mod-declaration rendering is underspecified.** +The design says "SourceFile auto-generates mod declarations in parent modules" but the mechanics are unclear. In Rust, `mod foo;` goes in the _parent_ file, not the child. So `lib.rs` declares `mod models;`, not `models/mod.rs`. But `ModuleDirectory` creates the child scope. How does the child registration propagate to the parent SourceFile for rendering? This needs clearer design. + +--- + +# 4. Ambiguities + +**A1: "Triple declaration space" (`["types", "values", "macros"]`) is not justified.** +The design proposes `declarationSpaces = ["types", "values", "macros"]` for module scopes. Go uses `["values", "types"]` which is the closest analog. However, _no existing package puts macros in a declaration space_. Rust macro names rarely conflict with type/value names in code generation contexts. This adds complexity without clear benefit for MVP. +_Recommendation:_ Use `["types", "values"]` like Go for MVP. Add `"macros"` later if needed. + +**A2: `RustImplScope` ownership semantics are unclear.** +The design says "methods declared inside are added to the target type's `members` space." But how does `RustImplScope` get the target type? By refkey? The scope is created with `ownerSymbol`, but the target type may not be in the same file. If the `ImplBlock` references a struct via refkey, that struct's symbol must be resolved before the scope is created. This temporal dependency is not addressed. + +**A3: `receiver` prop default behavior is undecided.** +Open question 1 asks: "Should methods in impl blocks auto-get `&self`?" The recommendation is "yes" but the answer is listed as "must resolve before T021" — while T021 is the task that _implements_ it. This is circular. The decision should be made in the design doc, not left to the implementing agent. + +**A4: `CrateDirectory` vs `ModuleDirectory` vs `SourceFile` — who renders `mod` declarations?** +T025 says "Update `SourceFile` to auto-generate `mod` declarations." But T009 says `CrateDirectory` creates the crate scope. The `mod` declarations need to go in the _root source file_ (`lib.rs`). Does `CrateDirectory` create `lib.rs` implicitly, or does the user create it? If the user creates ``, how does it know to render mod declarations? + +**A5: Struct variant fields in `EnumVariant` — confusing prop names.** +`EnumVariantProps` has `fields?: Children[]` for tuple variant and `children?: Children` for struct variant. The distinction between "fields" and "children" is confusing — both represent variant data. This could cause agent implementation errors. + +--- + +# 5. Architectural Risks + +**R1: Scopes in `symbols/` instead of `scopes/`.** +The proposed layout puts all scope classes in `src/symbols/`. But the actual Go package puts scopes in `src/scopes/` (a separate directory), and C# does the same. The symbols directory has 7 files in Go, scopes has 10 files. Mixing them would create an oversized directory. The design doc (03) proposes `symbols/` for everything; this is inconsistent with Go and C#. +_Evidence:_ `packages/go/src/scopes/` (10 files), `packages/csharp/src/scopes/` (9 files). +_Recommendation:_ Use a separate `src/scopes/` directory. + +**R2: `SymbolCreator` protocol claim is incorrect.** +The design doc (03, section 3.10) and patterns doc (02, section 5.8) say external dependency descriptors use the "SymbolCreator protocol." In reality, neither Go's `create-module.ts` nor C#'s `create-library.ts` use a formal `SymbolCreator` interface. They use `REFKEYABLE` + `TO_SYMBOL` symbols from core and call `createSymbol()` directly with a `WeakMap` for per-binder caching. Task T028 should reference the actual pattern, not the claimed "SymbolCreator protocol." +_Evidence:_ `packages/go/src/create-module.ts`, `packages/csharp/src/create-library.ts`. + +**R3: `Reference` component is passed to `CoreSourceFile`, not set as a context.** +The design doc (03, section 3.3) says language packages "provide a Reference component set on `SourceFile`'s `reference` prop." This is correct — it's a _prop_, not a context. But the backlog tasks (T009, T010) don't clearly describe how the Rust `Reference` component is connected to `CoreSourceFile`. The Go pattern is: ``. +_Implication:_ T009 must explicitly pass the `Reference` component as a prop to core's SourceFile. + +**R4: Factory functions assume scope availability.** +T006 (factory functions) says "Get the current scope via `useRustScope()`." But this only works inside a reactive component context. Factory functions called outside a component tree will fail silently. Go's factories have the same pattern, but this is a footgun. The implementation must validate scope availability and emit diagnostics if called in the wrong context. + +--- + +# 6. Scope Risks + +**S1: MVP may be slightly too large.** +The MVP includes `TraitDeclaration`, `ImplBlock`, and `self` receiver — these are the most architecturally novel features with no Alloy precedent. If these prove harder than expected, they could delay the entire MVP. Consider making Phase 3 (traits/impl) optional for a "Phase 0 MVP" that ships struct+enum+function without traits. + +**S2: `CargoTomlFile` TOML rendering is underestimated.** +TOML has specific formatting rules (inline tables vs standard tables, array of tables). `Cargo.toml` with features (`serde = { version = "1.0", features = ["derive"] }`) requires inline table syntax. The task description says "string-based rendering" — this is viable but fragile. Consider a small TOML serialization helper. + +**S3: `use` tree grouping (`use path::{A, B}`) is complex for MVP.** +The design recommends tree syntax. This requires grouping logic: collect all imports, group by common prefix, render `use` trees. Flat `use path::A; use path::B;` would be simpler and still correct (rustfmt will group them). Consider flat for MVP, tree for post-MVP. + +--- + +# 7. Testing Risks + +**T1: No `cargo check` validation.** +The plan acknowledges this but doesn't mitigate it. Golden scenario tests compare strings, but subtle syntax errors (missing semicolons, wrong brace placement) won't be caught. Consider adding a single integration test that runs `cargo check` on the golden scenario output (if Rust toolchain is available in CI). + +**T2: Cross-module tests depend on 4+ tasks completing correctly.** +T026 (import integration tests) depends on T022, T023, T025 — if any of those is buggy, T026 tests will fail for non-obvious reasons. The test strategy should include intermediate unit tests for `RustModuleScope.addUse()` independently of the Reference component. + +**T3: No negative tests.** +The test plan covers correct behavior but not error cases. What happens when: + +- A refkey references a non-existent symbol? +- A private symbol is referenced from another module? +- A `use` would be circular? + At minimum, the visibility error case should be tested (like Go's uppercase export check). + +**T4: `test/utils.tsx` bootstrapping problem.** +T002 creates test utilities, but `SourceFile` and `CrateDirectory` don't exist yet (created in T009). The initial `toSourceText()` must use core components as stubs — but T002 says "update in T009." This creates a fragile intermediate state where tests in T003–T008 may need different utils than T009+ tests. + +--- + +# 8. Backlog Risks + +**B1: T011 (StructDeclaration) has too many dependencies.** +T011 lists dependencies on T006, T010, T015, T016, T017 — that's 5 deps. T015 (Attribute) and T016 (DocComment) are needed because StructDeclaration uses `derives` and `doc` props. But this means T011 can't start until attributes _and_ doc comments are done. Consider: T011 should implement the core struct rendering first, with derives/doc as optional props that do nothing if the attribute/doc components aren't loaded yet. Then T015/T016 tests verify integration. + +**B2: T005 (scope hierarchy) is too large.** +T005 creates 6 scope classes + scope hooks in one task. This is the biggest single task and the most architecturally critical. It should be split: + +- T005a: `RustCrateScope` + `RustModuleScope` (with import/mod tracking) — the complex ones. +- T005b: `RustFunctionScope` + `RustLexicalScope` — simple. +- T005c: `RustImplScope` + `RustTraitScope` — member scopes. +- T005d: Scope hooks. + +**B3: T022 (reference resolution) is the riskiest task and has no sub-breakdown.** +This single task must: implement `ref()`, resolve refkeys, build use paths, determine same-module vs same-crate vs external, handle prelude, call `addUse()`. This is the most complex task in the entire backlog. It should be split into at least 2 tasks: (a) same-crate resolution and (b) external-crate resolution. + +**B4: Missing task: `vitest.setup.ts` content.** +Go's `vitest.setup.ts` is minimal (just imports `@alloy-js/core/testing`). But the Go `vitest.config.ts` references it via `setupFiles: ["./test/vitest.setup.ts"]`. T001 creates vitest.config.ts but T002 creates vitest.setup.ts. If T001's vitest.config.ts references a file that doesn't exist yet, the build may fail between T001 and T002. + +**B5: T032 (STC wrappers) underestimates scope.** +Go wraps 24 components. The Rust package will have ~15-20 components. Each STC wrapper must be typed correctly. This is tedious but straightforward. The task should note the expected component count. + +**B6: Missing task: barrel index files.** +T033 says "verify barrel exports" but there's no task to _create_ `src/components/index.ts` or `src/symbols/index.ts` as barrel files. These are incrementally built as components are added, but no task explicitly owns creating the initial barrel structure. + +--- + +# 9. Recommended Corrections + +## Must Fix (before implementation starts) + +1. **Add `api-extractor.json` and build script alignment to T001.** Include `api-extractor.json`, `alloy build --with-dev`, `prepack` script, and `#imports` hash pattern. Reference Go's `package.json` exactly. + +2. **Move scopes to `src/scopes/`** matching Go and C# patterns. Update the proposed layout in the design doc and all backlog task file paths. + +3. **Fix the SymbolCreator protocol claim.** Update T028 (createCrate) context files and implementation guidance to reference the actual `REFKEYABLE` + `TO_SYMBOL` + `WeakMap` pattern used by Go and C#, not a non-existent "SymbolCreator protocol." + +4. **Simplify declaration spaces to `["types", "values"]`** for MVP module scopes. Remove `"macros"` space — it adds complexity without demonstrated need. + +5. **Split T005** into at least 2 tasks: (a) module/crate scopes with tracking, (b) function/lexical/impl/trait scopes with hooks. + +6. **Add a name conflict resolver task** (new T-number) — don't rely on core's default. At minimum, Rust `use` imports should be renamed before local declarations on conflict. + +7. **Resolve the `receiver` prop decision in the design doc** — don't leave it as an open question for the implementation agent. Recommendation: default `&self` inside impl blocks, `none` for standalone functions, overridable via prop. + +8. **Define the prelude type list** in either the design doc or T029 (std builtins). This is a concrete, enumerable list — not something to leave undefined. + +## Should Fix (improve quality) + +9. **Reduce T011 dependencies.** Make `derives` and `doc` props render `null` if Attribute/DocComment components aren't available yet. T011 should only depend on T006 and T010. + +10. **Split T022 (reference resolution)** into same-crate and external-crate sub-tasks. + +11. **Add T001 → T002 ordering note:** T001's `vitest.config.ts` should either not reference `setupFiles` initially (add in T002), or T001 and T002 should be a single task. + +12. **Consider flat `use` statements for MVP** (`use path::A;` per item). Tree grouping (`use path::{A, B};`) can be a polish task. This reduces complexity in T023. + +13. **Add at least one negative test** for private symbol visibility errors to T026 or T035. + +## Nice to Have + +14. Add an optional integration test that runs `cargo check` on golden scenario output. + +15. Add intermediate unit tests for `RustModuleScope.addUse()` independent of the Reference component. + +16. Consider whether `tsconfig.base.json` references need updating (currently only 6 projects are listed — Go/Python/C# are not there, suggesting it's optional). + +--- + +# 10. Verdict + +## **Ready with minor revisions** + +The planning set is comprehensive, well-reasoned, and largely correct. The identified issues are **fixable without rethinking the architecture**. The 9 "must fix" items are concrete and can be addressed in a focused revision pass before implementation begins. + +**Key strengths:** Accurate Rust semantics, correct dependency ordering, thorough backlog with agent-ready task specs. +**Key weaknesses:** Missing infrastructure details (api-extractor, build scripts), incorrect directory layout (scopes in symbols/), a few oversized tasks that should be split, and some decisions left unresolved that will confuse implementing agents. + +After the corrections in section 9 are applied, this plan is **ready for implementation**. diff --git a/docs/language-packages/rust/07-revision-summary.md b/docs/language-packages/rust/07-revision-summary.md new file mode 100644 index 000000000..299dd7859 --- /dev/null +++ b/docs/language-packages/rust/07-revision-summary.md @@ -0,0 +1,80 @@ +# Revision Summary + +## What Changed + +### Design Doc (03-rust-design-notes.md) + +| Change | Why | +| ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Declaration spaces changed from `["types", "values", "macros"]` to `["types", "values"]` | Review G3/A1: "macros" space adds complexity without demonstrated need for MVP. Go uses dual spaces without macros. Can be added later. | +| Directory layout: scopes moved from `src/symbols/` to `src/scopes/` | Review R1: Go and C# both use separate `scopes/` directories. Mixing 14+ files into `symbols/` creates an oversized directory. | +| Added `name-conflict-resolver.ts` to layout | Review G7: TypeScript has a custom resolver; Rust needs one too for imported vs local symbol priority. | +| Component file names changed from PascalCase to kebab-case | Consistency with Alloy component file naming convention. | +| Removed `StaticDeclaration.tsx`, `MatchExpression.tsx`, `DeriveAttribute.tsx` (merged into `attribute.tsx`) | Simplified component set. Static decl deferred. Match deferred. DeriveAttribute is a sub-component of Attribute. | +| Added section 3.14: Prelude Types with full list | Review G6: Prelude list was undefined. Now explicitly enumerated (~55 types). | +| Added section 3.15: Name Conflict Resolver | Review G7: Design decision documented. Import symbols renamed before local declarations. | +| Resolved self receiver design decision | Review A3: Default `&self` inside impl/trait, `none` for standalone fns. Overridable via `receiver` prop. No longer an open question. | +| Fixed SymbolCreator protocol claim | Review R2: Changed to accurately describe the `REFKEYABLE` + `TO_SYMBOL` + `WeakMap` pattern. | + +### PRD (04-rust-prd.md) + +| Change | Why | +| ------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| FR-3.1/FR-3.2: declaration spaces `["types", "values"]` | Matches design doc change. | +| Architecture: scopes in `src/scopes/` directory | Matches design doc change. | +| Open questions: 5 of 6 resolved | Review A3: Decisions made — no longer blocking implementation agents. | +| `use` syntax changed to flat for MVP | Review S3/should-fix #12: `use path::A;` per item is simpler. Tree grouping deferred to polish. | + +### Backlog + +| Change | Why | +| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | +| T001: Added api-extractor.json, build scripts, prepack, #imports | Review G1/G2/G3/G5: Missing infrastructure items that every existing package has. | +| T005: Split into T005 (crate/module scopes) + T005b (function/lexical/member scopes) | Review B2: Original T005 was too large (6 scopes + hooks). Now split into manageable pieces. | +| T007b: New task — name conflict resolver | Review G7: Core default is insufficient. Rust needs import-priority conflict resolution. | +| T011: Dependencies reduced from 5 to 2 (T006, T010 only) | Review B1: T015/T016/T017 were soft deps. Struct renders without derives/doc initially. | +| T022: Added PRELUDE_TYPES list to scope | Review G6: Exact prelude types now specified for the implementation agent. | +| T023: Changed from tree grouping to flat `use` statements | Review S3: Simpler for MVP. Tree grouping moved to post-MVP. | +| T026: Added negative test for private symbol visibility | Review T3: No negative tests existed. | +| T035: Added prelude type and private symbol edge cases | Review T3: Expanded edge case coverage. | +| E002 epic: Updated task list with T005b, T007b | Reflects new tasks. | +| P01 phase: Updated task table | Reflects new tasks. | +| Agent rules: Updated sequential chains | Reflects T005→T005b chain. | +| Index: Updated task table with T005b, T007b | Reflects new tasks. Total now 37 tasks. | + +## Why It Changed + +All changes address concrete findings from the architecture review (`06-review.md`). The review cross-referenced the planning docs against actual repository code and found: + +1. **Factual inaccuracies** — scopes directory location, SymbolCreator protocol, missing infrastructure files. +2. **Unresolved decisions** — self receiver, use syntax, prelude handling were left as open questions but must be decided before agents implement. +3. **Oversized tasks** — T005 was one task for 6 scopes; now split into 2 manageable pieces. +4. **Missing tasks** — name conflict resolver, api-extractor config. +5. **Over-engineering** — triple declaration spaces and tree use grouping added complexity without MVP justification. + +## Remaining Unresolved Questions + +1. **Trailing commas in multi-line contexts** — Recommend yes (matches rustfmt). Low risk, can change later. +2. **`tsconfig.base.json` references** — Go/Python/C# are not in the base tsconfig references. This appears optional. Investigate during T001 if build issues arise. +3. **Integration test with `cargo check`** — Nice to have. Can be added post-MVP if Rust toolchain is available in CI. + +## Is the Plan Ready for Implementation? + +**Yes.** All "must fix" items from the review have been addressed: + +- [x] api-extractor.json and build scripts added to T001 +- [x] Scopes moved to `src/scopes/` +- [x] SymbolCreator protocol claim fixed +- [x] Declaration spaces simplified to `["types", "values"]` +- [x] T005 split into T005 + T005b +- [x] Name conflict resolver added (T007b) +- [x] Receiver prop decision resolved in design doc +- [x] Prelude type list defined + +All "should fix" items have also been addressed: + +- [x] T011 dependencies reduced +- [x] Flat `use` statements for MVP +- [x] Negative tests added to T026 and T035 + +The plan is **ready for implementation** starting with T001 (package scaffold). diff --git a/docs/language-packages/rust/08-v1-plan.md b/docs/language-packages/rust/08-v1-plan.md new file mode 100644 index 000000000..111ac2aa1 --- /dev/null +++ b/docs/language-packages/rust/08-v1-plan.md @@ -0,0 +1,224 @@ +# @alloy-js/rust — v1 Plan + +**Date:** 2026-03-11 +**Status:** Draft +**Scope:** MVP → v1 gap closure + +--- + +## 1. Current State + +The Rust package has completed 34 of 35 MVP backlog tasks (T001–T035). All core components, symbol system, scope hierarchy, name policy, reference resolution, import generation, Cargo.toml generation, and test infrastructure are implemented and passing. + +**One task remains blocked:** + +| Task | Status | Reason | +| ------------------- | ------- | -------------------------------------------------------------------------- | +| T029 (std builtins) | blocked | TS2742 / API Extractor export typing portability failures after 3 attempts | + +--- + +## 2. Gap Analysis + +### 2.1 Blocked: std Builtins (T029) + +The `std` crate descriptor (`src/builtins/std.ts`) failed to build due to TypeScript/API Extractor portability errors on the exported `std` typing. The `createCrate()` infrastructure works (T028 done, tested with `serde`), but the specific type shape of `std` triggers TS2742. + +**Root cause hypothesis:** The `std` descriptor is large (many modules, many symbols) and the inferred return type of `createCrate()` with deeply nested generics exceeds API Extractor's portability check threshold. Go solved this with explicit exported type aliases for the export surface. + +**Fix approach:** Use explicit type annotations on the exported `std` constant rather than relying on type inference, per the custom instruction: _"Prefer explicit exported type aliases/interfaces for the export surface."_ + +### 2.2 Design Gap: `std` Should Not Appear in Cargo.toml + +**This is a new finding not covered by any existing task.** + +`std` is implicit in Rust — it must never appear in `Cargo.toml` `[dependencies]`. But the current `createCrate()` → `ref()` → `addDependency()` pipeline unconditionally adds every external crate to `RustCrateScope.dependencies`, which gets rendered in Cargo.toml. + +Go solves this with a `builtin: boolean` flag in `createModule()` that propagates to `PackageSymbol`. Rust needs equivalent handling. + +**Evidence:** + +- `packages/rust/src/symbols/reference.ts` line 101: `sourceCrate.addDependency(targetCrate.name, ...)` — called for ALL external crates +- `packages/go/src/create-module.ts` line 102: `builtin: boolean = false` parameter +- `packages/rust/src/scopes/rust-crate-scope.ts`: no builtin/implicit concept exists + +**Impact:** Without this fix, referencing `std::collections::HashMap` via builtins would add `std = "*"` to Cargo.toml. + +### 2.3 Incomplete STC Wrappers + +T032 (STC wrappers) is marked done but only wraps 12 of ~24 components. Go wraps all 28 of its components. Missing wrappers: CrateDirectory, SourceFile, ModuleDirectory, CargoTomlFile, ModuleDocComment, Declaration, Reference, UseStatement, UseStatements, Parameters, TypeParameters, Value, ModDeclarations. + +**Note:** Not all of these may need wrappers — structural/container components may not benefit from STC. But Go wraps SourceFile, ModuleDirectory, and Reference, so at minimum those should be covered. + +### 2.4 CrateDirectory `crateType` Prop + +The PRD (Open Question 5, resolved) specifies `crateType?: "lib" | "bin"` support. Currently, CrateDirectory has no such prop. However, users can already create `` and it works correctly — `isModuleRootPath()` in `source-file.tsx` recognizes `main.rs`. + +The prop would add: + +- Semantic clarity (explicit lib vs bin intent) +- Potential future Cargo.toml `[lib]`/`[[bin]]` section generation + +T030 (Cargo.toml) explicitly deferred `[lib]`/`[[bin]]` as out-of-scope. This is low priority. + +### 2.5 Positive Deviation: Use-Tree Grouping Already Works + +The PRD revision (07-revision-summary.md) changed use statements to "flat for MVP." The implementation already supports brace grouping (`use path::{A, B};`), tested in `imports.test.tsx`. This is ahead of PRD — no work needed. + +### 2.6 No Additional Gaps Found + +The following are confirmed complete and tested: + +- Prelude type import suppression: 60-type `PRELUDE_TYPES` set in `reference.ts`, tested in `imports.test.tsx` +- External crate e2e pipeline: tested in `create-crate.test.tsx` and `golden-scenarios.test.tsx` +- Name conflict resolver: implemented and tested +- api-extractor.json, build scripts, prepack: all configured +- All 32 test files passing + +--- + +## 3. v1 Task Plan + +### New Tasks + +| ID | Title | Type | Priority | Dependencies | Blocks | +| ---- | ---------------------------------------- | ------- | -------- | ------------ | ------ | +| T036 | Builtin crate support in createCrate/ref | feature | P0 | T028 | T029 | +| T037 | Complete STC wrappers | feature | P2 | T032 | — | +| T038 | CrateDirectory crateType prop | feature | P3 | T009 | — | + +### Existing Task Update + +| ID | Title | Change | +| ---- | ------------ | ------------------------------------------------------------------- | +| T029 | std builtins | Unblock: depends on T036; fix TS2742 with explicit type annotations | + +### Critical Path + +``` +T036 (builtin flag) → T029 (std builtins, unblocked) → barrel export update → final validation +T037 (STC wrappers) — parallel, independent +T038 (crateType) — parallel, independent +``` + +--- + +## 4. Task Details + +### T036: Builtin Crate Support in createCrate / ref + +**Problem:** `createCrate()` has no way to mark a crate as builtin/implicit. Referencing symbols from such a crate should generate `use` statements but NOT add the crate to `Cargo.toml` dependencies. + +**Scope:** + +1. Add `builtin?: boolean` option to `CrateDescriptor` interface in `create-crate.ts` +2. Store the `builtin` flag on the `CrateFactoryState` so it can be queried +3. Expose a `isBuiltin(crate: ExternalCrate): boolean` or similar helper +4. In `reference.ts` `ref()` function: skip `sourceCrate.addDependency()` call when the target crate is marked builtin +5. Add test: create a builtin crate, reference its symbol, verify `use` is generated but NO Cargo.toml dependency appears + +**Files to modify:** + +- `packages/rust/src/create-crate.ts` — add `builtin` to descriptor, store on state, expose check +- `packages/rust/src/symbols/reference.ts` — conditional `addDependency()` call +- `packages/rust/test/create-crate.test.tsx` — add builtin crate test + +**Acceptance criteria:** + +- `createCrate({ name: "std", builtin: true, ... })` works +- Referencing a symbol from a builtin crate generates `use std::collections::HashMap;` +- Builtin crate does NOT appear in `RustCrateScope.dependencies` +- Existing non-builtin crate behavior is unchanged +- Build passes: `pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test` + +### T029: std Builtins (Unblocked) + +**Change from original:** Now depends on T036 (builtin flag). Use `createCrate({ name: "std", builtin: true, ... })`. + +**Additional fix for TS2742:** Use explicit exported type alias for the `std` export surface rather than relying on inferred types from `createCrate()`. Pattern: + +```typescript +// Explicit type to avoid TS2742/API Extractor portability issues +export type StdCrate = CrateRef & + SymbolCreator & + ExternalCrate; +export const std: StdCrate = createCrate(stdDescriptor); +``` + +**Acceptance criteria (same as original T029 plus):** + +- `std` does NOT appear in Cargo.toml dependencies +- Prelude types (Option, Vec, String, etc.) referenced via `std[""].Option` generate no `use` statement +- Non-prelude types (HashMap, Display, etc.) referenced via `std.collections.HashMap` generate correct `use std::collections::HashMap;` + +### T037: Complete STC Wrappers + +**Problem:** Only 12 of ~24 components have STC wrappers. Go wraps all 28 components. + +**Scope:** Add STC wrappers for components that benefit from programmatic construction. At minimum: + +- `ModuleDocComment` (parallels DocComment which is already wrapped) +- `SourceFile`, `ModuleDirectory`, `CrateDirectory` (Go wraps equivalents) +- `Reference` (Go wraps this) +- `Parameters`, `TypeParameters`, `Value` (leaf components useful in STC) + +Skip internal/rendering-only components: `UseStatement`, `UseStatements`, `ModDeclarations`, `Declaration` (these are auto-rendered, not user-constructed). + +**Files to modify:** + +- `packages/rust/src/components/stc/index.ts` +- `packages/rust/test/stc.test.tsx` + +**Acceptance criteria:** + +- All user-facing components have STC wrappers +- STC test file updated and passing +- Build passes + +### T038: CrateDirectory crateType Prop + +**Problem:** No explicit lib/bin crate type distinction. + +**Scope:** + +1. Add `crateType?: "lib" | "bin"` to `CrateDirectoryProps` (default `"lib"`) +2. Add `crateType` to `CrateContextValue` +3. Optionally add Cargo.toml `[lib]`/`[[bin]]` section rendering (stretch) + +**Files to modify:** + +- `packages/rust/src/components/crate-directory.tsx` +- `packages/rust/src/context/crate-context.tsx` +- `packages/rust/test/source-file-crate-directory.test.tsx` + +**Acceptance criteria:** + +- `` sets context correctly +- Users can create `` inside a bin crate (already works) +- Context exposes `crateType` for downstream components +- Build passes + +--- + +## 5. Estimated Effort + +| Task | Complexity | Risk | +| -------------------------- | ------------------------------------------------------ | --------------------------------------- | +| T036 (builtin flag) | Low — ~4 touch points, well-understood pattern from Go | Low | +| T029 (std builtins, retry) | Medium — large descriptor, TS2742 mitigation needed | Medium (TS2742 was previously blocking) | +| T037 (STC wrappers) | Low — mechanical, follow existing pattern | Very low | +| T038 (crateType prop) | Low — additive prop, no breaking changes | Very low | + +--- + +## 6. Definition of Done (v1) + +All of the following must be true: + +1. T036 implemented and tested (builtin crate support) +2. T029 unblocked and completed (std builtins) +3. T037 completed (STC wrappers) +4. T038 completed or explicitly deferred (crateType) +5. `pnpm --filter @alloy-js/rust build` passes (includes API Extractor) +6. `pnpm --filter @alloy-js/rust test` passes (all 32+ test files) +7. Barrel exports include builtins: `import { std } from "@alloy-js/rust"` works diff --git a/eng/ralph-logger.mjs b/eng/ralph-logger.mjs new file mode 100644 index 000000000..be1e89036 --- /dev/null +++ b/eng/ralph-logger.mjs @@ -0,0 +1,262 @@ +#!/usr/bin/env node + +/** + * Ralph Parallel — Structured JSONL Logger + * + * Provides a shared logging layer for all Ralph orchestrator components. + * Each component gets its own JSONL log file with structured entries. + * + * Usage: + * import { createLogger, flushAll, setLogDir, setRetentionDays, rotateAllLogs } from './ralph-logger.mjs'; + * const log = createLogger('orchestrator'); + * log.info('worker:assigned', { workerId: '1', taskId: 'M7-042' }); + * log.error('merge:conflict', { branch: 'ralph/worker-1', files: ['src/foo.ts'] }); + * // On shutdown: + * flushAll(); + * + * Log format (one JSON object per line): + * {"ts":"2026-03-09T23:24:56.443Z","level":"info","component":"orchestrator","event":"worker:assigned","ctx":{"workerId":"1"}} + * + * @see docs/design-decisions/ (parallel ralph loop) + */ + +import { + appendFileSync, + existsSync, + mkdirSync, + readdirSync, + statSync, + unlinkSync, +} from "fs"; +import { resolve } from "path"; + +// ── Configuration ─────────────────────────────────────────────────────── + +let logDir = resolve(process.cwd(), "eng/logs"); +let retentionDays = 7; + +/** + * Override the log output directory. + * @param {string} dir — absolute path to log directory + */ +export function setLogDir(dir) { + logDir = dir; +} + +/** + * Set log retention period in days. + * @param {number} days + */ +export function setRetentionDays(days) { + retentionDays = days; +} + +// ── Date helpers ──────────────────────────────────────────────────────── + +function isoDate() { + return new Date().toISOString().slice(0, 10); // YYYY-MM-DD +} + +function isoTimestamp() { + return new Date().toISOString(); +} + +function timestampForFile() { + return new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19); +} + +// ── Logger registry ───────────────────────────────────────────────────── + +/** @type {Map} */ +const loggers = new Map(); + +/** + * Get or create a logger for a component. + * Loggers are singletons per component name — calling createLogger('merge') + * twice returns the same instance. + * + * @param {string} component — e.g., 'orchestrator', 'merge', 'tasks' + * @returns {RalphLogger} + */ +export function createLogger(component) { + if (loggers.has(component)) { + return loggers.get(component); + } + const logger = new RalphLogger(component); + loggers.set(component, logger); + return logger; +} + +/** + * Flush all loggers (call on graceful shutdown). + * Currently a no-op since we use synchronous appends, but exists as a + * hook point in case we switch to buffered writes for performance. + */ +export function flushAll() { + // Synchronous writes don't need explicit flushing, but iterate + // loggers in case any have buffered state in the future. + for (const logger of loggers.values()) { + logger.flush(); + } +} + +/** + * Rotate (delete) old log files across all known JSONL and JSON patterns. + * Extends the existing cleanOldLogs() from ralph.mjs to cover new file types. + */ +export function rotateAllLogs() { + if (!existsSync(logDir)) return; + + const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1000; + let deleted = 0; + + try { + const files = readdirSync(logDir); + for (const file of files) { + // Match patterns: *.jsonl, *.json, ralph-*.log, validation-*.log + const isRotatable = + file.endsWith(".jsonl") || + file.endsWith(".json") || + (file.startsWith("ralph-") && file.endsWith(".log")) || + (file.startsWith("validation-") && file.endsWith(".log")) || + (file.startsWith("tui-dump-") && file.endsWith(".json")); + + if (!isRotatable) continue; + + const filePath = resolve(logDir, file); + try { + const mtime = statSync(filePath).mtimeMs; + if (mtime < cutoff) { + unlinkSync(filePath); + deleted++; + } + } catch { + // File may have been deleted between readdir and stat + } + } + } catch { + // Log dir may not exist yet + } + + return deleted; +} + +// ── Logger Class ──────────────────────────────────────────────────────── + +class RalphLogger { + /** + * @param {string} component — logical component name (used in filename and entries) + */ + constructor(component) { + this.component = component; + this._currentDate = null; + this._filePath = null; + } + + /** + * Resolve the current log file path. + * Creates a new file per day: {component}-YYYY-MM-DD.jsonl + */ + _getFilePath() { + const today = isoDate(); + if (this._currentDate !== today) { + this._currentDate = today; + mkdirSync(logDir, { recursive: true }); + this._filePath = resolve(logDir, `${this.component}-${today}.jsonl`); + } + return this._filePath; + } + + /** + * Write a structured log entry. + * + * @param {'debug'|'info'|'warn'|'error'} level + * @param {string} event — dot-separated event name (e.g., 'worker:assigned') + * @param {object} [ctx] — arbitrary context data + */ + _write(level, event, ctx) { + const entry = { + ts: isoTimestamp(), + level, + component: this.component, + event, + }; + if (ctx !== undefined && ctx !== null) { + entry.ctx = ctx; + } + + try { + appendFileSync( + this._getFilePath(), + JSON.stringify(entry) + "\n", + "utf-8", + ); + } catch { + // Non-fatal — don't crash the orchestrator because of a log write failure + } + } + + debug(event, ctx) { + this._write("debug", event, ctx); + } + + info(event, ctx) { + this._write("info", event, ctx); + } + + warn(event, ctx) { + this._write("warn", event, ctx); + } + + error(event, ctx) { + this._write("error", event, ctx); + } + + /** + * Flush any buffered state (currently no-op for sync writes). + */ + flush() { + // Reserved for future buffered mode + } +} + +// ── Utility: Write a one-shot JSON file ───────────────────────────────── + +/** + * Write a JSON object to a file in the log directory. + * Useful for run summaries and TUI dumps. + * + * @param {string} prefix — filename prefix (e.g., 'run-summary', 'tui-dump') + * @param {object} data — JSON-serializable object + * @returns {string} absolute path to the written file + */ +export function writeJsonLog(prefix, data) { + mkdirSync(logDir, { recursive: true }); + const filePath = resolve(logDir, `${prefix}-${timestampForFile()}.json`); + try { + const content = JSON.stringify(data, null, 2) + "\n"; + appendFileSync(filePath, content, "utf-8"); + } catch { + // Non-fatal + } + return filePath; +} + +/** + * Write raw text to a file in the log directory. + * Useful for validation output. + * + * @param {string} prefix — filename prefix (e.g., 'validation') + * @param {string} content — text content + * @returns {string} absolute path to the written file + */ +export function writeTextLog(prefix, content) { + mkdirSync(logDir, { recursive: true }); + const filePath = resolve(logDir, `${prefix}-${timestampForFile()}.log`); + try { + appendFileSync(filePath, content, "utf-8"); + } catch { + // Non-fatal + } + return filePath; +} diff --git a/eng/ralph.md b/eng/ralph.md new file mode 100644 index 000000000..85d362bee --- /dev/null +++ b/eng/ralph.md @@ -0,0 +1,226 @@ +Complete ONE task per loop. After completion, exit the copilot CLI. NEVER work on a second task. + +## Project: @alloy-js/rust + +You are building `@alloy-js/rust`, a new Alloy language package for generating +Rust source code. All work is tracked in `docs/backlog/`. Supporting design +documents live in `docs/language-packages/rust/`. + +## Available tooling beyond subagents + +Use these tools directly from the main context when they provide faster or +more precise results than spawning a subagent: + +- **TypeScript LSP** — use `goToDefinition`, `findReferences`, + `goToImplementation`, `hover`, `documentSymbol`, `workspaceSymbol`, + `incomingCalls`, `outgoingCalls`, and `rename` for precise code navigation. + Prefer LSP over grep when looking up symbols, call sites, or type info. +- **GitHub MCP** — use for issue/PR lookups and repository search. + +## Context Stack — loaded deterministically every loop + +These files form the context stack that is loaded at the start of every +iteration. Load the same stack every loop to ensure consistency: + +- **`.github/copilot-instructions.md`** — loaded automatically by Copilot. + Alloy JSX conventions, component structure rules, and quality bar. +- **`docs/backlog/index.md`** — read via subagent. The full task index with + status tracking, dependency graph, and phase ordering for `@alloy-js/rust`. +- **`docs/backlog/agents/execution-rules.md`** — read via subagent. Rules for + task readiness, sequential chains, parallel-safe groups, and quality gates. +- **`docs/language-packages/rust/`** — design documents (core understanding, + existing language patterns, Rust design notes, PRD). Load the relevant + document(s) for the task you are working on. + +> **Your primary context window is a scheduler.** Do NOT read large files +> directly. Use subagents for all exploration, file reading, and searching. +> Reserve the main context for decision-making and orchestration. + +--- + +## Phase 1: ORIENT — Pick the next task + +1. Use a subagent to read `docs/backlog/index.md` and identify pending tasks. +2. Use a subagent to read `docs/backlog/agents/execution-rules.md` for + readiness rules and sequential/parallel constraints. +3. **Pre-flight check:** Use a single subagent to run: + ```bash + pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test + ``` + If the build or tests fail BEFORE you start your work: + - **Search existing tasks first.** Use a subagent to check whether + `docs/backlog/tasks/` already contains a task tracking the same + failure (match on the failing test name and error signature). If a + matching task exists, do NOT create a duplicate — instead, add a + short "Still failing as of " note to the existing task, commit, + push, and **exit**. + - Only create a new task file at `docs/backlog/tasks/-.md` + if no existing task covers the failure. + - Update `docs/backlog/index.md` to include the new task. + - Commit, push, and **exit**. Do not work on anything else. +4. If pre-flight passes, choose the highest-priority `pending` task whose + dependencies are all `done`. **You decide** what has the highest priority — + not necessarily the first item. Use the Priority column (P0 > P1 > P2) + and the critical path from the backlog index. +5. If a task should be split into smaller tasks, split it: create the new + task files in `docs/backlog/tasks/`, update `docs/backlog/index.md`, commit, + push, and **exit** (splitting counts as your one task). + +--- + +## Phase 2: STUDY — Research before coding + +Use up to 500 parallel subagents to study the codebase, documents, +dependencies, or web resources. **Do NOT assume something is not +implemented** — always search first. Think hard about what you find. + +1. Read the task document fully (`docs/backlog/tasks/.md`). +2. Read all context files listed in the task's "Context Files" section. +3. Search the codebase for existing implementations related to your task. + Pay special attention to how `@alloy-js/typescript` implements analogous + features — it is the primary reference package. +4. Study how related code is structured and what patterns it follows. +5. If the task is already done, mark it as `done` in the task file and in + `docs/backlog/index.md`, commit, push, and exit. +6. Read relevant design documents from `docs/language-packages/rust/`: + - `01-core-understanding.md` — Alloy core concepts + - `02-existing-language-patterns.md` — patterns from TypeScript/Python packages + - `03-rust-design-notes.md` — Rust-specific design decisions + - `04-rust-prd.md` — product requirements + +--- + +## Phase 3: DESIGN — Evaluate approaches before coding + +Before writing any code, do a design review using subagents: + +1. Identify at least **2 viable approaches** for implementing the task. +2. For each approach, evaluate against these criteria (in priority order): + - Idiomatic to the Alloy codebase's existing patterns (study + `@alloy-js/typescript` and `@alloy-js/core` for reference) + - Idiomatic Rust output (generated code should look like hand-written Rust) + - Simple (KISS principle) + - Consistent with decisions in `docs/language-packages/rust/03-rust-design-notes.md` +3. Think hard. Choose the approach that best satisfies the criteria. +4. If the decision has systemic impact, record it in the Rust design notes + or create a new document in `docs/language-packages/rust/` so future loops + don't revisit the same question. + +--- + +## Phase 4: IMPLEMENT — Write code and tests + +1. All code lives in `packages/rust/` under the `@alloy-js/rust` package. +2. Components go in `packages/rust/src/components/` using kebab-case filenames. +3. Symbols go in `packages/rust/src/symbols/`. +4. Tests go in `packages/rust/test/` using Vitest with Alloy testing utilities. +5. Follow Alloy conventions from `.github/copilot-instructions.md`: + - Use `props.x` access (do NOT destructure props). + - Use `code` template tag for raw string content. + - Define `Props` interfaces for component props. + - Use `<>` instead of ``. + - Do NOT use HTML elements — use Alloy components only. +6. Tests use `toRenderTo()` matcher with `d` template tag for expected output. + Always import `"@alloy-js/core/testing"` in test files. +7. After implementing, run the tests **for just the Rust package** before + proceeding to full validation: + ```bash + pnpm --filter @alloy-js/rust test + ``` +8. Do NOT modify `@alloy-js/core` or any other existing package. +9. Do NOT update changelogs — these are managed by `npx chronus`. + +--- + +## Phase 5: VALIDATE — Back pressure + +Build and test form the **back pressure** that rejects bad code generation. +The faster this wheel turns, the better the outcomes. + +Run validation with a **single subagent** (do not fan out builds/tests to +multiple subagents — it causes conflicting backpressure): + +```bash +pnpm --filter @alloy-js/rust build && pnpm --filter @alloy-js/rust test +``` + +### What each validation step catches + +- **`build`** — TypeScript type system catches structural errors before + runtime. +- **`test`** — Vitest assertions verify correctness. This is the primary + correctness gate. + +If tests unrelated to your work fail, it is **your job** to resolve them as +part of this increment of change. Think hard when investigating these +failures — do NOT blindly update test expectations to make them pass without +being 100% certain it is the correct thing to do. + +### Stuck detection + +If validation fails **3 times** on the same issue, stop. Mark the task as +`blocked` in `docs/backlog/index.md` and the task file with a reason, +commit, push, and **exit**. Do not burn the remaining context window retrying. + +--- + +## Phase 6: RECORD — Document and commit + +1. Mark your task as `done` in both `docs/backlog/index.md` and the individual + task file. +2. If you discovered a failure mode, gotcha, or learning, record it in the + task file or update `docs/backlog/agents/execution-rules.md`. +3. If you learned something about how to build, test, or debug this project, + update `.github/copilot-instructions.md` via a subagent. Keep updates + brief and actionable. +4. `git add -A && git commit` using **conventional commit format**, then + `git push`. + - Format: `type(rust): description` — e.g., `feat(rust): add struct +declaration component`, `fix(rust): correct scope hierarchy for impl blocks`, + `test(rust): add enum variant rendering tests` + - Valid types: `feat`, `fix`, `test`, `chore`, `refactor`, `docs` + - Scope is always `rust` for `@alloy-js/rust` work. + +--- + +## Phase 7: EXIT + +Exit the copilot CLI. If the backlog is complete (no remaining `pending` +tasks), output `COMPLETED` before exiting. + +--- + +## Critical Rules (NEVER violate) + +9999\. DO NOT IMPLEMENT PLACEHOLDER, STUB, OR MINIMAL IMPLEMENTATIONS. Write +full, complete implementations. If you can't fully implement something, +create tasks for what's missing and exit. + +99999\. Use up to 500 parallel subagents for exploring, studying, or searching +code. Use only **1 subagent** for build and test operations. + +999999\. If you are stuck on a task (e.g., blocked by a missing dependency, +unclear spec, or repeated failures), mark the task as `blocked` in +`docs/backlog/index.md` and its individual task file with a reason, and exit. +Do not loop forever. + +9999999\. When you learn something new about how to build, test, or debug this +project — or discover a pattern that works well — update +`.github/copilot-instructions.md` via a subagent. Keep updates brief and +actionable. + +99999999\. When you discover a bug unrelated to your current task, create a +task in `docs/backlog/tasks/` to track it and update `docs/backlog/index.md`. +Commit and continue with your current task (do not fix unrelated bugs +in-loop unless they block your work). + +999999999\. NEVER commit secrets, API keys, passwords, or credentials. + +9999999999\. Do NOT modify `@alloy-js/core` or any package other than +`@alloy-js/rust`. If your task requires core changes, mark it as `blocked` +with an explanation and exit. + +99999999999\. Always study `@alloy-js/typescript` as the reference +implementation before building analogous features in `@alloy-js/rust`. +Follow its patterns for symbol classes, scope types, factory functions, +and component structure. diff --git a/eng/ralph.mjs b/eng/ralph.mjs new file mode 100644 index 000000000..483a2d247 --- /dev/null +++ b/eng/ralph.mjs @@ -0,0 +1,295 @@ +#!/usr/bin/env node + +import { execSync, spawn } from "child_process"; +import { + appendFileSync, + existsSync, + mkdirSync, + readFileSync, + writeFileSync, +} from "fs"; +import { resolve } from "path"; +import { rotateAllLogs, setLogDir, setRetentionDays } from "./ralph-logger.mjs"; + +const packageRoot = execSync("npm prefix", { encoding: "utf-8" }).trim(); +const logsDir = resolve(packageRoot, "eng/logs"); + +// ── Argument parsing ────────────────────────────────────────────────── + +const args = process.argv.slice(2); + +function getArg(name) { + const idx = args.indexOf(name); + return idx !== -1 && args[idx + 1] ? args[idx + 1] : undefined; +} + +const iterationsStr = getArg("--iterations") ?? "50"; +const iterations = parseInt(iterationsStr, 10); +if (isNaN(iterations) || iterations <= 0) { + console.error("Error: --iterations must be a positive number"); + process.exit(1); +} + +const model = getArg("--model"); +const promptFlag = getArg("--prompt") ?? "eng/ralph.md"; +const promptPath = resolve(packageRoot, promptFlag); +const prompt = readFileSync(promptPath, "utf-8"); + +const logRetentionDaysStr = getArg("--log-retention-days") ?? "7"; +const logRetentionDays = parseInt(logRetentionDaysStr, 10); +const autoStash = args.includes("--auto-stash"); +const progressMaxLines = 500; +const progressKeepEntries = 10; + +// ── Helpers ─────────────────────────────────────────────────────────── + +function formatDuration(ms) { + const s = Math.floor(ms / 1000); + const m = Math.floor(s / 60); + const h = Math.floor(m / 60); + if (h > 0) return `${h}h ${m % 60}m ${s % 60}s`; + if (m > 0) return `${m}m ${s % 60}s`; + return `${s}s`; +} + +function timestampForFile() { + return new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19); +} + +// ── Git state check ────────────────────────────────────────────────── + +function checkGitState(label) { + try { + const status = execSync("git status --porcelain", { + encoding: "utf-8", + cwd: packageRoot, + }).trim(); + if (status) { + const fileCount = status.split("\n").length; + console.warn( + `⚠️ [${label}] Working tree is dirty — ${fileCount} file(s) with uncommitted changes.`, + ); + if (autoStash) { + console.log("📦 Auto-stashing uncommitted changes…"); + execSync('git stash push -m "ralph-auto-stash"', { cwd: packageRoot }); + } + return true; + } + } catch { + // git not available or not a repo; silently continue + } + return false; +} + +// ── Log retention ─────────────────────────────────────────────────── + +function cleanOldLogs() { + setLogDir(logsDir); + setRetentionDays(logRetentionDays); + const deleted = rotateAllLogs(); + if (deleted > 0) { + console.log( + `🧹 Cleaned ${deleted} log file(s) older than ${logRetentionDays} days.`, + ); + } +} + +// ── Progress rotation ─────────────────────────────────────────────── + +function rotateProgress() { + const progressPath = resolve(packageRoot, "progress.md"); + if (!existsSync(progressPath)) return; + + const content = readFileSync(progressPath, "utf-8"); + const lineCount = content.split("\n").length; + if (lineCount <= progressMaxLines) return; + + const parts = content.split(/^(## .+)$/m); + // parts[0] = header, then alternating: heading, body + const header = parts[0]; + const entries = []; + for (let i = 1; i < parts.length; i += 2) { + const heading = parts[i]; + const body = i + 1 < parts.length ? parts[i + 1] : ""; + entries.push(heading + body); + } + + if (entries.length <= progressKeepEntries) return; + + const archiveEntries = entries.slice(0, -progressKeepEntries); + const recentEntries = entries.slice(-progressKeepEntries); + + const archiveDir = resolve(packageRoot, "eng/progress-archive"); + mkdirSync(archiveDir, { recursive: true }); + + const month = new Date().toISOString().slice(0, 7); // YYYY-MM + const archivePath = resolve(archiveDir, `${month}.md`); + + if (existsSync(archivePath)) { + appendFileSync( + archivePath, + "\n" + archiveEntries.join("\n") + "\n", + "utf-8", + ); + } else { + writeFileSync( + archivePath, + `# Progress Archive — ${month}\n\n` + archiveEntries.join("\n") + "\n", + "utf-8", + ); + } + + writeFileSync( + progressPath, + header + recentEntries.join("\n") + "\n", + "utf-8", + ); + + console.log( + `📋 Rotated ${archiveEntries.length} progress entries to eng/progress-archive/${month}.md`, + ); +} + +// ── Ensure logs directory exists ───────────────────────────────────── + +mkdirSync(logsDir, { recursive: true }); + +// ── Startup housekeeping ───────────────────────────────────────────── + +checkGitState("startup"); +cleanOldLogs(); + +// ── Signal handling ────────────────────────────────────────────────── + +let interrupted = false; +let activeChild = null; + +function handleSignal(signal) { + if (interrupted) return; // avoid double-handling + interrupted = true; + console.log(`\n⚡ Received ${signal} — finishing current iteration…`); + if (activeChild) { + activeChild.kill(signal); + } +} + +process.on("SIGINT", () => handleSignal("SIGINT")); +process.on("SIGTERM", () => handleSignal("SIGTERM")); + +// ── Core runner ────────────────────────────────────────────────────── + +function runCopilot(promptText) { + const copilotArgs = ["--yolo", "-p", promptText]; + if (model) { + copilotArgs.push("--model", model); + } + + return new Promise((resolve, reject) => { + const child = spawn("copilot", copilotArgs, { + stdio: ["inherit", "pipe", "pipe"], + env: process.env, + }); + + activeChild = child; + let combined = ""; + + child.stdout.setEncoding("utf8"); + child.stderr.setEncoding("utf8"); + + child.stdout.on("data", (chunk) => { + combined += chunk; + process.stdout.write(chunk); + }); + + child.stderr.on("data", (chunk) => { + combined += chunk; + process.stderr.write(chunk); + }); + + child.on("error", (err) => { + activeChild = null; + reject(err); + }); + + child.on("close", (code) => { + activeChild = null; + resolve({ code, output: combined }); + }); + }); +} + +// ── Main loop ──────────────────────────────────────────────────────── + +const globalStart = Date.now(); +let completed = 0; +let prdCompleted = false; + +console.log( + `\n🔁 Ralph Loop — ${iterations} iterations, prompt: ${promptFlag}`, +); +if (model) console.log(` Model: ${model}`); +console.log(); + +for (let i = 1; i <= iterations; i++) { + if (interrupted) break; + + const iterStart = Date.now(); + console.log( + `\n=== Iteration ${i} of ${iterations} [started ${new Date(iterStart).toLocaleTimeString()}] ===\n`, + ); + + let code, output; + try { + ({ code, output } = await runCopilot(prompt)); + } catch (err) { + console.error("\n❌ Failed to run copilot:", err?.message ?? err); + output = err?.message ?? String(err); + code = 1; + } + + const iterDuration = Date.now() - iterStart; + const totalElapsed = Date.now() - globalStart; + completed = i; + + console.log( + `\n⏱ Iteration ${i}: ${formatDuration(iterDuration)} | Total elapsed: ${formatDuration(totalElapsed)}`, + ); + + // Write iteration log file + const logFile = resolve( + logsDir, + `ralph-${timestampForFile()}-iteration-${i}.log`, + ); + try { + writeFileSync(logFile, output, "utf-8"); + console.log(`📄 Log: ${logFile}`); + } catch (err) { + console.warn(`⚠️ Could not write log file: ${err.message}`); + } + + if (output.includes("COMPLETED")) { + prdCompleted = true; + console.log("\n✅ PRD is complete! Exiting."); + break; + } + + if (code !== 0) { + console.log(`\n⚠️ Copilot exited with code ${code}`); + } + + // Post-iteration housekeeping + rotateProgress(); + checkGitState(`post-iteration ${i}`); +} + +// ── Summary ────────────────────────────────────────────────────────── + +const totalTime = Date.now() - globalStart; +console.log("\n" + "═".repeat(50)); +console.log(" Ralph Loop Summary"); +console.log("═".repeat(50)); +console.log(` Iterations completed: ${completed} / ${iterations}`); +console.log(` Total time: ${formatDuration(totalTime)}`); +console.log(` PRD completed: ${prdCompleted ? "✅ Yes" : "❌ No"}`); +if (interrupted) console.log(" Stopped by: signal (SIGINT/SIGTERM)"); +console.log("═".repeat(50) + "\n"); diff --git a/packages/rust/api-extractor.json b/packages/rust/api-extractor.json new file mode 100644 index 000000000..2069b8ac3 --- /dev/null +++ b/packages/rust/api-extractor.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", + "extends": "../../api-extractor.base.json" +} diff --git a/packages/rust/package.json b/packages/rust/package.json new file mode 100644 index 000000000..39b460a4b --- /dev/null +++ b/packages/rust/package.json @@ -0,0 +1,56 @@ +{ + "name": "@alloy-js/rust", + "version": "0.1.0", + "description": "Rust bindings for Alloy", + "repository": { + "type": "git", + "url": "git+https://github.com/alloy-framework/alloy.git" + }, + "exports": { + ".": { + "source": "./src/index.ts", + "development": "./dist/dev/src/index.js", + "import": "./dist/src/index.js" + }, + "./stc": { + "source": "./src/components/stc/index.ts", + "development": "./dist/dev/src/components/stc/index.js", + "import": "./dist/src/components/stc/index.js" + } + }, + "imports": { + "#test/*": "./test/*", + "#components/*": { + "source": "./src/components/*", + "development": "./dist/dev/src/components/*", + "default": "./dist/src/components/*" + } + }, + "scripts": { + "generate-docs": "api-extractor run", + "build": "alloy build --with-dev && pnpm run generate-docs", + "clean": "rimraf dist/ .temp/", + "test:watch": "vitest -w", + "watch": "alloy build --watch", + "test": "vitest run", + "prepack": "node ../../scripts/strip-dev-exports.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "dependencies": { + "@alloy-js/core": "workspace:~", + "change-case": "catalog:", + "pathe": "catalog:" + }, + "devDependencies": { + "@alloy-js/cli": "workspace:~", + "@alloy-js/rollup-plugin": "workspace:~", + "@microsoft/api-extractor": "catalog:", + "@rollup/plugin-typescript": "catalog:", + "concurrently": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "type": "module" +} diff --git a/packages/rust/scripts/generate-crate-descriptor.ts b/packages/rust/scripts/generate-crate-descriptor.ts new file mode 100644 index 000000000..0be7b6834 --- /dev/null +++ b/packages/rust/scripts/generate-crate-descriptor.ts @@ -0,0 +1,1000 @@ +/** + * Generic crate descriptor generator for alloy-js/rust. + * + * Takes any rustdoc JSON and generates a per-module directory of TypeScript + * files with crate descriptors compatible with `createCrate()`. Extracts + * inherent methods as members with `associated` flag for :: vs . distinction. + * + * Usage: + * npx tsx scripts/generate-crate-descriptor.ts [options] + * + * Options: + * --builtin Mark as builtin (no Cargo.toml dependency) + * --prelude Generate prelude.ts with edition-aware PRELUDE_TYPES + * --prelude-source PATH Core rustdoc JSON for resolving edition prelude globs + * --merge-from PATH Merge another crate's symbols (repeatable) + * --out PATH Output directory (default: src/builtins//) + * --skip MOD1,MOD2 Comma-separated modules to skip + * + * Examples: + * # Standard library crates + * npx tsx scripts/generate-crate-descriptor.ts core.json --builtin + * npx tsx scripts/generate-crate-descriptor.ts alloc.json --builtin + * npx tsx scripts/generate-crate-descriptor.ts std.json --builtin \ + * --prelude --prelude-source core.json \ + * --merge-from core.json --merge-from alloc.json + * + * # Third-party crates + * npx tsx scripts/generate-crate-descriptor.ts target/doc/serde.json + */ + +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; + +// --------------------------------------------------------------------------- +// CLI argument parsing +// --------------------------------------------------------------------------- + +interface CliArgs { + jsonPath: string; + builtin: boolean; + prelude: boolean; + preludeSource: string | null; // path to core.json for resolving edition prelude globs + mergeFrom: string[]; // paths to other rustdoc JSONs to merge into this crate + outPath: string | null; + skipModules: Set; +} + +function parseArgs(): CliArgs { + const args = process.argv.slice(2); + if (args.length === 0 || args[0] === "--help") { + console.error( + "Usage: npx tsx generate-crate-descriptor.ts [--builtin] [--prelude] [--prelude-source ] [--merge-from ] [--out ] [--skip ]", + ); + process.exit(args[0] === "--help" ? 0 : 1); + } + + const result: CliArgs = { + jsonPath: "", + builtin: false, + prelude: false, + preludeSource: null, + mergeFrom: [], + outPath: null, + skipModules: new Set(), + }; + + let i = 0; + while (i < args.length) { + const arg = args[i]; + if (arg === "--builtin") { + result.builtin = true; + } else if (arg === "--prelude") { + result.prelude = true; + } else if (arg === "--merge-from" && i + 1 < args.length) { + result.mergeFrom.push(args[++i]); + } else if (arg === "--prelude-source" && i + 1 < args.length) { + result.preludeSource = args[++i]; + } else if (arg === "--out" && i + 1 < args.length) { + result.outPath = args[++i]; + } else if (arg === "--skip" && i + 1 < args.length) { + for (const mod of args[++i].split(",")) { + result.skipModules.add(mod.trim()); + } + } else if (!arg.startsWith("--")) { + result.jsonPath = arg; + } else { + console.error(`Unknown option: ${arg}`); + process.exit(1); + } + i++; + } + + if (!result.jsonPath) { + console.error("Error: rustdoc JSON path is required"); + process.exit(1); + } + + return result; +} + +// --------------------------------------------------------------------------- +// Default skip list for standard library crates +// --------------------------------------------------------------------------- + +const DEFAULT_STD_SKIP = new Set([ + "os", + "simd", + "arch", + "autodiff", + "bstr", + "prelude", + "f16", + "f128", + "intrinsics", + "field", + "hint", + "unsafe_binder", + "from", + "async_iter", + "primitive", + "index", // nightly (core::ops::index re-export), collides with index.ts +]); + +// --------------------------------------------------------------------------- +// Accepted item kinds for the crate descriptor +// --------------------------------------------------------------------------- + +const ACCEPTED_KINDS = new Set([ + "struct", + "enum", + "trait", + "function", + "type_alias", + "constant", + "macro", +]); + +// --------------------------------------------------------------------------- +// Rustdoc JSON types +// --------------------------------------------------------------------------- + +interface RustdocJson { + format_version: number; + root: string; + crate_version: string | null; + index: Record; + paths: Record; + external_crates: Record; +} + +interface RustdocItem { + id: number; + crate_id: number; + name: string | null; + visibility: string; + docs: string | null; + attrs: Array; + inner: Record; +} + +// --------------------------------------------------------------------------- +// Stability extraction +// --------------------------------------------------------------------------- + +const STABILITY_RE = + /Stable\s*\{\s*since:\s*Version\(RustcVersion\s*\{\s*major:\s*(\d+),\s*minor:\s*(\d+),\s*patch:\s*(\d+)\s*\}\)/; + +function extractStability(item: RustdocItem): string | undefined { + if (!item.attrs) return undefined; + for (const attr of item.attrs) { + const text = typeof attr === "string" ? attr : attr.other ?? ""; + const match = STABILITY_RE.exec(text); + if (match) { + return `${match[1]}.${match[2]}.${match[3]}`; + } + // Check for unstable + if (text.includes("Unstable")) { + return "unstable"; + } + } + return undefined; +} + +// --------------------------------------------------------------------------- +// Kind mapping +// --------------------------------------------------------------------------- + +/** Maps rustdoc item kinds to RustSymbolKind values. */ +function mapKind(rustdocKind: string): string { + switch (rustdocKind) { + case "struct": + return "struct"; + case "enum": + return "enum"; + case "trait": + return "trait"; + case "function": + return "function"; + case "type_alias": + return "type-alias"; + case "constant": + return "const"; + case "macro": + return "symbol"; + default: + return "struct"; + } +} + +// --------------------------------------------------------------------------- +// Core generation logic +// --------------------------------------------------------------------------- + +interface MemberEntry { + name: string; + kind: string; + associated?: boolean; + since?: string; +} + +interface SymbolEntry { + name: string; + kind: string; + since?: string; + members?: MemberEntry[]; + /** Rustdoc item ID, used for extracting members after walk */ + itemId?: string; +} + +interface GeneratorState { + data: RustdocJson; + modules: Map; + skipModules: Set; +} + +function createGeneratorState( + data: RustdocJson, + skipModules: Set, +): GeneratorState { + return { data, modules: new Map(), skipModules }; +} + +function addSymbol( + state: GeneratorState, + modulePath: string, + name: string, + kind: string, + since?: string, + itemId?: string, +) { + let syms = state.modules.get(modulePath); + if (!syms) { + syms = []; + state.modules.set(modulePath, syms); + } + if (!syms.some((s) => s.name === name)) { + syms.push({ name, kind, since, itemId }); + } +} + +function isSkipped(state: GeneratorState, path: string): boolean { + if (!path) return false; + const topLevel = path.split("::")[0]; + return state.skipModules.has(topLevel); +} + +function getItemKind(item: RustdocItem): string | null { + if (!item.inner) return null; + const keys = Object.keys(item.inner); + return keys.length > 0 ? keys[0] : null; +} + +function walkModule( + state: GeneratorState, + moduleItem: RustdocItem, + modulePath: string, +) { + if (isSkipped(state, modulePath)) return; + + const inner = moduleItem.inner?.module as { items: string[] } | undefined; + if (!inner) return; + + for (const childId of inner.items || []) { + const child = state.data.index[childId]; + if (!child || child.visibility !== "public") continue; + + const kind = getItemKind(child); + if (!kind || !child.name) { + if (kind === "use") handleUseItem(state, child, modulePath); + continue; + } + + if (kind === "module") { + const childPath = modulePath + ? `${modulePath}::${child.name}` + : child.name; + walkModule(state, child, childPath); + } else if (kind === "use") { + handleUseItem(state, child, modulePath); + } else if (ACCEPTED_KINDS.has(kind)) { + const since = extractStability(child); + addSymbol(state, modulePath, child.name, mapKind(kind), since, String(childId)); + } + } +} + +function handleUseItem( + state: GeneratorState, + item: RustdocItem, + modulePath: string, +) { + const useInner = item.inner?.use as + | { source: string; name: string | null; id: string; is_glob: boolean } + | undefined; + if (!useInner || useInner.is_glob) return; + + const name = useInner.name || item.name; + if (!name) return; + + let kind: string | null = null; + const target = state.data.index[useInner.id]; + if (target) kind = getItemKind(target); + + if (!kind) { + const pathInfo = state.data.paths[useInner.id]; + if ( + pathInfo && + (pathInfo.kind === "module" || ACCEPTED_KINDS.has(pathInfo.kind)) + ) { + kind = pathInfo.kind; + } + } + + if (kind && (kind === "module" || ACCEPTED_KINDS.has(kind))) { + if (kind === "module") { + const childPath = modulePath ? `${modulePath}::${name}` : name; + if (target) { + walkModule(state, target, childPath); + } else { + walkExternalModule(state, useInner.id, childPath); + } + } else { + const since = target ? extractStability(target) : undefined; + addSymbol(state, modulePath, name, mapKind(kind), since, String(useInner.id)); + } + } +} + +function walkExternalModule( + state: GeneratorState, + modulePathId: string, + targetModulePath: string, +) { + if (isSkipped(state, targetModulePath)) return; + + const modulePath = state.data.paths[modulePathId]; + if (!modulePath) return; + + const modSuffix = modulePath.path.slice(1).join("::"); + if (!modSuffix) return; + + // Scan all crate prefixes — the re-exporting crate exposes everything + const prefixes = new Set(); + prefixes.add(modulePath.path.join("::")); + for (const [, crate] of Object.entries(state.data.external_crates)) { + prefixes.add(`${crate.name}::${modSuffix}`); + } + // Also check the crate's own name + const root = state.data.index[state.data.root]; + if (root?.name) prefixes.add(`${root.name}::${modSuffix}`); + + for (const [, pathInfo] of Object.entries(state.data.paths)) { + if (pathInfo.kind === "module" || pathInfo.kind === "primitive") continue; + if (!ACCEPTED_KINDS.has(pathInfo.kind)) continue; + + const fullPath = pathInfo.path.join("::"); + for (const prefix of prefixes) { + if (!fullPath.startsWith(prefix + "::")) continue; + const remainder = fullPath.substring(prefix.length + 2); + if (remainder.includes("::")) continue; + addSymbol(state, targetModulePath, remainder, mapKind(pathInfo.kind)); + break; + } + } +} + +// --------------------------------------------------------------------------- +// Prelude extraction +// --------------------------------------------------------------------------- + +interface PreludeSet { + edition: string; + types: Set; +} + +const PRELUDE_USE_SUPPRESS_KINDS = new Set([ + "struct", + "enum", + "trait", + "variant", + "type_alias", +]); + +const PRIMITIVES = [ + "bool", "char", "f32", "f64", + "i8", "i16", "i32", "i64", "i128", "isize", + "u8", "u16", "u32", "u64", "u128", "usize", "str", +]; + +function findChildModule( + data: RustdocJson, + parentItem: RustdocItem, + name: string, +): RustdocItem | null { + const items = + (parentItem.inner?.module as { items: string[] } | undefined)?.items || []; + for (const id of items) { + const item = data.index[id]; + if (item && item.name === name) return item; + } + return null; +} + +function extractPreludeTypes( + data: RustdocJson, + moduleItem: RustdocItem, +): Set { + const types = new Set(); + const items = + (moduleItem.inner?.module as { items: string[] } | undefined)?.items || []; + + for (const childId of items) { + const child = data.index[childId]; + if (!child || child.visibility !== "public") continue; + if (getItemKind(child) !== "use") continue; + + const useInner = child.inner?.use as + | { name: string | null; id: string; is_glob: boolean } + | undefined; + if (!useInner || useInner.is_glob) continue; + + const name = useInner.name || child.name; + if (!name) continue; + + let targetKind: string | null = null; + const target = data.index[useInner.id]; + if (target) targetKind = getItemKind(target); + if (!targetKind) { + const pathInfo = data.paths[useInner.id]; + if (pathInfo) targetKind = pathInfo.kind; + } + + if (targetKind && PRELUDE_USE_SUPPRESS_KINDS.has(targetKind)) { + types.add(name); + } + } + + return types; +} + +function extractPrelude( + data: RustdocJson, + preludeModule: RustdocItem, + coreData: RustdocJson | null, +): PreludeSet[] { + const results: PreludeSet[] = []; + + // Extract v1 (base) prelude + const v1Module = findChildModule(data, preludeModule, "v1"); + if (v1Module) { + const types = extractPreludeTypes(data, v1Module); + if (types.size > 0) results.push({ edition: "v1", types }); + } + + // For edition-specific preludes, the items are glob re-exports + // of core::prelude::rust_XXXX. We need core's rustdoc to resolve them. + const corePreludeModule = coreData + ? findChildModule(coreData, coreData.index[coreData.root], "prelude") + : null; + + for (const editionName of ["rust_2015", "rust_2018", "rust_2021", "rust_2024"]) { + const edition = editionName.replace("rust_", ""); + + // Start with v1 types as base + const v1Types = results.find((r) => r.edition === "v1")?.types; + const types = new Set(v1Types); + + // Add edition-specific types from core's prelude if available + if (corePreludeModule) { + const coreEditionModule = findChildModule( + coreData!, + corePreludeModule, + editionName, + ); + if (coreEditionModule) { + const editionTypes = extractPreludeTypes(coreData!, coreEditionModule); + for (const t of editionTypes) types.add(t); + } + } + + // Also check non-glob use items in std's edition module + const stdEditionModule = findChildModule(data, preludeModule, editionName); + if (stdEditionModule) { + const stdTypes = extractPreludeTypes(data, stdEditionModule); + for (const t of stdTypes) types.add(t); + } + + results.push({ edition, types }); + } + + return results; +} + +// --------------------------------------------------------------------------- +// Code generation +// --------------------------------------------------------------------------- + +function generatePreludeFile( + formatVersion: number, + preludeSets: PreludeSet[], +): string { + const lines: string[] = []; + lines.push(`// Generated by scripts/generate-crate-descriptor.ts`); + lines.push(`// Source: rustdoc JSON format version ${formatVersion}`); + lines.push(``); + + // Build a combined set from all editions, plus primitives + const allTypes = new Set(PRIMITIVES); + for (const ps of preludeSets) { + for (const t of ps.types) allTypes.add(t); + } + + lines.push(`/**`); + lines.push( + ` * Types, traits, enum variants, and primitives that are automatically in scope`, + ); + lines.push( + ` * via the Rust prelude. References to these do not need \`use\` statements.`, + ); + lines.push(` *`); + lines.push( + ` * This is the union of all edition preludes (2015–2024) plus primitive types.`, + ); + lines.push(` */`); + lines.push(`export const PRELUDE_TYPES = new Set([`); + for (const name of [...allTypes].sort()) { + lines.push(` "${name}",`); + } + lines.push(`]);`); + lines.push(``); + + // Per-edition sets + for (const ps of preludeSets) { + if (ps.edition === "v1") continue; // v1 is the legacy name, skip + const varName = `PRELUDE_TYPES_${ps.edition}`; + lines.push(`/** Prelude types for the ${ps.edition} edition. */`); + lines.push(`export const ${varName} = new Set([`); + for (const name of [...ps.types].sort()) { + lines.push(` "${name}",`); + } + // Add primitives to each edition set too + for (const p of PRIMITIVES.sort()) { + lines.push(` "${p}",`); + } + lines.push(`]);`); + lines.push(``); + } + + return lines.join("\n"); +} + +// --------------------------------------------------------------------------- +// Utility +// --------------------------------------------------------------------------- + +const JS_RESERVED = new Set([ + "abstract", "arguments", "await", "boolean", "break", "byte", "case", "catch", + "char", "class", "const", "continue", "debugger", "default", "delete", "do", + "double", "else", "enum", "eval", "export", "extends", "false", "final", + "finally", "float", "for", "function", "goto", "if", "implements", "import", + "in", "instanceof", "int", "interface", "let", "long", "native", "new", + "null", "package", "private", "protected", "public", "return", "short", + "static", "super", "switch", "synchronized", "this", "throw", "throws", + "transient", "true", "try", "typeof", "undefined", "var", "void", + "volatile", "while", "with", "yield", +]); + +function sanitizeIdentifier(name: string): string { + const cleaned = name.replace(/-/g, "_"); + if (JS_RESERVED.has(cleaned)) return `${cleaned}_`; + return cleaned; +} + +function pascalCase(name: string): string { + return name + .split(/[-_]/) + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(""); +} + +// --------------------------------------------------------------------------- +// Main +// --------------------------------------------------------------------------- + +const cli = parseArgs(); + +const data: RustdocJson = JSON.parse( + readFileSync(resolve(cli.jsonPath), "utf-8"), +); + +// Extract crate name from the root module +const root = data.index[data.root]; +if (!root) { + console.error("Could not find root module in index"); + process.exit(1); +} + +const crateName = root.name ?? "unknown"; +const crateVersion = data.crate_version; + +console.log(`Crate: ${crateName} ${crateVersion ?? "(no version)"}`); +console.log(`Rustdoc JSON format version: ${data.format_version}`); +console.log(`Index items: ${Object.keys(data.index).length}`); +console.log(`Builtin: ${cli.builtin}`); + +// Merge skip modules: CLI-provided + defaults for std-family crates +const skipModules = new Set(cli.skipModules); +if (["std", "core", "alloc"].includes(crateName)) { + for (const m of DEFAULT_STD_SKIP) skipModules.add(m); +} + +// Walk the module tree +const state = createGeneratorState(data, skipModules); +walkModule(state, root, ""); + +// For std-like crates, also walk the prelude to pick up re-exports +const preludeModule = findChildModule(data, root, "prelude"); +if (preludeModule && crateName === "std") { + // Walk v1 prelude to pick up types for the crate descriptor + const v1Module = findChildModule(data, preludeModule, "v1"); + if (v1Module) { + const v1Items = + (v1Module.inner?.module as { items: string[] } | undefined)?.items || []; + + for (const childId of v1Items) { + const child = data.index[childId]; + if (!child || child.visibility !== "public") continue; + if (getItemKind(child) !== "use") continue; + + const useInner = child.inner?.use as + | { source: string; name: string | null; id: string; is_glob: boolean } + | undefined; + if (!useInner || useInner.is_glob) continue; + + const name = useInner.name || child.name; + if (!name) continue; + + let targetKind: string | null = null; + const target = data.index[useInner.id]; + if (target) targetKind = getItemKind(target); + if (!targetKind) { + const pathInfo = data.paths[useInner.id]; + if (pathInfo) targetKind = pathInfo.kind; + } + if (!targetKind || !ACCEPTED_KINDS.has(targetKind)) continue; + + // Map to canonical module using paths table + const pathInfo = data.paths[String(useInner.id)]; + if (!pathInfo) continue; + + // For prelude items, determine the std module they belong to + // by checking what module they're re-exported from + const sourcePath = pathInfo.path; + const typeName = sourcePath[sourcePath.length - 1]; + + // Find which module in our descriptor already has this type + let placed = false; + for (const [modPath, syms] of state.modules) { + if (syms.some((s) => s.name === typeName)) { + placed = true; + break; + } + } + + // If not already placed, add to root module + if (!placed) { + const since = target ? extractStability(target) : undefined; + addSymbol(state, "", name, mapKind(targetKind), since); + } + } + + console.log("Prelude v1 items processed"); + } +} + +// --------------------------------------------------------------------------- +// --------------------------------------------------------------------------- +// Extract inherent methods from impl blocks +// --------------------------------------------------------------------------- + +const MEMBER_KINDS = new Set(["struct", "enum", "trait"]); + +function extractMembers(genState: GeneratorState) { + let totalMembers = 0; + + for (const [, symbols] of genState.modules) { + for (const sym of symbols) { + if (!sym.itemId || !MEMBER_KINDS.has(sym.kind)) continue; + + const item = genState.data.index[sym.itemId]; + if (!item) continue; + + const inner = item.inner?.[sym.kind === "type-alias" ? "type_alias" : sym.kind] as + | { impls?: string[] } + | undefined; + if (!inner?.impls) continue; + + const members: MemberEntry[] = []; + + for (const implId of inner.impls) { + const impl = genState.data.index[implId]; + if (!impl?.inner?.impl) continue; + const implInner = impl.inner.impl as { + trait?: unknown; + items?: string[]; + }; + + // Only inherent impls (no trait impls) + if (implInner.trait) continue; + + for (const methodId of implInner.items || []) { + const method = genState.data.index[methodId]; + if (!method || method.visibility !== "public" || !method.name) continue; + + const methodKind = getItemKind(method); + if (methodKind !== "function") continue; + + const sig = (method.inner?.function as { sig?: { inputs?: [string, unknown][] } })?.sig; + const hasSelfReceiver = sig?.inputs?.some( + ([name]) => name === "self", + ); + + if (!members.some((m) => m.name === method.name)) { + members.push({ + name: method.name!, + kind: "function", + associated: hasSelfReceiver ? undefined : true, + since: extractStability(method), + }); + } + } + } + + if (members.length > 0) { + members.sort((a, b) => a.name.localeCompare(b.name)); + sym.members = members; + totalMembers += members.length; + } + } + } + + console.log(`Extracted ${totalMembers} inherent methods`); +} + +// --------------------------------------------------------------------------- +// Merge modules from other crates (--merge-from) +// --------------------------------------------------------------------------- +// Used for crates that re-export from dependencies (e.g., std re-exports +// core and alloc). We walk each source crate's rustdoc and merge its +// modules into our state, so the output includes the full re-exported API. + +for (const mergePath of cli.mergeFrom) { + const mergeData: RustdocJson = JSON.parse( + readFileSync(resolve(mergePath), "utf-8"), + ); + const mergeRoot = mergeData.index[mergeData.root]; + if (!mergeRoot) continue; + + const mergeName = mergeRoot.name ?? "unknown"; + const mergeState = createGeneratorState(mergeData, skipModules); + walkModule(mergeState, mergeRoot, ""); + + // Extract members in the merge state BEFORE merging (uses merge data's index) + extractMembers(mergeState); + + // Merge into main state: for each module in the source crate, + // add its symbols (with members) to the corresponding module in our state + let mergedCount = 0; + for (const [modulePath, symbols] of mergeState.modules) { + for (const sym of symbols) { + // Add the symbol — if it already exists, skip (deduplication in addSymbol) + addSymbol(state, modulePath, sym.name, sym.kind, sym.since, sym.itemId); + // If the merged symbol has members and the main state's copy doesn't, transfer them + if (sym.members) { + let mainSyms = state.modules.get(modulePath); + if (mainSyms) { + const mainSym = mainSyms.find((s) => s.name === sym.name); + if (mainSym && !mainSym.members) { + mainSym.members = sym.members; + } + } + } + mergedCount++; + } + } + + console.log(`Merged ${mergedCount} symbols from ${mergeName}`); +} + +extractMembers(state); + +// Sort and print summary +const sortedModules = [...state.modules.entries()].sort(([a], [b]) => + a.localeCompare(b), +); +let totalSymbols = 0; +for (const [mod, syms] of sortedModules) { + syms.sort((a, b) => a.name.localeCompare(b.name)); + totalSymbols += syms.length; +} +console.log(`\nModules: ${sortedModules.length}, Symbols: ${totalSymbols}`); + +for (const [mod, syms] of sortedModules) { + const names = syms.map((s) => s.name); + const display = + names.length > 8 + ? `${names.slice(0, 8).join(", ")}, ... +${names.length - 8}` + : names.join(", "); + console.log(` ${mod || "(root)"} [${syms.length}]: ${display}`); +} + +// --------------------------------------------------------------------------- +// Write output — per-module directory structure +// --------------------------------------------------------------------------- + +import { mkdirSync } from "node:fs"; + +const scriptDir = dirname(new URL(import.meta.url).pathname); +const defaultOutDir = join(scriptDir, "..", "src", "builtins", crateName); +const outDir = cli.outPath ? resolve(cli.outPath) : defaultOutDir; +mkdirSync(outDir, { recursive: true }); + +// Generate a file per top-level module +const topLevelModules = new Map(); +for (const [modulePath, symbols] of sortedModules) { + const topLevel = modulePath === "" ? "root" : modulePath.split("::")[0]; + if (!topLevelModules.has(topLevel)) { + topLevelModules.set(topLevel, []); + } + topLevelModules.get(topLevel)!.push([modulePath, symbols]); +} + +function generateModuleFile( + moduleEntries: [string, SymbolEntry[]][], + formatVersion: number, +): string { + const lines: string[] = []; + lines.push(`import type { SymbolDescriptor } from "../../create-crate.js";`); + lines.push(``); + lines.push(`// Generated by scripts/generate-crate-descriptor.ts`); + lines.push(`// Source: rustdoc JSON format version ${formatVersion}`); + lines.push(``); + + for (const [modulePath, symbols] of moduleEntries) { + const varName = modulePath === "" + ? "mod_root" + : "mod_" + sanitizeIdentifier(modulePath.replace(/::/g, "_")); + + lines.push( + `export const ${varName} = {`, + ); + for (const sym of symbols) { + if (sym.members && sym.members.length > 0) { + const meta = sym.since ? `, metadata: { since: "${sym.since}" }` : ""; + lines.push(` ${sym.name}: {`); + lines.push(` kind: "${sym.kind}"${meta},`); + lines.push(` members: {`); + for (const m of sym.members) { + const mAssoc = m.associated ? ", associated: true" : ""; + const mMeta = m.since ? `, metadata: { since: "${m.since}" }` : ""; + lines.push(` ${m.name}: { kind: "${m.kind}"${mAssoc}${mMeta} },`); + } + lines.push(` },`); + lines.push(` },`); + } else { + if (sym.since) { + lines.push( + ` ${sym.name}: { kind: "${sym.kind}", metadata: { since: "${sym.since}" } },`, + ); + } else { + lines.push(` ${sym.name}: { kind: "${sym.kind}" },`); + } + } + } + lines.push( + `} as const satisfies Record;`, + ); + lines.push(``); + } + + return lines.join("\n"); +} + +// Write module files +const moduleFileNames: { varName: string; fileName: string; modulePath: string }[] = []; + +for (const [topLevel, entries] of [...topLevelModules.entries()].sort(([a], [b]) => a.localeCompare(b))) { + const fileName = `${topLevel}.ts`; + const filePath = join(outDir, fileName); + const source = generateModuleFile(entries, data.format_version); + writeFileSync(filePath, source); + + // Track for the index file — variable names are prefixed with mod_ to avoid + // collisions with the crate export name and JS reserved words + for (const [modulePath] of entries) { + const varName = modulePath === "" + ? "mod_root" + : "mod_" + sanitizeIdentifier(modulePath.replace(/::/g, "_")); + moduleFileNames.push({ varName, fileName, modulePath }); + } +} + +// Write crate index file +{ + const lines: string[] = []; + lines.push(`import { type SymbolCreator } from "@alloy-js/core";`); + lines.push(`import {`); + lines.push(` type CrateDescriptor,`); + lines.push(` type CrateRef,`); + lines.push(` createCrate,`); + lines.push(` type ExternalCrate,`); + lines.push(`} from "../../create-crate.js";`); + + // Import each module + const importsByFile = new Map(); + for (const { varName, fileName } of moduleFileNames) { + if (!importsByFile.has(fileName)) importsByFile.set(fileName, []); + importsByFile.get(fileName)!.push(varName); + } + for (const [fileName, vars] of [...importsByFile.entries()].sort(([a], [b]) => a.localeCompare(b))) { + const jsName = fileName.replace(".ts", ".js"); + lines.push(`import { ${vars.join(", ")} } from "./${jsName}";`); + } + + lines.push(``); + lines.push(`// Generated by scripts/generate-crate-descriptor.ts`); + lines.push(`// Source: rustdoc JSON format version ${data.format_version}`); + lines.push(``); + + const descriptorName = `${sanitizeIdentifier(crateName)}Descriptor`; + const typeName = `${pascalCase(crateName)}Crate`; + + lines.push(`const ${descriptorName} = {`); + lines.push(` name: "${crateName}",`); + if (crateVersion && crateVersion !== "0.0.0") { + lines.push(` version: "${crateVersion}",`); + } + if (cli.builtin) { + lines.push(` builtin: true,`); + } + lines.push(` modules: {`); + + for (const { varName, modulePath } of moduleFileNames) { + const key = modulePath === "" ? '""' : `"${modulePath}"`; + lines.push(` ${key}: ${varName},`); + } + + lines.push(` },`); + lines.push(`} as const satisfies CrateDescriptor;`); + lines.push(``); + lines.push(`/**`); + lines.push(` * The \`${crateName}\` crate descriptor.`); + lines.push(` */`); + lines.push(`export type ${typeName} = CrateRef &`); + lines.push(` SymbolCreator &`); + lines.push(` ExternalCrate;`); + lines.push(`export const ${sanitizeIdentifier(crateName)}: ${typeName} = createCrate(${descriptorName});`); + lines.push(``); + + writeFileSync(join(outDir, "index.ts"), lines.join("\n")); +} + +console.log(`\nWrote ${outDir}/ (${topLevelModules.size} module files + index.ts)`); + +// Write prelude if requested +if (cli.prelude && preludeModule) { + const coreData: RustdocJson | null = cli.preludeSource + ? JSON.parse(readFileSync(resolve(cli.preludeSource), "utf-8")) + : null; + const preludeSets = extractPrelude(data, preludeModule, coreData); + const preludeSource = generatePreludeFile(data.format_version, preludeSets); + // Write prelude one level up (alongside the crate directory) + const preludePath = join(outDir, "..", "prelude.ts"); + writeFileSync(preludePath, preludeSource); + console.log(`Wrote ${preludePath}`); + + for (const ps of preludeSets) { + console.log(` Prelude ${ps.edition}: ${ps.types.size} types`); + } +} diff --git a/packages/rust/src/builtins/README.md b/packages/rust/src/builtins/README.md new file mode 100644 index 000000000..55b867bf3 --- /dev/null +++ b/packages/rust/src/builtins/README.md @@ -0,0 +1,219 @@ +# Rust Standard Library Builtins + +Auto-generated crate descriptors for `core`, `alloc`, and `std`, produced by a generic tool that works with any rustdoc JSON. + +## Directory structure + +All files except `index.ts` and `prelude.ts` in the root, and `index.ts` in each crate directory, are generated — do not edit by hand. + +``` +builtins/ + index.ts — barrel export (std, core, alloc, PRELUDE_TYPES, etc.) + prelude.ts — edition-aware PRELUDE_TYPES sets + core/ + index.ts — core crate descriptor (imports all module files) + fmt.ts — core::fmt module (Display, Debug, Formatter + methods) + option.ts — core::option module (Option + methods) + iter.ts — core::iter module (Iterator, Chain, Map, Filter, ...) + ... — 43 module files total + alloc/ + index.ts — alloc crate descriptor + vec.ts — alloc::vec module (Vec + 70+ methods) + string.ts — alloc::string module (String + methods) + ... — 14 module files total + std/ + index.ts — std crate descriptor (includes merged core+alloc modules) + collections.ts — std::collections (HashMap, BTreeMap, etc. + methods) + io.ts — std::io (Read, Write, BufReader, etc.) + ... — 54 module files total +``` + +Each crate is generated independently from its own rustdoc JSON. The `std` descriptor includes all `core` and `alloc` modules via `--merge-from`. + +## What's encoded + +Each symbol has: + +- **`kind`**: `struct`, `enum`, `trait`, `function`, `type-alias`, `const`, `symbol` +- **`metadata.since`**: minimum Rust version (e.g., `"1.80.0"`) from stability attributes +- **`members`**: inherent methods/associated functions on types (structs, enums, traits) + - `kind: "function"` for all members + - `associated: true` for associated functions (no `self`, called with `::` like `HashMap::new()`) + - Without `associated`: instance methods (has `self`, called with `.` like `map.insert(k, v)`) + +## Usage + +```tsx +import { std, core, alloc } from "@alloy-js/rust"; + +// Use directly as JSX children or in props — no wrapper needed + // → use std::fmt::Display; + // → use std::collections::HashMap; + +// Composing generic types +{"Option<"}{std.time.Duration}{">"}} /> + +// Access member refkeys +std.collections.HashMap.insert // Refkey for the insert method +std.collections.HashMap.new // Refkey for the new associated function + +// #![no_std] — use core directly + // → use core::fmt::Display; + +// #![no_std] + alloc + // → use alloc::vec::Vec; +``` + +### Edition-aware preludes + +The `reference.tsx` module automatically selects the correct prelude based on the `edition` prop on ``. Types in the active prelude don't generate `use` statements. + +```ts +import { PRELUDE_TYPES_2021, PRELUDE_TYPES_2024 } from "@alloy-js/rust"; + +// 2021 adds: TryFrom, TryInto, FromIterator +// 2024 adds: Future, IntoFuture +``` + +## Regenerating + +### Prerequisites + +- Rust **nightly** toolchain with `rust-src` component: + ```sh + rustup toolchain install nightly + rustup component add rust-src --toolchain nightly + ``` +- If `libz.so.1` is not on the default library path (e.g. Nix environments): + ```sh + export LD_LIBRARY_PATH=$(dirname $(find /nix/store -name "libz.so.1" 2>/dev/null | head -1)) + ``` + +### Step 1: Generate rustdoc JSON + +Run `cargo doc` inside each library's source tree: + +```sh +SYSROOT=$(rustc +nightly --print sysroot) +LIBS="$SYSROOT/lib/rustlib/src/rust/library" + +for crate in core alloc std; do + cd "$LIBS/$crate" + CARGO_TARGET_DIR=~/rustdoc-output \ + RUSTDOCFLAGS="-Z unstable-options --output-format json" \ + cargo +nightly doc --no-deps +done +``` + +This produces `~/rustdoc-output/doc/{core,alloc,std}.json`. + +> **Why nightly?** The `--output-format json` flag is unstable. + +> **`/tmp` won't work** if mounted with `noexec`. Use a home directory path. + +### Step 2: Run the generator + +From the repository root: + +```sh +npx tsx packages/rust/scripts/generate-crate-descriptor.ts \ + ~/rustdoc-output/doc/core.json --builtin + +npx tsx packages/rust/scripts/generate-crate-descriptor.ts \ + ~/rustdoc-output/doc/alloc.json --builtin + +npx tsx packages/rust/scripts/generate-crate-descriptor.ts \ + ~/rustdoc-output/doc/std.json --builtin \ + --prelude --prelude-source ~/rustdoc-output/doc/core.json \ + --merge-from ~/rustdoc-output/doc/core.json \ + --merge-from ~/rustdoc-output/doc/alloc.json +``` + +### Step 3: Verify + +```sh +cd packages/rust +npx tsc --noEmit +npx vitest run +``` + +## Using with third-party crates + +The generator works with any crate's rustdoc JSON: + +```sh +# In a project that depends on serde +RUSTDOCFLAGS="-Z unstable-options --output-format json" cargo +nightly doc +npx tsx packages/rust/scripts/generate-crate-descriptor.ts target/doc/serde.json +``` + +This generates a descriptor directory with versioned dependency info (the version goes into Cargo.toml `[dependencies]`). + +## How the generator works + +The generic tool (`scripts/generate-crate-descriptor.ts`): + +1. **Walks the module tree** from the root, recording public symbols with their rustdoc item IDs. + +2. **Follows re-exports** (`pub use` items). When the target is in the index, it walks into it. When external (cross-crate), it scans the paths table. + +3. **Extracts inherent methods** from `impl` blocks for structs, enums, and traits. Classifies as instance method (has `self`) or associated function (no `self`). + +4. **Extracts stability metadata** from `#[stable(since = "1.80.0")]` attributes. + +5. **Merges from other crates** (`--merge-from`). For std, this pulls in all core and alloc symbols with their methods, so re-exported types like `BTreeMap` get full member listings. + +6. **Generates edition-aware preludes** (`--prelude --prelude-source`). Walks `std::prelude::v1` and each edition module, using core's rustdoc to resolve glob re-exports. + +7. **Outputs per-module files** in a crate directory, keeping each file small to avoid TS2742 API Extractor portability errors. + +### CLI options + +``` +npx tsx generate-crate-descriptor.ts [options] + + --builtin Mark as builtin (no Cargo.toml dependency) + --prelude Generate prelude.ts with PRELUDE_TYPES sets + --prelude-source PATH Core rustdoc JSON for resolving edition prelude globs + --merge-from PATH Merge another crate's symbols (repeatable) + --out PATH Output directory (default: src/builtins//) + --skip MOD1,MOD2 Comma-separated modules to skip +``` + +## Excluded modules + +Standard library crates automatically skip: + +| Module | Reason | +| ------------------------------------- | -------------------------------- | +| `os`, `arch`, `simd` | Platform-specific | +| `f16`, `f128`, `bstr`, `autodiff` | Nightly-only | +| `intrinsics`, `field`, `hint` | Compiler internals | +| `prelude`, `primitive`, `index` | Meta-modules or naming conflicts | +| `unsafe_binder`, `from`, `async_iter` | Nightly-only | + +Override with `--skip` for other crates. + +## Versioning + +- **core/alloc/std**: version `0.0.0` (implicit toolchain version). Stability is tracked per-symbol via `since`. +- **Third-party crates**: semver from `crate_version` in rustdoc JSON → `version` in descriptor → Cargo.toml `[dependencies]`. + +## Rustdoc JSON format + +Currently uses format version 57. The format is documented at https://github.com/rust-lang/rust/blob/main/src/rustdoc-json-types/lib.rs. + +## What's NOT yet encoded + +The following data is available in rustdoc JSON but not yet extracted: + +| Data | Available in rustdoc | Potential use | +| ----------------------------- | -------------------- | ------------------------------- | +| Generic parameters (``) | Yes | Type-safe generic instantiation | +| Parameter types on functions | Yes | Type-checked JSX props | +| Return types | Yes | Type-checked output | +| Trait implementations | Yes | Derive validation, trait bounds | +| Enum variants | Yes | Match arm generation | +| Struct fields | Yes | Struct literal generation | +| Documentation strings | Yes | Doc comments in generated code | +| Where clauses | Yes | Constraint rendering | diff --git a/packages/rust/src/builtins/alloc/alloc.ts b/packages/rust/src/builtins/alloc/alloc.ts new file mode 100644 index 000000000..7588c73e8 --- /dev/null +++ b/packages/rust/src/builtins/alloc/alloc.ts @@ -0,0 +1,13 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_alloc = { + alloc: { kind: "function", metadata: { since: "1.28.0" } }, + alloc_zeroed: { kind: "function", metadata: { since: "1.28.0" } }, + dealloc: { kind: "function", metadata: { since: "1.28.0" } }, + Global: { kind: "struct", metadata: { since: "unstable" } }, + handle_alloc_error: { kind: "function", metadata: { since: "1.28.0" } }, + realloc: { kind: "function", metadata: { since: "1.28.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/borrow.ts b/packages/rust/src/builtins/alloc/borrow.ts new file mode 100644 index 000000000..544b6aafd --- /dev/null +++ b/packages/rust/src/builtins/alloc/borrow.ts @@ -0,0 +1,19 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_borrow = { + Borrow: { kind: "trait" }, + BorrowMut: { kind: "trait" }, + Cow: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + into_owned: { kind: "function", metadata: { since: "1.0.0" } }, + is_borrowed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_owned: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_mut: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ToOwned: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/boxed.ts b/packages/rust/src/builtins/alloc/boxed.ts new file mode 100644 index 000000000..b0fd71cc2 --- /dev/null +++ b/packages/rust/src/builtins/alloc/boxed.ts @@ -0,0 +1,69 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_boxed = { + Box: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_mut_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.0.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + from_non_null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_non_null_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_boxed_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_non_null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_non_null_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_pin: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + leak: { kind: "function", associated: true, metadata: { since: "1.26.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + take: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + write: { kind: "function", associated: true, metadata: { since: "1.87.0" } }, + }, + }, + ThinBox: { + kind: "struct", metadata: { since: "unstable" }, + members: { + new: { kind: "function", associated: true }, + new_unsize: { kind: "function", associated: true }, + try_new: { kind: "function", associated: true }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/collections.ts b/packages/rust/src/builtins/alloc/collections.ts new file mode 100644 index 000000000..46e0f3549 --- /dev/null +++ b/packages/rust/src/builtins/alloc/collections.ts @@ -0,0 +1,484 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_collections = { + BinaryHeap: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.11.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.80.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + drain_sorted: { kind: "function", metadata: { since: "unstable" } }, + from_raw_vec: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_iter_sorted: { kind: "function", metadata: { since: "unstable" } }, + into_sorted_vec: { kind: "function", metadata: { since: "1.5.0" } }, + into_vec: { kind: "function", metadata: { since: "1.5.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.12.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "unstable" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.70.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + BTreeMap: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + append: { kind: "function", metadata: { since: "1.11.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains_key: { kind: "function", metadata: { since: "1.0.0" } }, + entry: { kind: "function", metadata: { since: "1.0.0" } }, + extract_if: { kind: "function", metadata: { since: "1.91.0" } }, + first_entry: { kind: "function", metadata: { since: "1.66.0" } }, + first_key_value: { kind: "function", metadata: { since: "1.66.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_key_value: { kind: "function", metadata: { since: "1.40.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + into_keys: { kind: "function", metadata: { since: "1.54.0" } }, + into_values: { kind: "function", metadata: { since: "1.54.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + keys: { kind: "function", metadata: { since: "1.0.0" } }, + last_entry: { kind: "function", metadata: { since: "1.66.0" } }, + last_key_value: { kind: "function", metadata: { since: "1.66.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + lower_bound: { kind: "function", metadata: { since: "unstable" } }, + lower_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + merge: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_first: { kind: "function", metadata: { since: "1.66.0" } }, + pop_last: { kind: "function", metadata: { since: "1.66.0" } }, + range: { kind: "function", metadata: { since: "1.17.0" } }, + range_mut: { kind: "function", metadata: { since: "1.17.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + remove_entry: { kind: "function", metadata: { since: "1.45.0" } }, + retain: { kind: "function", metadata: { since: "1.53.0" } }, + split_off: { kind: "function", metadata: { since: "1.11.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + upper_bound: { kind: "function", metadata: { since: "unstable" } }, + upper_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + values: { kind: "function", metadata: { since: "1.0.0" } }, + values_mut: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + BTreeSet: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + append: { kind: "function", metadata: { since: "1.11.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.0.0" } }, + difference: { kind: "function", metadata: { since: "1.0.0" } }, + entry: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.91.0" } }, + first: { kind: "function", metadata: { since: "1.66.0" } }, + get: { kind: "function", metadata: { since: "1.9.0" } }, + get_or_insert: { kind: "function", metadata: { since: "unstable" } }, + get_or_insert_with: { kind: "function", metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + intersection: { kind: "function", metadata: { since: "1.0.0" } }, + is_disjoint: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + is_subset: { kind: "function", metadata: { since: "1.0.0" } }, + is_superset: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + last: { kind: "function", metadata: { since: "1.66.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + lower_bound: { kind: "function", metadata: { since: "unstable" } }, + lower_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_first: { kind: "function", metadata: { since: "1.66.0" } }, + pop_last: { kind: "function", metadata: { since: "1.66.0" } }, + range: { kind: "function", metadata: { since: "1.17.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.9.0" } }, + retain: { kind: "function", metadata: { since: "1.53.0" } }, + split_off: { kind: "function", metadata: { since: "1.11.0" } }, + symmetric_difference: { kind: "function", metadata: { since: "1.0.0" } }, + take: { kind: "function", metadata: { since: "1.9.0" } }, + union: { kind: "function", metadata: { since: "1.0.0" } }, + upper_bound: { kind: "function", metadata: { since: "unstable" } }, + upper_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + LinkedList: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + append: { kind: "function", metadata: { since: "1.0.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + cursor_back: { kind: "function", metadata: { since: "unstable" } }, + cursor_back_mut: { kind: "function", metadata: { since: "unstable" } }, + cursor_front: { kind: "function", metadata: { since: "unstable" } }, + cursor_front_mut: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + remove: { kind: "function", metadata: { since: "unstable" } }, + retain: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + TryReserveError: { + kind: "struct", metadata: { since: "1.57.0" }, + members: { + kind: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + TryReserveErrorKind: { kind: "enum", metadata: { since: "unstable" } }, + VecDeque: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_slices: { kind: "function", metadata: { since: "1.5.0" } }, + as_slices: { kind: "function", metadata: { since: "1.5.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + binary_search: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by_key: { kind: "function", metadata: { since: "1.54.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "unstable" } }, + extend_front: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.5.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + make_contiguous: { kind: "function", metadata: { since: "1.48.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + partition_point: { kind: "function", metadata: { since: "1.54.0" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_back_if: { kind: "function", metadata: { since: "1.93.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front_if: { kind: "function", metadata: { since: "1.93.0" } }, + prepend: { kind: "function", metadata: { since: "unstable" } }, + prepend_from_within: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + range: { kind: "function", metadata: { since: "1.51.0" } }, + range_mut: { kind: "function", metadata: { since: "1.51.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.16.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.4.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.36.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.36.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.5.0" } }, + splice: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + swap_remove_back: { kind: "function", metadata: { since: "1.5.0" } }, + swap_remove_front: { kind: "function", metadata: { since: "1.5.0" } }, + truncate: { kind: "function", metadata: { since: "1.16.0" } }, + truncate_front: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_binary_heap = { + BinaryHeap: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.11.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.80.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + drain_sorted: { kind: "function", metadata: { since: "unstable" } }, + from_raw_vec: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_iter_sorted: { kind: "function", metadata: { since: "unstable" } }, + into_sorted_vec: { kind: "function", metadata: { since: "1.5.0" } }, + into_vec: { kind: "function", metadata: { since: "1.5.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.12.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "unstable" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.70.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Drain: { + kind: "struct", metadata: { since: "1.6.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + DrainSorted: { + kind: "struct", metadata: { since: "unstable" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIterSorted: { + kind: "struct", metadata: { since: "unstable" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Iter: { kind: "struct", metadata: { since: "1.0.0" } }, + PeekMut: { + kind: "struct", metadata: { since: "1.12.0" }, + members: { + pop: { kind: "function", associated: true, metadata: { since: "1.18.0" } }, + refresh: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_linked_list = { + Cursor: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_list: { kind: "function", metadata: { since: "unstable" } }, + back: { kind: "function", metadata: { since: "unstable" } }, + current: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "unstable" } }, + index: { kind: "function", metadata: { since: "unstable" } }, + move_next: { kind: "function", metadata: { since: "unstable" } }, + move_prev: { kind: "function", metadata: { since: "unstable" } }, + peek_next: { kind: "function", metadata: { since: "unstable" } }, + peek_prev: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + CursorMut: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_cursor: { kind: "function", metadata: { since: "unstable" } }, + as_list: { kind: "function", metadata: { since: "unstable" } }, + back: { kind: "function", metadata: { since: "unstable" } }, + back_mut: { kind: "function", metadata: { since: "unstable" } }, + current: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "unstable" } }, + front_mut: { kind: "function", metadata: { since: "unstable" } }, + index: { kind: "function", metadata: { since: "unstable" } }, + insert_after: { kind: "function", metadata: { since: "unstable" } }, + insert_before: { kind: "function", metadata: { since: "unstable" } }, + move_next: { kind: "function", metadata: { since: "unstable" } }, + move_prev: { kind: "function", metadata: { since: "unstable" } }, + peek_next: { kind: "function", metadata: { since: "unstable" } }, + peek_prev: { kind: "function", metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "unstable" } }, + pop_front: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "unstable" } }, + push_front: { kind: "function", metadata: { since: "unstable" } }, + remove_current: { kind: "function", metadata: { since: "unstable" } }, + remove_current_as_list: { kind: "function", metadata: { since: "unstable" } }, + splice_after: { kind: "function", metadata: { since: "unstable" } }, + splice_before: { kind: "function", metadata: { since: "unstable" } }, + split_after: { kind: "function", metadata: { since: "unstable" } }, + split_before: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExtractIf: { kind: "struct", metadata: { since: "1.87.0" } }, + IntoIter: { kind: "struct", metadata: { since: "1.0.0" } }, + Iter: { kind: "struct", metadata: { since: "1.0.0" } }, + IterMut: { kind: "struct", metadata: { since: "1.0.0" } }, + LinkedList: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + append: { kind: "function", metadata: { since: "1.0.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + cursor_back: { kind: "function", metadata: { since: "unstable" } }, + cursor_back_mut: { kind: "function", metadata: { since: "unstable" } }, + cursor_front: { kind: "function", metadata: { since: "unstable" } }, + cursor_front_mut: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + remove: { kind: "function", metadata: { since: "unstable" } }, + retain: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_vec_deque = { + Drain: { kind: "struct", metadata: { since: "1.6.0" } }, + ExtractIf: { + kind: "struct", metadata: { since: "unstable" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIter: { kind: "struct", metadata: { since: "1.0.0" } }, + Iter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slices: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IterMut: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_mut_slices: { kind: "function", metadata: { since: "unstable" } }, + as_slices: { kind: "function", metadata: { since: "unstable" } }, + into_slices: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Splice: { kind: "struct", metadata: { since: "unstable" } }, + VecDeque: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_slices: { kind: "function", metadata: { since: "1.5.0" } }, + as_slices: { kind: "function", metadata: { since: "1.5.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + binary_search: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by_key: { kind: "function", metadata: { since: "1.54.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "unstable" } }, + extend_front: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.5.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + make_contiguous: { kind: "function", metadata: { since: "1.48.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + partition_point: { kind: "function", metadata: { since: "1.54.0" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_back_if: { kind: "function", metadata: { since: "1.93.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front_if: { kind: "function", metadata: { since: "1.93.0" } }, + prepend: { kind: "function", metadata: { since: "unstable" } }, + prepend_from_within: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + range: { kind: "function", metadata: { since: "1.51.0" } }, + range_mut: { kind: "function", metadata: { since: "1.51.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.16.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.4.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.36.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.36.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.5.0" } }, + splice: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + swap_remove_back: { kind: "function", metadata: { since: "1.5.0" } }, + swap_remove_front: { kind: "function", metadata: { since: "1.5.0" } }, + truncate: { kind: "function", metadata: { since: "1.16.0" } }, + truncate_front: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/ffi.ts b/packages/rust/src/builtins/alloc/ffi.ts new file mode 100644 index 000000000..0785c6f91 --- /dev/null +++ b/packages/rust/src/builtins/alloc/ffi.ts @@ -0,0 +1,88 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ffi = { + CString: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + as_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_vec_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_vec_with_nul: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + from_vec_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + into_boxed_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.7.0" } }, + into_bytes_with_nul: { kind: "function", metadata: { since: "1.7.0" } }, + into_raw: { kind: "function", metadata: { since: "1.4.0" } }, + into_string: { kind: "function", metadata: { since: "1.7.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + FromVecWithNulError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, + IntoStringError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + into_cstring: { kind: "function", metadata: { since: "1.7.0" } }, + utf8_error: { kind: "function", metadata: { since: "1.7.0" } }, + }, + }, + NulError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + into_vec: { kind: "function", metadata: { since: "1.0.0" } }, + nul_position: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_ffi_c_str = { + CString: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + as_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_vec_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_vec_with_nul: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + from_vec_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + into_boxed_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.7.0" } }, + into_bytes_with_nul: { kind: "function", metadata: { since: "1.7.0" } }, + into_raw: { kind: "function", metadata: { since: "1.4.0" } }, + into_string: { kind: "function", metadata: { since: "1.7.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + FromVecWithNulError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, + IntoStringError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + into_cstring: { kind: "function", metadata: { since: "1.7.0" } }, + utf8_error: { kind: "function", metadata: { since: "1.7.0" } }, + }, + }, + NulError: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + into_vec: { kind: "function", metadata: { since: "1.0.0" } }, + nul_position: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/fmt.ts b/packages/rust/src/builtins/alloc/fmt.ts new file mode 100644 index 000000000..12b7934c6 --- /dev/null +++ b/packages/rust/src/builtins/alloc/fmt.ts @@ -0,0 +1,34 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_fmt = { + Alignment: { kind: "enum" }, + Arguments: { kind: "struct" }, + Binary: { kind: "trait" }, + Debug: { kind: "trait" }, + DebugAsHex: { kind: "enum" }, + DebugList: { kind: "struct" }, + DebugMap: { kind: "struct" }, + DebugSet: { kind: "struct" }, + DebugStruct: { kind: "struct" }, + DebugTuple: { kind: "struct" }, + Display: { kind: "trait" }, + Error: { kind: "struct" }, + format: { kind: "function", metadata: { since: "1.0.0" } }, + Formatter: { kind: "struct" }, + FormattingOptions: { kind: "struct" }, + from_fn: { kind: "function" }, + FromFn: { kind: "struct" }, + LowerExp: { kind: "trait" }, + LowerHex: { kind: "trait" }, + Octal: { kind: "trait" }, + Pointer: { kind: "trait" }, + Result: { kind: "type-alias" }, + Sign: { kind: "enum" }, + UpperExp: { kind: "trait" }, + UpperHex: { kind: "trait" }, + write: { kind: "function" }, + Write: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/index.ts b/packages/rust/src/builtins/alloc/index.ts new file mode 100644 index 000000000..664da2b9b --- /dev/null +++ b/packages/rust/src/builtins/alloc/index.ts @@ -0,0 +1,58 @@ +import { type SymbolCreator } from "@alloy-js/core"; +import { + type CrateDescriptor, + type CrateRef, + createCrate, + type ExternalCrate, +} from "../../create-crate.js"; +import { mod_alloc } from "./alloc.js"; +import { mod_borrow } from "./borrow.js"; +import { mod_boxed } from "./boxed.js"; +import { mod_collections, mod_collections_binary_heap, mod_collections_linked_list, mod_collections_vec_deque } from "./collections.js"; +import { mod_ffi, mod_ffi_c_str } from "./ffi.js"; +import { mod_fmt } from "./fmt.js"; +import { mod_rc } from "./rc.js"; +import { mod_root } from "./root.js"; +import { mod_slice } from "./slice.js"; +import { mod_str, mod_str_pattern } from "./str.js"; +import { mod_string } from "./string.js"; +import { mod_sync } from "./sync.js"; +import { mod_task } from "./task.js"; +import { mod_vec } from "./vec.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +const allocDescriptor = { + name: "alloc", + builtin: true, + modules: { + "alloc": mod_alloc, + "borrow": mod_borrow, + "boxed": mod_boxed, + "collections": mod_collections, + "collections::binary_heap": mod_collections_binary_heap, + "collections::linked_list": mod_collections_linked_list, + "collections::vec_deque": mod_collections_vec_deque, + "ffi": mod_ffi, + "ffi::c_str": mod_ffi_c_str, + "fmt": mod_fmt, + "rc": mod_rc, + "": mod_root, + "slice": mod_slice, + "str": mod_str, + "str::pattern": mod_str_pattern, + "string": mod_string, + "sync": mod_sync, + "task": mod_task, + "vec": mod_vec, + }, +} as const satisfies CrateDescriptor; + +/** + * The `alloc` crate descriptor. + */ +export type AllocCrate = CrateRef & + SymbolCreator & + ExternalCrate; +export const alloc: AllocCrate = createCrate(allocDescriptor); diff --git a/packages/rust/src/builtins/alloc/rc.ts b/packages/rust/src/builtins/alloc/rc.ts new file mode 100644 index 000000000..166fdbd30 --- /dev/null +++ b/packages/rust/src/builtins/alloc/rc.ts @@ -0,0 +1,90 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_rc = { + Rc: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + decrement_strong_count: { kind: "function", associated: true, metadata: { since: "1.53.0" } }, + decrement_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.29.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + downgrade: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + get_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + increment_strong_count: { kind: "function", associated: true, metadata: { since: "1.53.0" } }, + increment_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + make_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_cyclic: { kind: "function", associated: true, metadata: { since: "1.60.0" } }, + new_cyclic_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + strong_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_unwrap: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + unwrap_or_clone: { kind: "function", associated: true, metadata: { since: "1.76.0" } }, + weak_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + }, + }, + UniqueRc: { + kind: "struct", metadata: { since: "unstable" }, + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_rc: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Weak: { + kind: "struct", metadata: { since: "1.4.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.45.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_raw: { kind: "function", metadata: { since: "1.45.0" } }, + into_raw_with_allocator: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", metadata: { since: "1.39.0" } }, + strong_count: { kind: "function", metadata: { since: "1.41.0" } }, + upgrade: { kind: "function", metadata: { since: "1.4.0" } }, + weak_count: { kind: "function", metadata: { since: "1.41.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/root.ts b/packages/rust/src/builtins/alloc/root.ts new file mode 100644 index 000000000..c3958a41c --- /dev/null +++ b/packages/rust/src/builtins/alloc/root.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_root = { + format: { kind: "symbol", metadata: { since: "1.0.0" } }, + vec: { kind: "symbol", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/slice.ts b/packages/rust/src/builtins/alloc/slice.ts new file mode 100644 index 000000000..df825d51e --- /dev/null +++ b/packages/rust/src/builtins/alloc/slice.ts @@ -0,0 +1,44 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_slice = { + ArrayWindows: { kind: "struct" }, + ChunkBy: { kind: "struct" }, + ChunkByMut: { kind: "struct" }, + Chunks: { kind: "struct" }, + ChunksExact: { kind: "struct" }, + ChunksExactMut: { kind: "struct" }, + ChunksMut: { kind: "struct" }, + Concat: { kind: "trait", metadata: { since: "unstable" } }, + EscapeAscii: { kind: "struct" }, + from_mut: { kind: "function" }, + from_mut_ptr_range: { kind: "function" }, + from_ptr_range: { kind: "function" }, + from_raw_parts: { kind: "function" }, + from_raw_parts_mut: { kind: "function" }, + from_ref: { kind: "function" }, + GetDisjointMutError: { kind: "enum" }, + Iter: { kind: "struct" }, + IterMut: { kind: "struct" }, + Join: { kind: "trait", metadata: { since: "unstable" } }, + range: { kind: "function" }, + RChunks: { kind: "struct" }, + RChunksExact: { kind: "struct" }, + RChunksExactMut: { kind: "struct" }, + RChunksMut: { kind: "struct" }, + RSplit: { kind: "struct" }, + RSplitMut: { kind: "struct" }, + RSplitN: { kind: "struct" }, + RSplitNMut: { kind: "struct" }, + SliceIndex: { kind: "trait" }, + Split: { kind: "struct" }, + SplitInclusive: { kind: "struct" }, + SplitInclusiveMut: { kind: "struct" }, + SplitMut: { kind: "struct" }, + SplitN: { kind: "struct" }, + SplitNMut: { kind: "struct" }, + try_range: { kind: "function" }, + Windows: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/str.ts b/packages/rust/src/builtins/alloc/str.ts new file mode 100644 index 000000000..f65ec55e2 --- /dev/null +++ b/packages/rust/src/builtins/alloc/str.ts @@ -0,0 +1,61 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_str = { + Bytes: { kind: "struct" }, + CharIndices: { kind: "struct" }, + Chars: { kind: "struct" }, + EncodeUtf16: { kind: "struct" }, + EscapeDebug: { kind: "struct" }, + EscapeDefault: { kind: "struct" }, + EscapeUnicode: { kind: "struct" }, + from_boxed_utf8_unchecked: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw_parts: { kind: "function" }, + from_raw_parts_mut: { kind: "function" }, + from_utf8: { kind: "function" }, + from_utf8_mut: { kind: "function" }, + from_utf8_unchecked: { kind: "function" }, + from_utf8_unchecked_mut: { kind: "function" }, + FromStr: { kind: "trait" }, + Lines: { kind: "struct" }, + LinesAny: { kind: "struct" }, + Matches: { kind: "struct" }, + MatchIndices: { kind: "struct" }, + ParseBoolError: { kind: "struct" }, + RMatches: { kind: "struct" }, + RMatchIndices: { kind: "struct" }, + RSplit: { kind: "struct" }, + RSplitN: { kind: "struct" }, + RSplitTerminator: { kind: "struct" }, + Split: { kind: "struct" }, + SplitAsciiWhitespace: { kind: "struct" }, + SplitInclusive: { kind: "struct" }, + SplitN: { kind: "struct" }, + SplitTerminator: { kind: "struct" }, + SplitWhitespace: { kind: "struct" }, + Utf8Chunk: { kind: "struct" }, + Utf8Chunks: { kind: "struct" }, + Utf8Error: { kind: "struct" }, +} as const satisfies Record; + +export const mod_str_pattern = { + CharArrayRefSearcher: { kind: "struct" }, + CharArraySearcher: { kind: "struct" }, + CharPredicateSearcher: { kind: "struct" }, + CharSearcher: { kind: "struct" }, + CharSliceSearcher: { kind: "struct" }, + DoubleEndedSearcher: { kind: "trait" }, + EmptyNeedle: { kind: "struct" }, + MultiCharEqPattern: { kind: "struct" }, + MultiCharEqSearcher: { kind: "struct" }, + Pattern: { kind: "trait" }, + ReverseSearcher: { kind: "trait" }, + Searcher: { kind: "trait" }, + SearchStep: { kind: "enum" }, + StrSearcher: { kind: "struct" }, + StrSearcherImpl: { kind: "enum" }, + TwoWaySearcher: { kind: "struct" }, + Utf8Pattern: { kind: "enum" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/string.ts b/packages/rust/src/builtins/alloc/string.ts new file mode 100644 index 000000000..889c2066d --- /dev/null +++ b/packages/rust/src/builtins/alloc/string.ts @@ -0,0 +1,85 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_string = { + Drain: { + kind: "struct", metadata: { since: "1.6.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.55.0" } }, + }, + }, + FromUtf16Error: { kind: "struct", metadata: { since: "1.0.0" } }, + FromUtf8Error: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.26.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + into_utf8_lossy: { kind: "function", metadata: { since: "unstable" } }, + utf8_error: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + IntoChars: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_str: { kind: "function", metadata: { since: "unstable" } }, + into_string: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ParseError: { kind: "type-alias", metadata: { since: "1.5.0" } }, + String: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_mut_str: { kind: "function", metadata: { since: "1.7.0" } }, + as_mut_vec: { kind: "function", metadata: { since: "1.0.0" } }, + as_str: { kind: "function", metadata: { since: "1.7.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "1.87.0" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16_lossy: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16be_lossy: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16le_lossy: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf8: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf8_lossy: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf8_lossy_owned: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf8_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + insert_str: { kind: "function", metadata: { since: "1.16.0" } }, + into_boxed_str: { kind: "function", metadata: { since: "1.4.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + into_chars: { kind: "function", metadata: { since: "unstable" } }, + into_raw_parts: { kind: "function", metadata: { since: "1.93.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.72.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + push_str: { kind: "function", metadata: { since: "1.0.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + remove_matches: { kind: "function", metadata: { since: "unstable" } }, + replace_first: { kind: "function", metadata: { since: "unstable" } }, + replace_last: { kind: "function", metadata: { since: "unstable" } }, + replace_range: { kind: "function", metadata: { since: "1.27.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.26.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + split_off: { kind: "function", metadata: { since: "1.16.0" } }, + truncate: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + ToString: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/sync.ts b/packages/rust/src/builtins/alloc/sync.ts new file mode 100644 index 000000000..d955216f8 --- /dev/null +++ b/packages/rust/src/builtins/alloc/sync.ts @@ -0,0 +1,93 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_sync = { + Arc: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + decrement_strong_count: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + decrement_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.29.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + downgrade: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + get_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + increment_strong_count: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + increment_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_unique: { kind: "function", associated: true, metadata: { since: "unstable" } }, + make_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_cyclic: { kind: "function", associated: true, metadata: { since: "1.60.0" } }, + new_cyclic_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + strong_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_pin: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_unwrap: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + unwrap_or_clone: { kind: "function", associated: true, metadata: { since: "1.76.0" } }, + weak_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + }, + }, + UniqueArc: { + kind: "struct", metadata: { since: "unstable" }, + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_arc: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Weak: { + kind: "struct", metadata: { since: "1.4.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.45.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_raw: { kind: "function", metadata: { since: "1.45.0" } }, + into_raw_with_allocator: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", metadata: { since: "1.39.0" } }, + strong_count: { kind: "function", metadata: { since: "1.41.0" } }, + upgrade: { kind: "function", metadata: { since: "1.4.0" } }, + weak_count: { kind: "function", metadata: { since: "1.41.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/task.ts b/packages/rust/src/builtins/alloc/task.ts new file mode 100644 index 000000000..c2553985f --- /dev/null +++ b/packages/rust/src/builtins/alloc/task.ts @@ -0,0 +1,11 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_task = { + local_waker_fn: { kind: "function", metadata: { since: "unstable" } }, + LocalWake: { kind: "trait", metadata: { since: "unstable" } }, + Wake: { kind: "trait", metadata: { since: "1.51.0" } }, + waker_fn: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/alloc/vec.ts b/packages/rust/src/builtins/alloc/vec.ts new file mode 100644 index 000000000..61853a583 --- /dev/null +++ b/packages/rust/src/builtins/alloc/vec.ts @@ -0,0 +1,109 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_vec = { + Drain: { + kind: "struct", metadata: { since: "1.6.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.46.0" } }, + keep_rest: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExtractIf: { + kind: "struct", metadata: { since: "1.87.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.15.0" } }, + as_slice: { kind: "function", metadata: { since: "1.15.0" } }, + }, + }, + PeekMut: { + kind: "struct", metadata: { since: "unstable" }, + members: { + pop: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Splice: { kind: "struct", metadata: { since: "1.21.0" } }, + Vec: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_ptr: { kind: "function", metadata: { since: "1.37.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.7.0" } }, + as_non_null: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.37.0" } }, + as_slice: { kind: "function", metadata: { since: "1.7.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + const_make_global: { kind: "function", metadata: { since: "unstable" } }, + dedup: { kind: "function", metadata: { since: "1.0.0" } }, + dedup_by: { kind: "function", metadata: { since: "1.16.0" } }, + dedup_by_key: { kind: "function", metadata: { since: "1.16.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_slice: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "1.53.0" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + from_fn: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_parts: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_parts_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_raw_parts_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + into_boxed_slice: { kind: "function", metadata: { since: "1.0.0" } }, + into_chunks: { kind: "function", metadata: { since: "unstable" } }, + into_flattened: { kind: "function", metadata: { since: "1.80.0" } }, + into_parts: { kind: "function", metadata: { since: "unstable" } }, + into_parts_with_alloc: { kind: "function", metadata: { since: "unstable" } }, + into_raw_parts: { kind: "function", metadata: { since: "1.93.0" } }, + into_raw_parts_with_alloc: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.47.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek_mut: { kind: "function", metadata: { since: "unstable" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "1.86.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + push_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_within_capacity: { kind: "function", metadata: { since: "unstable" } }, + recycle: { kind: "function", metadata: { since: "unstable" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.5.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.0.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + set_len: { kind: "function", metadata: { since: "1.0.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + spare_capacity_mut: { kind: "function", metadata: { since: "1.60.0" } }, + splice: { kind: "function", metadata: { since: "1.21.0" } }, + split_at_spare_mut: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap_remove: { kind: "function", metadata: { since: "1.0.0" } }, + truncate: { kind: "function", metadata: { since: "1.0.0" } }, + try_remove: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_shrink_to: { kind: "function", metadata: { since: "unstable" } }, + try_shrink_to_fit: { kind: "function", metadata: { since: "unstable" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/alloc.ts b/packages/rust/src/builtins/core/alloc.ts new file mode 100644 index 000000000..959aff782 --- /dev/null +++ b/packages/rust/src/builtins/core/alloc.ts @@ -0,0 +1,37 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_alloc = { + Allocator: { kind: "trait", metadata: { since: "unstable" } }, + AllocError: { kind: "struct", metadata: { since: "unstable" } }, + GlobalAlloc: { kind: "trait", metadata: { since: "1.28.0" } }, + Layout: { + kind: "struct", metadata: { since: "1.28.0" }, + members: { + adjust_alignment_to: { kind: "function", metadata: { since: "unstable" } }, + align: { kind: "function", metadata: { since: "1.28.0" } }, + align_to: { kind: "function", metadata: { since: "1.44.0" } }, + alignment: { kind: "function", metadata: { since: "unstable" } }, + array: { kind: "function", associated: true, metadata: { since: "1.44.0" } }, + dangling_ptr: { kind: "function", metadata: { since: "1.95.0" } }, + extend: { kind: "function", metadata: { since: "1.44.0" } }, + extend_packed: { kind: "function", metadata: { since: "1.95.0" } }, + for_value: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + for_value_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_size_align: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + from_size_align_unchecked: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + from_size_alignment: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_size_alignment_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + pad_to_align: { kind: "function", metadata: { since: "1.44.0" } }, + padding_needed_for: { kind: "function", metadata: { since: "unstable" } }, + repeat: { kind: "function", metadata: { since: "1.95.0" } }, + repeat_packed: { kind: "function", metadata: { since: "1.95.0" } }, + size: { kind: "function", metadata: { since: "1.28.0" } }, + }, + }, + LayoutErr: { kind: "type-alias", metadata: { since: "1.28.0" } }, + LayoutError: { kind: "struct", metadata: { since: "1.50.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/any.ts b/packages/rust/src/builtins/core/any.ts new file mode 100644 index 000000000..1c56d28a1 --- /dev/null +++ b/packages/rust/src/builtins/core/any.ts @@ -0,0 +1,21 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_any = { + Any: { kind: "trait", metadata: { since: "1.0.0" } }, + try_as_dyn: { kind: "function", metadata: { since: "unstable" } }, + try_as_dyn_mut: { kind: "function", metadata: { since: "unstable" } }, + type_name: { kind: "function", metadata: { since: "1.38.0" } }, + type_name_of_val: { kind: "function", metadata: { since: "1.76.0" } }, + TypeId: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + info: { kind: "function", metadata: { since: "unstable" } }, + of: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + trait_info_of: { kind: "function", metadata: { since: "unstable" } }, + trait_info_of_trait_type_id: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/array.ts b/packages/rust/src/builtins/core/array.ts new file mode 100644 index 000000000..27cfd4e67 --- /dev/null +++ b/packages/rust/src/builtins/core/array.ts @@ -0,0 +1,23 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_array = { + from_fn: { kind: "function", metadata: { since: "1.63.0" } }, + from_mut: { kind: "function", metadata: { since: "1.53.0" } }, + from_ref: { kind: "function", metadata: { since: "1.53.0" } }, + IntoIter: { + kind: "struct", metadata: { since: "1.51.0" }, + members: { + as_mut_slice: { kind: "function", metadata: { since: "1.51.0" } }, + as_slice: { kind: "function", metadata: { since: "1.51.0" } }, + empty: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + repeat: { kind: "function", metadata: { since: "1.91.0" } }, + try_from_fn: { kind: "function", metadata: { since: "unstable" } }, + TryFromSliceError: { kind: "struct", metadata: { since: "1.34.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/ascii.ts b/packages/rust/src/builtins/core/ascii.ts new file mode 100644 index 000000000..07ed59331 --- /dev/null +++ b/packages/rust/src/builtins/core/ascii.ts @@ -0,0 +1,38 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ascii = { + Char: { + kind: "enum", metadata: { since: "unstable" }, + members: { + as_str: { kind: "function", metadata: { since: "unstable" } }, + digit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + digit_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + eq_ignore_case: { kind: "function", metadata: { since: "unstable" } }, + escape_ascii: { kind: "function", metadata: { since: "unstable" } }, + from_u8: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_u8_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_alphabetic: { kind: "function", metadata: { since: "unstable" } }, + is_alphanumeric: { kind: "function", metadata: { since: "unstable" } }, + is_control: { kind: "function", metadata: { since: "unstable" } }, + is_digit: { kind: "function", metadata: { since: "unstable" } }, + is_graphic: { kind: "function", metadata: { since: "unstable" } }, + is_hexdigit: { kind: "function", metadata: { since: "unstable" } }, + is_lowercase: { kind: "function", metadata: { since: "unstable" } }, + is_octdigit: { kind: "function", metadata: { since: "unstable" } }, + is_punctuation: { kind: "function", metadata: { since: "unstable" } }, + is_uppercase: { kind: "function", metadata: { since: "unstable" } }, + is_whitespace: { kind: "function", metadata: { since: "unstable" } }, + make_lowercase: { kind: "function", metadata: { since: "unstable" } }, + make_uppercase: { kind: "function", metadata: { since: "unstable" } }, + to_char: { kind: "function", metadata: { since: "unstable" } }, + to_lowercase: { kind: "function", metadata: { since: "unstable" } }, + to_u8: { kind: "function", metadata: { since: "unstable" } }, + to_uppercase: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + escape_default: { kind: "function", metadata: { since: "1.0.0" } }, + EscapeDefault: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/borrow.ts b/packages/rust/src/builtins/core/borrow.ts new file mode 100644 index 000000000..ac3b196cb --- /dev/null +++ b/packages/rust/src/builtins/core/borrow.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_borrow = { + Borrow: { kind: "trait", metadata: { since: "1.0.0" } }, + BorrowMut: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/cell.ts b/packages/rust/src/builtins/core/cell.ts new file mode 100644 index 000000000..4e63e3825 --- /dev/null +++ b/packages/rust/src/builtins/core/cell.ts @@ -0,0 +1,120 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_cell = { + BorrowError: { kind: "struct", metadata: { since: "1.13.0" } }, + BorrowMutError: { kind: "struct", metadata: { since: "1.13.0" } }, + Cell: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_array_of_cells: { kind: "function", metadata: { since: "1.91.0" } }, + as_ptr: { kind: "function", metadata: { since: "1.12.0" } }, + as_slice_of_cells: { kind: "function", metadata: { since: "1.37.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.37.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_cloned: { kind: "function" }, + get_mut: { kind: "function", metadata: { since: "1.11.0" } }, + into_inner: { kind: "function", metadata: { since: "1.17.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.17.0" } }, + set: { kind: "function", metadata: { since: "1.0.0" } }, + swap: { kind: "function", metadata: { since: "1.17.0" } }, + take: { kind: "function", metadata: { since: "1.17.0" } }, + update: { kind: "function", metadata: { since: "1.88.0" } }, + }, + }, + CloneFromCell: { kind: "trait", metadata: { since: "unstable" } }, + LazyCell: { + kind: "struct", metadata: { since: "1.80.0" }, + members: { + force: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + force_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + }, + }, + OnceCell: { + kind: "struct", metadata: { since: "1.70.0" }, + members: { + get: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut_or_init: { kind: "function", metadata: { since: "unstable" } }, + get_mut_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + get_or_init: { kind: "function", metadata: { since: "1.70.0" } }, + get_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "1.70.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + set: { kind: "function", metadata: { since: "1.70.0" } }, + take: { kind: "function", metadata: { since: "1.70.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Ref: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + clone: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + filter_map: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + leak: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + map_split: { kind: "function", associated: true, metadata: { since: "1.35.0" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RefCell: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_ptr: { kind: "function", metadata: { since: "1.12.0" } }, + borrow: { kind: "function", metadata: { since: "1.0.0" } }, + borrow_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.11.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.24.0" } }, + replace_with: { kind: "function", metadata: { since: "1.35.0" } }, + swap: { kind: "function", metadata: { since: "1.24.0" } }, + take: { kind: "function", metadata: { since: "1.50.0" } }, + try_borrow: { kind: "function", metadata: { since: "1.13.0" } }, + try_borrow_mut: { kind: "function", metadata: { since: "1.13.0" } }, + try_borrow_unguarded: { kind: "function", metadata: { since: "1.37.0" } }, + undo_leak: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RefMut: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + leak: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + map_split: { kind: "function", associated: true, metadata: { since: "1.35.0" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + SyncUnsafeCell: { + kind: "struct", metadata: { since: "unstable" }, + members: { + get: { kind: "function" }, + get_mut: { kind: "function" }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true }, + raw_get: { kind: "function", associated: true }, + }, + }, + UnsafeCell: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_mut_unchecked: { kind: "function", metadata: { since: "unstable" } }, + as_ref_unchecked: { kind: "function", metadata: { since: "unstable" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.50.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + raw_get: { kind: "function", associated: true, metadata: { since: "1.56.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/char.ts b/packages/rust/src/builtins/core/char.ts new file mode 100644 index 000000000..3a5141544 --- /dev/null +++ b/packages/rust/src/builtins/core/char.ts @@ -0,0 +1,33 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_char_ = { + CharCase: { kind: "enum", metadata: { since: "unstable" } }, + CharTryFromError: { kind: "struct", metadata: { since: "1.34.0" } }, + decode_utf16: { kind: "function", metadata: { since: "1.9.0" } }, + DecodeUtf16: { kind: "struct", metadata: { since: "1.9.0" } }, + DecodeUtf16Error: { + kind: "struct", metadata: { since: "1.9.0" }, + members: { + unpaired_surrogate: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + EscapeDebug: { kind: "struct", metadata: { since: "1.20.0" } }, + EscapeDefault: { kind: "struct", metadata: { since: "1.0.0" } }, + EscapeUnicode: { kind: "struct", metadata: { since: "1.0.0" } }, + from_digit: { kind: "function", metadata: { since: "1.0.0" } }, + from_u32: { kind: "function", metadata: { since: "1.0.0" } }, + from_u32_unchecked: { kind: "function", metadata: { since: "1.5.0" } }, + MAX: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_LEN_UTF16: { kind: "const", metadata: { since: "unstable" } }, + MAX_LEN_UTF8: { kind: "const", metadata: { since: "unstable" } }, + ParseCharError: { kind: "struct", metadata: { since: "1.20.0" } }, + REPLACEMENT_CHARACTER: { kind: "const", metadata: { since: "1.9.0" } }, + ToLowercase: { kind: "struct", metadata: { since: "1.0.0" } }, + ToTitlecase: { kind: "struct", metadata: { since: "unstable" } }, + ToUppercase: { kind: "struct", metadata: { since: "1.0.0" } }, + TryFromCharError: { kind: "struct", metadata: { since: "1.59.0" } }, + UNICODE_VERSION: { kind: "const", metadata: { since: "1.45.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/clone.ts b/packages/rust/src/builtins/core/clone.ts new file mode 100644 index 000000000..eed039612 --- /dev/null +++ b/packages/rust/src/builtins/core/clone.ts @@ -0,0 +1,11 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_clone = { + Clone: { kind: "trait", metadata: { since: "1.0.0" } }, + CloneToUninit: { kind: "trait", metadata: { since: "unstable" } }, + TrivialClone: { kind: "trait", metadata: { since: "unstable" } }, + UseCloned: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/cmp.ts b/packages/rust/src/builtins/core/cmp.ts new file mode 100644 index 000000000..82d457b45 --- /dev/null +++ b/packages/rust/src/builtins/core/cmp.ts @@ -0,0 +1,35 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_cmp = { + Eq: { kind: "trait", metadata: { since: "1.0.0" } }, + max: { kind: "function", metadata: { since: "1.0.0" } }, + max_by: { kind: "function", metadata: { since: "1.53.0" } }, + max_by_key: { kind: "function", metadata: { since: "1.53.0" } }, + min: { kind: "function", metadata: { since: "1.0.0" } }, + min_by: { kind: "function", metadata: { since: "1.53.0" } }, + min_by_key: { kind: "function", metadata: { since: "1.53.0" } }, + minmax: { kind: "function", metadata: { since: "unstable" } }, + minmax_by: { kind: "function", metadata: { since: "unstable" } }, + minmax_by_key: { kind: "function", metadata: { since: "unstable" } }, + Ord: { kind: "trait", metadata: { since: "1.0.0" } }, + Ordering: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + is_eq: { kind: "function", metadata: { since: "1.53.0" } }, + is_ge: { kind: "function", metadata: { since: "1.53.0" } }, + is_gt: { kind: "function", metadata: { since: "1.53.0" } }, + is_le: { kind: "function", metadata: { since: "1.53.0" } }, + is_lt: { kind: "function", metadata: { since: "1.53.0" } }, + is_ne: { kind: "function", metadata: { since: "1.53.0" } }, + reverse: { kind: "function", metadata: { since: "1.48.0" } }, + then: { kind: "function", metadata: { since: "1.48.0" } }, + then_with: { kind: "function", metadata: { since: "1.17.0" } }, + }, + }, + PartialEq: { kind: "trait", metadata: { since: "1.0.0" } }, + PartialOrd: { kind: "trait", metadata: { since: "1.0.0" } }, + Reverse: { kind: "struct", metadata: { since: "1.19.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/contracts.ts b/packages/rust/src/builtins/core/contracts.ts new file mode 100644 index 000000000..86067df67 --- /dev/null +++ b/packages/rust/src/builtins/core/contracts.ts @@ -0,0 +1,8 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_contracts = { + build_check_ensures: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/convert.ts b/packages/rust/src/builtins/core/convert.ts new file mode 100644 index 000000000..ddc99096b --- /dev/null +++ b/packages/rust/src/builtins/core/convert.ts @@ -0,0 +1,16 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_convert = { + AsMut: { kind: "trait", metadata: { since: "1.0.0" } }, + AsRef: { kind: "trait", metadata: { since: "1.0.0" } }, + FloatToInt: { kind: "trait", metadata: { since: "unstable" } }, + From: { kind: "trait", metadata: { since: "1.0.0" } }, + identity: { kind: "function", metadata: { since: "1.33.0" } }, + Infallible: { kind: "enum", metadata: { since: "1.34.0" } }, + Into: { kind: "trait", metadata: { since: "1.0.0" } }, + TryFrom: { kind: "trait", metadata: { since: "1.34.0" } }, + TryInto: { kind: "trait", metadata: { since: "1.34.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/default.ts b/packages/rust/src/builtins/core/default.ts new file mode 100644 index 000000000..4ced5dd08 --- /dev/null +++ b/packages/rust/src/builtins/core/default.ts @@ -0,0 +1,8 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_default_ = { + Default: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/error.ts b/packages/rust/src/builtins/core/error.ts new file mode 100644 index 000000000..ed780fa2c --- /dev/null +++ b/packages/rust/src/builtins/core/error.ts @@ -0,0 +1,22 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_error = { + Error: { kind: "trait", metadata: { since: "1.0.0" } }, + Request: { + kind: "struct", metadata: { since: "unstable" }, + members: { + provide_ref: { kind: "function", metadata: { since: "unstable" } }, + provide_ref_with: { kind: "function", metadata: { since: "unstable" } }, + provide_value: { kind: "function", metadata: { since: "unstable" } }, + provide_value_with: { kind: "function", metadata: { since: "unstable" } }, + would_be_satisfied_by_ref_of: { kind: "function", metadata: { since: "unstable" } }, + would_be_satisfied_by_value_of: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + request_ref: { kind: "function", metadata: { since: "unstable" } }, + request_value: { kind: "function", metadata: { since: "unstable" } }, + Source: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/f32.ts b/packages/rust/src/builtins/core/f32.ts new file mode 100644 index 000000000..da80bb12e --- /dev/null +++ b/packages/rust/src/builtins/core/f32.ts @@ -0,0 +1,67 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_f32 = { + DIGITS: { kind: "const", metadata: { since: "1.0.0" } }, + EPSILON: { kind: "const", metadata: { since: "1.0.0" } }, + INFINITY: { kind: "const", metadata: { since: "1.0.0" } }, + MANTISSA_DIGITS: { kind: "const", metadata: { since: "1.0.0" } }, + MAX: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_10_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_10_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_POSITIVE: { kind: "const", metadata: { since: "1.0.0" } }, + NAN: { kind: "const", metadata: { since: "1.0.0" } }, + NEG_INFINITY: { kind: "const", metadata: { since: "1.0.0" } }, + RADIX: { kind: "const", metadata: { since: "1.0.0" } }, +} as const satisfies Record; + +export const mod_f32_consts = { + E: { kind: "const", metadata: { since: "1.0.0" } }, + EULER_GAMMA: { kind: "const", metadata: { since: "1.94.0" } }, + FRAC_1_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_2_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_2_SQRT_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_3: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_4: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_6: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_8: { kind: "const", metadata: { since: "1.0.0" } }, + GOLDEN_RATIO: { kind: "const", metadata: { since: "1.94.0" } }, + LN_10: { kind: "const", metadata: { since: "1.0.0" } }, + LN_2: { kind: "const", metadata: { since: "1.0.0" } }, + LOG10_2: { kind: "const", metadata: { since: "1.43.0" } }, + LOG10_E: { kind: "const", metadata: { since: "1.0.0" } }, + LOG2_10: { kind: "const", metadata: { since: "1.43.0" } }, + LOG2_E: { kind: "const", metadata: { since: "1.0.0" } }, + PI: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + TAU: { kind: "const", metadata: { since: "1.47.0" } }, +} as const satisfies Record; + +export const mod_f32_math = { + abs_sub: { kind: "function", metadata: { since: "1.0.0" } }, + cbrt: { kind: "function", metadata: { since: "unstable" } }, + ceil: { kind: "function", metadata: { since: "unstable" } }, + div_euclid: { kind: "function", metadata: { since: "unstable" } }, + floor: { kind: "function", metadata: { since: "unstable" } }, + fract: { kind: "function", metadata: { since: "unstable" } }, + mul_add: { kind: "function", metadata: { since: "unstable" } }, + powi: { kind: "function", metadata: { since: "unstable" } }, + rem_euclid: { kind: "function", metadata: { since: "unstable" } }, + round: { kind: "function", metadata: { since: "unstable" } }, + round_ties_even: { kind: "function", metadata: { since: "unstable" } }, + sqrt: { kind: "function", metadata: { since: "unstable" } }, + trunc: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/f64.ts b/packages/rust/src/builtins/core/f64.ts new file mode 100644 index 000000000..c2b535f66 --- /dev/null +++ b/packages/rust/src/builtins/core/f64.ts @@ -0,0 +1,67 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_f64 = { + DIGITS: { kind: "const", metadata: { since: "1.0.0" } }, + EPSILON: { kind: "const", metadata: { since: "1.0.0" } }, + INFINITY: { kind: "const", metadata: { since: "1.0.0" } }, + MANTISSA_DIGITS: { kind: "const", metadata: { since: "1.0.0" } }, + MAX: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_10_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_10_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_EXP: { kind: "const", metadata: { since: "1.0.0" } }, + MIN_POSITIVE: { kind: "const", metadata: { since: "1.0.0" } }, + NAN: { kind: "const", metadata: { since: "1.0.0" } }, + NEG_INFINITY: { kind: "const", metadata: { since: "1.0.0" } }, + RADIX: { kind: "const", metadata: { since: "1.0.0" } }, +} as const satisfies Record; + +export const mod_f64_consts = { + E: { kind: "const", metadata: { since: "1.0.0" } }, + EULER_GAMMA: { kind: "const", metadata: { since: "1.94.0" } }, + FRAC_1_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_2_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_2_SQRT_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_3: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_4: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_6: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_8: { kind: "const", metadata: { since: "1.0.0" } }, + GOLDEN_RATIO: { kind: "const", metadata: { since: "1.94.0" } }, + LN_10: { kind: "const", metadata: { since: "1.0.0" } }, + LN_2: { kind: "const", metadata: { since: "1.0.0" } }, + LOG10_2: { kind: "const", metadata: { since: "1.43.0" } }, + LOG10_E: { kind: "const", metadata: { since: "1.0.0" } }, + LOG2_10: { kind: "const", metadata: { since: "1.43.0" } }, + LOG2_E: { kind: "const", metadata: { since: "1.0.0" } }, + PI: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + TAU: { kind: "const", metadata: { since: "1.47.0" } }, +} as const satisfies Record; + +export const mod_f64_math = { + abs_sub: { kind: "function", metadata: { since: "unstable" } }, + cbrt: { kind: "function", metadata: { since: "unstable" } }, + ceil: { kind: "function", metadata: { since: "unstable" } }, + div_euclid: { kind: "function", metadata: { since: "unstable" } }, + floor: { kind: "function", metadata: { since: "unstable" } }, + fract: { kind: "function", metadata: { since: "unstable" } }, + mul_add: { kind: "function", metadata: { since: "unstable" } }, + powi: { kind: "function", metadata: { since: "unstable" } }, + rem_euclid: { kind: "function", metadata: { since: "unstable" } }, + round: { kind: "function", metadata: { since: "unstable" } }, + round_ties_even: { kind: "function", metadata: { since: "unstable" } }, + sqrt: { kind: "function", metadata: { since: "unstable" } }, + trunc: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/ffi.ts b/packages/rust/src/builtins/core/ffi.ts new file mode 100644 index 000000000..78c0ab787 --- /dev/null +++ b/packages/rust/src/builtins/core/ffi.ts @@ -0,0 +1,85 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ffi = { + c_char: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_double: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_float: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_int: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_long: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_longlong: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_ptrdiff_t: { kind: "type-alias", metadata: { since: "unstable" } }, + c_schar: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_short: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_size_t: { kind: "type-alias", metadata: { since: "unstable" } }, + c_ssize_t: { kind: "type-alias", metadata: { since: "unstable" } }, + c_uchar: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_uint: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_ulong: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_ulonglong: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_ushort: { kind: "type-alias", metadata: { since: "1.64.0" } }, + c_void: { kind: "enum", metadata: { since: "1.30.0" } }, + CStr: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_c_str: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.0.0" } }, + bytes: { kind: "function", metadata: { since: "unstable" } }, + count_bytes: { kind: "function", metadata: { since: "1.79.0" } }, + display: { kind: "function", metadata: { since: "unstable" } }, + from_bytes_until_nul: { kind: "function", associated: true, metadata: { since: "1.69.0" } }, + from_bytes_with_nul: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_bytes_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.71.0" } }, + to_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + to_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + FromBytesUntilNulError: { kind: "struct", metadata: { since: "1.69.0" } }, + FromBytesWithNulError: { kind: "enum", metadata: { since: "1.64.0" } }, + VaArgSafe: { kind: "trait" }, + VaList: { + kind: "struct", + members: { + arg: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_ffi_c_str = { + Bytes: { kind: "struct", metadata: { since: "unstable" } }, + CStr: { + kind: "struct", metadata: { since: "1.64.0" }, + members: { + as_c_str: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.0.0" } }, + bytes: { kind: "function", metadata: { since: "unstable" } }, + count_bytes: { kind: "function", metadata: { since: "1.79.0" } }, + display: { kind: "function", metadata: { since: "unstable" } }, + from_bytes_until_nul: { kind: "function", associated: true, metadata: { since: "1.69.0" } }, + from_bytes_with_nul: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_bytes_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.71.0" } }, + to_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + to_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + FromBytesUntilNulError: { kind: "struct", metadata: { since: "1.69.0" } }, + FromBytesWithNulError: { kind: "enum", metadata: { since: "1.64.0" } }, +} as const satisfies Record; + +export const mod_ffi_va_list = { + VaArgSafe: { kind: "trait" }, + VaList: { + kind: "struct", + members: { + arg: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/fmt.ts b/packages/rust/src/builtins/core/fmt.ts new file mode 100644 index 000000000..740e36b97 --- /dev/null +++ b/packages/rust/src/builtins/core/fmt.ts @@ -0,0 +1,141 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_fmt = { + Alignment: { kind: "enum", metadata: { since: "1.28.0" } }, + Arguments: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.52.0" } }, + from_str: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Binary: { kind: "trait", metadata: { since: "1.0.0" } }, + Debug: { kind: "trait", metadata: { since: "1.0.0" } }, + DebugAsHex: { kind: "enum", metadata: { since: "unstable" } }, + DebugList: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + entry_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + DebugMap: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + key: { kind: "function", metadata: { since: "1.42.0" } }, + key_with: { kind: "function", metadata: { since: "unstable" } }, + value: { kind: "function", metadata: { since: "1.42.0" } }, + value_with: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + DebugSet: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + entry_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + DebugStruct: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + field: { kind: "function", metadata: { since: "1.2.0" } }, + field_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.53.0" } }, + }, + }, + DebugTuple: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + field: { kind: "function", metadata: { since: "1.2.0" } }, + field_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + Display: { kind: "trait", metadata: { since: "1.0.0" } }, + Error: { kind: "struct", metadata: { since: "1.0.0" } }, + Formatter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + align: { kind: "function", metadata: { since: "1.28.0" } }, + alternate: { kind: "function", metadata: { since: "1.5.0" } }, + debug_list: { kind: "function", metadata: { since: "1.2.0" } }, + debug_map: { kind: "function", metadata: { since: "1.2.0" } }, + debug_set: { kind: "function", metadata: { since: "1.2.0" } }, + debug_struct: { kind: "function", metadata: { since: "1.2.0" } }, + debug_tuple: { kind: "function", metadata: { since: "1.2.0" } }, + fill: { kind: "function", metadata: { since: "1.5.0" } }, + flags: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + options: { kind: "function", metadata: { since: "unstable" } }, + pad: { kind: "function", metadata: { since: "1.0.0" } }, + pad_integral: { kind: "function", metadata: { since: "1.0.0" } }, + precision: { kind: "function", metadata: { since: "1.5.0" } }, + sign: { kind: "function", metadata: { since: "unstable" } }, + sign_aware_zero_pad: { kind: "function", metadata: { since: "1.5.0" } }, + sign_minus: { kind: "function", metadata: { since: "1.5.0" } }, + sign_plus: { kind: "function", metadata: { since: "1.5.0" } }, + width: { kind: "function", metadata: { since: "1.5.0" } }, + with_options: { kind: "function", metadata: { since: "unstable" } }, + write_fmt: { kind: "function", metadata: { since: "1.0.0" } }, + write_str: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + FormattingOptions: { + kind: "struct", metadata: { since: "unstable" }, + members: { + align: { kind: "function", metadata: { since: "unstable" } }, + alternate: { kind: "function", metadata: { since: "unstable" } }, + create_formatter: { kind: "function", metadata: { since: "unstable" } }, + debug_as_hex: { kind: "function", metadata: { since: "unstable" } }, + fill: { kind: "function", metadata: { since: "unstable" } }, + get_align: { kind: "function", metadata: { since: "unstable" } }, + get_alternate: { kind: "function", metadata: { since: "unstable" } }, + get_debug_as_hex: { kind: "function", metadata: { since: "unstable" } }, + get_fill: { kind: "function", metadata: { since: "unstable" } }, + get_precision: { kind: "function", metadata: { since: "unstable" } }, + get_sign: { kind: "function", metadata: { since: "unstable" } }, + get_sign_aware_zero_pad: { kind: "function", metadata: { since: "unstable" } }, + get_width: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + precision: { kind: "function", metadata: { since: "unstable" } }, + sign: { kind: "function", metadata: { since: "unstable" } }, + sign_aware_zero_pad: { kind: "function", metadata: { since: "unstable" } }, + width: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + from_fn: { kind: "function", metadata: { since: "1.93.0" } }, + FromFn: { kind: "struct", metadata: { since: "1.93.0" } }, + LowerExp: { kind: "trait", metadata: { since: "1.0.0" } }, + LowerHex: { kind: "trait", metadata: { since: "1.0.0" } }, + NumBuffer: { + kind: "struct", metadata: { since: "unstable" }, + members: { + capacity: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + NumBufferTrait: { kind: "trait", metadata: { since: "unstable" } }, + Octal: { kind: "trait", metadata: { since: "1.0.0" } }, + Pointer: { kind: "trait", metadata: { since: "1.0.0" } }, + Result: { kind: "type-alias", metadata: { since: "1.0.0" } }, + Sign: { kind: "enum", metadata: { since: "unstable" } }, + UpperExp: { kind: "trait", metadata: { since: "1.0.0" } }, + UpperHex: { kind: "trait", metadata: { since: "1.0.0" } }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + Write: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/future.ts b/packages/rust/src/builtins/core/future.ts new file mode 100644 index 000000000..c78564f9e --- /dev/null +++ b/packages/rust/src/builtins/core/future.ts @@ -0,0 +1,23 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_future = { + async_drop_in_place: { kind: "function", metadata: { since: "unstable" } }, + AsyncDrop: { kind: "trait", metadata: { since: "unstable" } }, + Future: { kind: "trait", metadata: { since: "1.36.0" } }, + IntoFuture: { kind: "trait", metadata: { since: "1.64.0" } }, + join: { kind: "symbol", metadata: { since: "unstable" } }, + pending: { kind: "function", metadata: { since: "1.48.0" } }, + Pending: { kind: "struct", metadata: { since: "1.48.0" } }, + poll_fn: { kind: "function", metadata: { since: "1.64.0" } }, + PollFn: { kind: "struct", metadata: { since: "1.64.0" } }, + ready: { kind: "function", metadata: { since: "1.48.0" } }, + Ready: { + kind: "struct", metadata: { since: "1.48.0" }, + members: { + into_inner: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/hash.ts b/packages/rust/src/builtins/core/hash.ts new file mode 100644 index 000000000..6ea554a2f --- /dev/null +++ b/packages/rust/src/builtins/core/hash.ts @@ -0,0 +1,23 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_hash = { + BuildHasher: { kind: "trait", metadata: { since: "1.7.0" } }, + BuildHasherDefault: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.85.0" } }, + }, + }, + Hash: { kind: "trait", metadata: { since: "1.0.0" } }, + Hasher: { kind: "trait", metadata: { since: "1.0.0" } }, + SipHasher: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_with_keys: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/index.ts b/packages/rust/src/builtins/core/index.ts new file mode 100644 index 000000000..f32608f4b --- /dev/null +++ b/packages/rust/src/builtins/core/index.ts @@ -0,0 +1,122 @@ +import { type SymbolCreator } from "@alloy-js/core"; +import { + type CrateDescriptor, + type CrateRef, + createCrate, + type ExternalCrate, +} from "../../create-crate.js"; +import { mod_alloc } from "./alloc.js"; +import { mod_any } from "./any.js"; +import { mod_array } from "./array.js"; +import { mod_ascii } from "./ascii.js"; +import { mod_borrow } from "./borrow.js"; +import { mod_cell } from "./cell.js"; +import { mod_char_ } from "./char.js"; +import { mod_clone } from "./clone.js"; +import { mod_cmp } from "./cmp.js"; +import { mod_contracts } from "./contracts.js"; +import { mod_convert } from "./convert.js"; +import { mod_default_ } from "./default.js"; +import { mod_error } from "./error.js"; +import { mod_f32, mod_f32_consts, mod_f32_math } from "./f32.js"; +import { mod_f64, mod_f64_consts, mod_f64_math } from "./f64.js"; +import { mod_ffi, mod_ffi_c_str, mod_ffi_va_list } from "./ffi.js"; +import { mod_fmt } from "./fmt.js"; +import { mod_future } from "./future.js"; +import { mod_hash } from "./hash.js"; +import { mod_io } from "./io.js"; +import { mod_iter } from "./iter.js"; +import { mod_marker } from "./marker.js"; +import { mod_mem, mod_mem_type_info } from "./mem.js"; +import { mod_net } from "./net.js"; +import { mod_num } from "./num.js"; +import { mod_ops } from "./ops.js"; +import { mod_option } from "./option.js"; +import { mod_panic } from "./panic.js"; +import { mod_panicking, mod_panicking_panic_const } from "./panicking.js"; +import { mod_pat } from "./pat.js"; +import { mod_pin } from "./pin.js"; +import { mod_profiling } from "./profiling.js"; +import { mod_ptr } from "./ptr.js"; +import { mod_random } from "./random.js"; +import { mod_range, mod_range_legacy } from "./range.js"; +import { mod_result } from "./result.js"; +import { mod_root } from "./root.js"; +import { mod_slice } from "./slice.js"; +import { mod_str, mod_str_pattern } from "./str.js"; +import { mod_sync, mod_sync_atomic } from "./sync.js"; +import { mod_task } from "./task.js"; +import { mod_time } from "./time.js"; +import { mod_ub_checks } from "./ub_checks.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +const coreDescriptor = { + name: "core", + builtin: true, + modules: { + "alloc": mod_alloc, + "any": mod_any, + "array": mod_array, + "ascii": mod_ascii, + "borrow": mod_borrow, + "cell": mod_cell, + "char": mod_char_, + "clone": mod_clone, + "cmp": mod_cmp, + "contracts": mod_contracts, + "convert": mod_convert, + "default": mod_default_, + "error": mod_error, + "f32": mod_f32, + "f32::consts": mod_f32_consts, + "f32::math": mod_f32_math, + "f64": mod_f64, + "f64::consts": mod_f64_consts, + "f64::math": mod_f64_math, + "ffi": mod_ffi, + "ffi::c_str": mod_ffi_c_str, + "ffi::va_list": mod_ffi_va_list, + "fmt": mod_fmt, + "future": mod_future, + "hash": mod_hash, + "io": mod_io, + "iter": mod_iter, + "marker": mod_marker, + "mem": mod_mem, + "mem::type_info": mod_mem_type_info, + "net": mod_net, + "num": mod_num, + "ops": mod_ops, + "option": mod_option, + "panic": mod_panic, + "panicking": mod_panicking, + "panicking::panic_const": mod_panicking_panic_const, + "pat": mod_pat, + "pin": mod_pin, + "profiling": mod_profiling, + "ptr": mod_ptr, + "random": mod_random, + "range": mod_range, + "range::legacy": mod_range_legacy, + "result": mod_result, + "": mod_root, + "slice": mod_slice, + "str": mod_str, + "str::pattern": mod_str_pattern, + "sync": mod_sync, + "sync::atomic": mod_sync_atomic, + "task": mod_task, + "time": mod_time, + "ub_checks": mod_ub_checks, + }, +} as const satisfies CrateDescriptor; + +/** + * The `core` crate descriptor. + */ +export type CoreCrate = CrateRef & + SymbolCreator & + ExternalCrate; +export const core: CoreCrate = createCrate(coreDescriptor); diff --git a/packages/rust/src/builtins/core/io.ts b/packages/rust/src/builtins/core/io.ts new file mode 100644 index 000000000..de9f20421 --- /dev/null +++ b/packages/rust/src/builtins/core/io.ts @@ -0,0 +1,38 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_io = { + BorrowedBuf: { + kind: "struct", + members: { + capacity: { kind: "function" }, + clear: { kind: "function" }, + filled: { kind: "function" }, + filled_mut: { kind: "function" }, + init_len: { kind: "function" }, + into_filled: { kind: "function" }, + into_filled_mut: { kind: "function" }, + len: { kind: "function" }, + set_init: { kind: "function" }, + unfilled: { kind: "function" }, + }, + }, + BorrowedCursor: { + kind: "struct", + members: { + advance: { kind: "function" }, + advance_unchecked: { kind: "function" }, + append: { kind: "function" }, + as_mut: { kind: "function" }, + capacity: { kind: "function" }, + ensure_init: { kind: "function" }, + init_mut: { kind: "function" }, + reborrow: { kind: "function" }, + set_init: { kind: "function" }, + with_unfilled_buf: { kind: "function" }, + written: { kind: "function" }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/iter.ts b/packages/rust/src/builtins/core/iter.ts new file mode 100644 index 000000000..ba269e57d --- /dev/null +++ b/packages/rust/src/builtins/core/iter.ts @@ -0,0 +1,92 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_iter = { + ArrayChunks: { + kind: "struct", metadata: { since: "unstable" }, + members: { + into_remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ByRefSized: { kind: "struct", metadata: { since: "unstable" } }, + chain: { kind: "function", metadata: { since: "1.91.0" } }, + Chain: { kind: "struct", metadata: { since: "1.0.0" } }, + Cloned: { kind: "struct", metadata: { since: "1.1.0" } }, + Copied: { kind: "struct", metadata: { since: "1.36.0" } }, + Cycle: { kind: "struct", metadata: { since: "1.0.0" } }, + DoubleEndedIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + empty: { kind: "function", metadata: { since: "1.2.0" } }, + Empty: { kind: "struct", metadata: { since: "1.2.0" } }, + Enumerate: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + next_index: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExactSizeIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Extend: { kind: "trait", metadata: { since: "1.0.0" } }, + Filter: { kind: "struct", metadata: { since: "1.0.0" } }, + FilterMap: { kind: "struct", metadata: { since: "1.0.0" } }, + FlatMap: { kind: "struct", metadata: { since: "1.0.0" } }, + Flatten: { kind: "struct", metadata: { since: "1.29.0" } }, + from_coroutine: { kind: "function", metadata: { since: "unstable" } }, + from_fn: { kind: "function", metadata: { since: "1.34.0" } }, + FromCoroutine: { kind: "struct", metadata: { since: "unstable" } }, + FromFn: { kind: "struct", metadata: { since: "1.34.0" } }, + FromIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Fuse: { kind: "struct", metadata: { since: "1.0.0" } }, + FusedIterator: { kind: "trait", metadata: { since: "1.26.0" } }, + Inspect: { kind: "struct", metadata: { since: "1.0.0" } }, + Intersperse: { kind: "struct", metadata: { since: "unstable" } }, + IntersperseWith: { kind: "struct", metadata: { since: "unstable" } }, + IntoIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + iter: { kind: "symbol", metadata: { since: "unstable" } }, + Iterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Map: { kind: "struct", metadata: { since: "1.0.0" } }, + MapWhile: { kind: "struct", metadata: { since: "1.57.0" } }, + MapWindows: { kind: "struct", metadata: { since: "unstable" } }, + once: { kind: "function", metadata: { since: "1.2.0" } }, + Once: { kind: "struct", metadata: { since: "1.2.0" } }, + once_with: { kind: "function", metadata: { since: "1.43.0" } }, + OnceWith: { kind: "struct", metadata: { since: "1.43.0" } }, + Peekable: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + next_if: { kind: "function", metadata: { since: "1.51.0" } }, + next_if_eq: { kind: "function", metadata: { since: "1.51.0" } }, + next_if_map: { kind: "function", metadata: { since: "1.94.0" } }, + next_if_map_mut: { kind: "function", metadata: { since: "1.94.0" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.53.0" } }, + }, + }, + Product: { kind: "trait", metadata: { since: "1.12.0" } }, + repeat: { kind: "function", metadata: { since: "1.0.0" } }, + Repeat: { kind: "struct", metadata: { since: "1.0.0" } }, + repeat_n: { kind: "function", metadata: { since: "1.82.0" } }, + repeat_with: { kind: "function", metadata: { since: "1.28.0" } }, + RepeatN: { kind: "struct", metadata: { since: "1.82.0" } }, + RepeatWith: { kind: "struct", metadata: { since: "1.28.0" } }, + Rev: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + into_inner: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Scan: { kind: "struct", metadata: { since: "1.0.0" } }, + Skip: { kind: "struct", metadata: { since: "1.0.0" } }, + SkipWhile: { kind: "struct", metadata: { since: "1.0.0" } }, + Step: { kind: "trait", metadata: { since: "unstable" } }, + StepBy: { kind: "struct", metadata: { since: "1.28.0" } }, + successors: { kind: "function", metadata: { since: "1.34.0" } }, + Successors: { kind: "struct", metadata: { since: "1.34.0" } }, + Sum: { kind: "trait", metadata: { since: "1.12.0" } }, + Take: { kind: "struct", metadata: { since: "1.0.0" } }, + TakeWhile: { kind: "struct", metadata: { since: "1.0.0" } }, + TrustedLen: { kind: "trait", metadata: { since: "unstable" } }, + TrustedStep: { kind: "trait", metadata: { since: "unstable" } }, + zip: { kind: "function", metadata: { since: "1.59.0" } }, + Zip: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/marker.ts b/packages/rust/src/builtins/core/marker.ts new file mode 100644 index 000000000..ba175a0a1 --- /dev/null +++ b/packages/rust/src/builtins/core/marker.ts @@ -0,0 +1,63 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_marker = { + ConstParamTy_: { kind: "trait", metadata: { since: "unstable" } }, + Copy: { kind: "trait", metadata: { since: "1.0.0" } }, + Destruct: { kind: "trait", metadata: { since: "unstable" } }, + DiscriminantKind: { kind: "trait", metadata: { since: "unstable" } }, + FnPtr: { kind: "trait", metadata: { since: "unstable" } }, + Freeze: { kind: "trait", metadata: { since: "unstable" } }, + MetaSized: { kind: "trait", metadata: { since: "unstable" } }, + PhantomContravariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomContravariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomCovariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomCovariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomData: { kind: "struct", metadata: { since: "1.0.0" } }, + PhantomInvariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomInvariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomPinned: { kind: "struct", metadata: { since: "1.33.0" } }, + PointeeSized: { kind: "trait", metadata: { since: "unstable" } }, + Send: { kind: "trait", metadata: { since: "1.0.0" } }, + Sized: { kind: "trait", metadata: { since: "1.0.0" } }, + StructuralPartialEq: { kind: "trait", metadata: { since: "unstable" } }, + Sync: { kind: "trait", metadata: { since: "1.0.0" } }, + Tuple: { kind: "trait", metadata: { since: "unstable" } }, + Unpin: { kind: "trait", metadata: { since: "1.33.0" } }, + UnsafeUnpin: { kind: "trait", metadata: { since: "unstable" } }, + Unsize: { kind: "trait", metadata: { since: "unstable" } }, + variance: { kind: "function" }, + Variance: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/mem.ts b/packages/rust/src/builtins/core/mem.ts new file mode 100644 index 000000000..37c10715b --- /dev/null +++ b/packages/rust/src/builtins/core/mem.ts @@ -0,0 +1,120 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_mem = { + align_of: { kind: "function", metadata: { since: "1.0.0" } }, + align_of_val: { kind: "function", metadata: { since: "1.0.0" } }, + align_of_val_raw: { kind: "function", metadata: { since: "unstable" } }, + Alignment: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_nonzero: { kind: "function", metadata: { since: "unstable" } }, + as_nonzero_usize: { kind: "function", metadata: { since: "unstable" } }, + as_usize: { kind: "function", metadata: { since: "unstable" } }, + log2: { kind: "function", metadata: { since: "unstable" } }, + mask: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of_val: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of_val_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Assume: { + kind: "struct", metadata: { since: "unstable" }, + members: { + and: { kind: "function", metadata: { since: "unstable" } }, + but_not: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + conjure_zst: { kind: "function", metadata: { since: "unstable" } }, + copy: { kind: "function", metadata: { since: "unstable" } }, + discriminant: { kind: "function", metadata: { since: "1.21.0" } }, + Discriminant: { kind: "struct", metadata: { since: "1.21.0" } }, + drop: { kind: "function", metadata: { since: "1.0.0" } }, + DropGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + dismiss: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + forget: { kind: "function", metadata: { since: "1.46.0" } }, + forget_unsized: { kind: "function", metadata: { since: "unstable" } }, + ManuallyDrop: { + kind: "struct", metadata: { since: "1.20.0" }, + members: { + drop: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + take: { kind: "function", associated: true, metadata: { since: "1.42.0" } }, + }, + }, + MaybeDangling: { + kind: "struct", + members: { + as_mut: { kind: "function" }, + as_ref: { kind: "function" }, + into_inner: { kind: "function" }, + new: { kind: "function", associated: true }, + }, + }, + min_align_of: { kind: "function", metadata: { since: "1.0.0" } }, + min_align_of_val: { kind: "function", metadata: { since: "1.0.0" } }, + needs_drop: { kind: "function", metadata: { since: "1.21.0" } }, + offset_of: { kind: "symbol", metadata: { since: "1.77.0" } }, + replace: { kind: "function", metadata: { since: "1.0.0" } }, + size_of: { kind: "function", metadata: { since: "1.0.0" } }, + size_of_val: { kind: "function", metadata: { since: "1.0.0" } }, + size_of_val_raw: { kind: "function", metadata: { since: "unstable" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + take: { kind: "function", metadata: { since: "1.40.0" } }, + transmute: { kind: "function", metadata: { since: "1.0.0" } }, + transmute_copy: { kind: "function", metadata: { since: "1.0.0" } }, + TransmuteFrom: { kind: "trait", metadata: { since: "unstable" } }, + uninitialized: { kind: "function", metadata: { since: "1.0.0" } }, + variant_count: { kind: "function", metadata: { since: "unstable" } }, + zeroed: { kind: "function", metadata: { since: "1.0.0" } }, +} as const satisfies Record; + +export const mod_mem_type_info = { + Abi: { kind: "enum", metadata: { since: "unstable" } }, + Array: { kind: "struct", metadata: { since: "unstable" } }, + Bool: { kind: "struct", metadata: { since: "unstable" } }, + Char: { kind: "struct", metadata: { since: "unstable" } }, + Const: { kind: "struct", metadata: { since: "unstable" } }, + DynTrait: { kind: "struct", metadata: { since: "unstable" } }, + DynTraitPredicate: { kind: "struct", metadata: { since: "unstable" } }, + Enum: { kind: "struct", metadata: { since: "unstable" } }, + Field: { kind: "struct", metadata: { since: "unstable" } }, + Float: { kind: "struct", metadata: { since: "unstable" } }, + FnPtr: { kind: "struct", metadata: { since: "unstable" } }, + Generic: { kind: "enum", metadata: { since: "unstable" } }, + GenericType: { kind: "struct", metadata: { since: "unstable" } }, + Int: { kind: "struct", metadata: { since: "unstable" } }, + Lifetime: { kind: "struct", metadata: { since: "unstable" } }, + Pointer: { kind: "struct", metadata: { since: "unstable" } }, + Reference: { kind: "struct", metadata: { since: "unstable" } }, + Slice: { kind: "struct", metadata: { since: "unstable" } }, + Str: { kind: "struct", metadata: { since: "unstable" } }, + Struct: { kind: "struct", metadata: { since: "unstable" } }, + Trait: { kind: "struct", metadata: { since: "unstable" } }, + TraitImpl: { + kind: "struct", metadata: { since: "unstable" }, + members: { + get_vtable: { kind: "function" }, + }, + }, + Tuple: { kind: "struct", metadata: { since: "unstable" } }, + Type: { + kind: "struct", metadata: { since: "unstable" }, + members: { + of: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + TypeKind: { kind: "enum", metadata: { since: "unstable" } }, + Union: { kind: "struct", metadata: { since: "unstable" } }, + Variant: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/net.ts b/packages/rust/src/builtins/core/net.ts new file mode 100644 index 000000000..48b4030fd --- /dev/null +++ b/packages/rust/src/builtins/core/net.ts @@ -0,0 +1,118 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_net = { + AddrParseError: { kind: "struct", metadata: { since: "1.0.0" } }, + IpAddr: { + kind: "enum", metadata: { since: "1.7.0" }, + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_documentation: { kind: "function", metadata: { since: "unstable" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_ipv4: { kind: "function", metadata: { since: "1.50.0" } }, + is_ipv6: { kind: "function", metadata: { since: "1.50.0" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_unspecified: { kind: "function", metadata: { since: "1.50.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_canonical: { kind: "function", metadata: { since: "1.75.0" } }, + }, + }, + Ipv4Addr: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + from_bits: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + from_octets: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_broadcast: { kind: "function", metadata: { since: "1.50.0" } }, + is_documentation: { kind: "function", metadata: { since: "1.50.0" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_link_local: { kind: "function", metadata: { since: "1.50.0" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_private: { kind: "function", metadata: { since: "1.50.0" } }, + is_reserved: { kind: "function", metadata: { since: "unstable" } }, + is_shared: { kind: "function", metadata: { since: "unstable" } }, + is_unspecified: { kind: "function", metadata: { since: "1.32.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.32.0" } }, + octets: { kind: "function", metadata: { since: "1.50.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_bits: { kind: "function", metadata: { since: "1.80.0" } }, + to_ipv6_compatible: { kind: "function", metadata: { since: "1.50.0" } }, + to_ipv6_mapped: { kind: "function", metadata: { since: "1.50.0" } }, + }, + }, + Ipv6Addr: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + from_bits: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + from_octets: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_segments: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_documentation: { kind: "function", metadata: { since: "unstable" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_ipv4_mapped: { kind: "function", metadata: { since: "unstable" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_unicast: { kind: "function", metadata: { since: "unstable" } }, + is_unicast_global: { kind: "function", metadata: { since: "unstable" } }, + is_unicast_link_local: { kind: "function", metadata: { since: "1.84.0" } }, + is_unique_local: { kind: "function", metadata: { since: "1.84.0" } }, + is_unspecified: { kind: "function", metadata: { since: "1.50.0" } }, + multicast_scope: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.32.0" } }, + octets: { kind: "function", metadata: { since: "1.32.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + segments: { kind: "function", metadata: { since: "1.50.0" } }, + to_bits: { kind: "function", metadata: { since: "1.80.0" } }, + to_canonical: { kind: "function", metadata: { since: "1.75.0" } }, + to_ipv4: { kind: "function", metadata: { since: "1.50.0" } }, + to_ipv4_mapped: { kind: "function", metadata: { since: "1.63.0" } }, + }, + }, + Ipv6MulticastScope: { kind: "enum", metadata: { since: "unstable" } }, + SocketAddr: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + ip: { kind: "function", metadata: { since: "1.7.0" } }, + is_ipv4: { kind: "function", metadata: { since: "1.16.0" } }, + is_ipv6: { kind: "function", metadata: { since: "1.16.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + SocketAddrV4: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + ip: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + SocketAddrV6: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + flowinfo: { kind: "function", metadata: { since: "1.0.0" } }, + ip: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + scope_id: { kind: "function", metadata: { since: "1.0.0" } }, + set_flowinfo: { kind: "function", metadata: { since: "1.9.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + set_scope_id: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/num.ts b/packages/rust/src/builtins/core/num.ts new file mode 100644 index 000000000..ca5ab7bd3 --- /dev/null +++ b/packages/rust/src/builtins/core/num.ts @@ -0,0 +1,135 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_num = { + FpCategory: { kind: "enum", metadata: { since: "1.0.0" } }, + IntErrorKind: { kind: "enum", metadata: { since: "1.55.0" } }, + NonZero: { + kind: "struct", metadata: { since: "1.79.0" }, + members: { + abs: { kind: "function", metadata: { since: "1.64.0" } }, + bit_width: { kind: "function", metadata: { since: "unstable" } }, + cast_signed: { kind: "function", metadata: { since: "1.87.0" } }, + cast_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + checked_abs: { kind: "function", metadata: { since: "1.64.0" } }, + checked_add: { kind: "function", metadata: { since: "1.64.0" } }, + checked_mul: { kind: "function", metadata: { since: "1.64.0" } }, + checked_neg: { kind: "function", metadata: { since: "1.71.0" } }, + checked_next_power_of_two: { kind: "function", metadata: { since: "1.64.0" } }, + checked_pow: { kind: "function", metadata: { since: "1.64.0" } }, + count_ones: { kind: "function", metadata: { since: "1.86.0" } }, + div_ceil: { kind: "function", metadata: { since: "1.92.0" } }, + from_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ascii_radix: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_str_radix: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get: { kind: "function", metadata: { since: "1.28.0" } }, + highest_one: { kind: "function", metadata: { since: "unstable" } }, + ilog10: { kind: "function", metadata: { since: "1.67.0" } }, + ilog2: { kind: "function", metadata: { since: "1.67.0" } }, + is_negative: { kind: "function", metadata: { since: "1.71.0" } }, + is_positive: { kind: "function", metadata: { since: "1.71.0" } }, + is_power_of_two: { kind: "function", metadata: { since: "1.59.0" } }, + isolate_highest_one: { kind: "function", metadata: { since: "unstable" } }, + isolate_lowest_one: { kind: "function", metadata: { since: "unstable" } }, + isqrt: { kind: "function", metadata: { since: "1.84.0" } }, + leading_zeros: { kind: "function", metadata: { since: "1.53.0" } }, + lowest_one: { kind: "function", metadata: { since: "unstable" } }, + midpoint: { kind: "function", metadata: { since: "1.85.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + overflowing_abs: { kind: "function", metadata: { since: "1.64.0" } }, + overflowing_neg: { kind: "function", metadata: { since: "1.71.0" } }, + reverse_bits: { kind: "function", metadata: { since: "unstable" } }, + rotate_left: { kind: "function", metadata: { since: "unstable" } }, + rotate_right: { kind: "function", metadata: { since: "unstable" } }, + saturating_abs: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_add: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_mul: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_neg: { kind: "function", metadata: { since: "1.71.0" } }, + saturating_pow: { kind: "function", metadata: { since: "1.64.0" } }, + swap_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_be: { kind: "function", metadata: { since: "unstable" } }, + to_le: { kind: "function", metadata: { since: "unstable" } }, + trailing_zeros: { kind: "function", metadata: { since: "1.53.0" } }, + unchecked_add: { kind: "function", metadata: { since: "unstable" } }, + unchecked_mul: { kind: "function", metadata: { since: "unstable" } }, + unsigned_abs: { kind: "function", metadata: { since: "1.64.0" } }, + wrapping_abs: { kind: "function", metadata: { since: "1.64.0" } }, + wrapping_neg: { kind: "function", metadata: { since: "1.71.0" } }, + }, + }, + NonZeroI128: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroI16: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroI32: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroI64: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroI8: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroIsize: { kind: "type-alias", metadata: { since: "1.34.0" } }, + NonZeroU128: { kind: "type-alias", metadata: { since: "1.28.0" } }, + NonZeroU16: { kind: "type-alias", metadata: { since: "1.28.0" } }, + NonZeroU32: { kind: "type-alias", metadata: { since: "1.28.0" } }, + NonZeroU64: { kind: "type-alias", metadata: { since: "1.28.0" } }, + NonZeroU8: { kind: "type-alias", metadata: { since: "1.28.0" } }, + NonZeroUsize: { kind: "type-alias", metadata: { since: "1.28.0" } }, + ParseFloatError: { kind: "struct", metadata: { since: "1.0.0" } }, + ParseIntError: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + kind: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, + Saturating: { + kind: "struct", metadata: { since: "1.74.0" }, + members: { + abs: { kind: "function", metadata: { since: "1.74.0" } }, + count_ones: { kind: "function", metadata: { since: "1.74.0" } }, + count_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + from_be: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + from_le: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + is_negative: { kind: "function", metadata: { since: "1.74.0" } }, + is_positive: { kind: "function", metadata: { since: "1.74.0" } }, + is_power_of_two: { kind: "function", metadata: { since: "1.74.0" } }, + leading_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + pow: { kind: "function", metadata: { since: "1.74.0" } }, + reverse_bits: { kind: "function", metadata: { since: "1.74.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.74.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.74.0" } }, + signum: { kind: "function", metadata: { since: "1.74.0" } }, + swap_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + to_be: { kind: "function", metadata: { since: "1.74.0" } }, + to_le: { kind: "function", metadata: { since: "1.74.0" } }, + trailing_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + }, + }, + TryFromIntError: { kind: "struct", metadata: { since: "1.34.0" } }, + Wrapping: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + abs: { kind: "function", metadata: { since: "unstable" } }, + count_ones: { kind: "function", metadata: { since: "unstable" } }, + count_zeros: { kind: "function", metadata: { since: "unstable" } }, + from_be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_negative: { kind: "function", metadata: { since: "unstable" } }, + is_positive: { kind: "function", metadata: { since: "unstable" } }, + is_power_of_two: { kind: "function", metadata: { since: "unstable" } }, + leading_zeros: { kind: "function", metadata: { since: "unstable" } }, + next_power_of_two: { kind: "function", metadata: { since: "unstable" } }, + pow: { kind: "function", metadata: { since: "unstable" } }, + reverse_bits: { kind: "function", metadata: { since: "1.37.0" } }, + rotate_left: { kind: "function", metadata: { since: "unstable" } }, + rotate_right: { kind: "function", metadata: { since: "unstable" } }, + signum: { kind: "function", metadata: { since: "unstable" } }, + swap_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_be: { kind: "function", metadata: { since: "unstable" } }, + to_le: { kind: "function", metadata: { since: "unstable" } }, + trailing_zeros: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ZeroablePrimitive: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/ops.ts b/packages/rust/src/builtins/core/ops.ts new file mode 100644 index 000000000..ab63e148a --- /dev/null +++ b/packages/rust/src/builtins/core/ops.ts @@ -0,0 +1,117 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ops = { + Add: { kind: "trait", metadata: { since: "1.0.0" } }, + AddAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + AsyncFn: { kind: "trait", metadata: { since: "1.85.0" } }, + AsyncFnMut: { kind: "trait", metadata: { since: "1.85.0" } }, + AsyncFnOnce: { kind: "trait", metadata: { since: "1.85.0" } }, + BitAnd: { kind: "trait", metadata: { since: "1.0.0" } }, + BitAndAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + BitOr: { kind: "trait", metadata: { since: "1.0.0" } }, + BitOrAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + BitXor: { kind: "trait", metadata: { since: "1.0.0" } }, + BitXorAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Bound: { + kind: "enum", metadata: { since: "1.17.0" }, + members: { + as_mut: { kind: "function", metadata: { since: "unstable" } }, + as_ref: { kind: "function", metadata: { since: "1.65.0" } }, + cloned: { kind: "function", metadata: { since: "1.55.0" } }, + copied: { kind: "function", metadata: { since: "unstable" } }, + map: { kind: "function", metadata: { since: "1.77.0" } }, + }, + }, + CoerceShared: { kind: "trait", metadata: { since: "unstable" } }, + CoerceUnsized: { kind: "trait", metadata: { since: "unstable" } }, + ControlFlow: { + kind: "enum", metadata: { since: "1.55.0" }, + members: { + break_ok: { kind: "function", metadata: { since: "unstable" } }, + break_value: { kind: "function", metadata: { since: "1.83.0" } }, + continue_ok: { kind: "function", metadata: { since: "unstable" } }, + continue_value: { kind: "function", metadata: { since: "1.83.0" } }, + into_value: { kind: "function", metadata: { since: "unstable" } }, + is_break: { kind: "function", metadata: { since: "1.59.0" } }, + is_continue: { kind: "function", metadata: { since: "1.59.0" } }, + map_break: { kind: "function", metadata: { since: "1.83.0" } }, + map_continue: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + Coroutine: { kind: "trait", metadata: { since: "unstable" } }, + CoroutineState: { kind: "enum", metadata: { since: "unstable" } }, + Deref: { kind: "trait", metadata: { since: "1.0.0" } }, + DerefMut: { kind: "trait", metadata: { since: "1.0.0" } }, + DerefPure: { kind: "trait", metadata: { since: "unstable" } }, + DispatchFromDyn: { kind: "trait", metadata: { since: "unstable" } }, + Div: { kind: "trait", metadata: { since: "1.0.0" } }, + DivAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Drop: { kind: "trait", metadata: { since: "1.0.0" } }, + Fn: { kind: "trait", metadata: { since: "1.0.0" } }, + FnMut: { kind: "trait", metadata: { since: "1.0.0" } }, + FnOnce: { kind: "trait", metadata: { since: "1.0.0" } }, + FromResidual: { kind: "trait", metadata: { since: "unstable" } }, + Index: { kind: "trait", metadata: { since: "1.0.0" } }, + IndexMut: { kind: "trait", metadata: { since: "1.0.0" } }, + IntoBounds: { kind: "trait", metadata: { since: "unstable" } }, + Mul: { kind: "trait", metadata: { since: "1.0.0" } }, + MulAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Neg: { kind: "trait", metadata: { since: "1.0.0" } }, + Not: { kind: "trait", metadata: { since: "1.0.0" } }, + OneSidedRange: { kind: "trait", metadata: { since: "unstable" } }, + OneSidedRangeBound: { kind: "enum", metadata: { since: "unstable" } }, + Range: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + }, + }, + RangeBounds: { kind: "trait", metadata: { since: "1.28.0" } }, + RangeFrom: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeFull: { kind: "struct", metadata: { since: "1.0.0" } }, + RangeInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + end: { kind: "function", metadata: { since: "1.27.0" } }, + into_inner: { kind: "function", metadata: { since: "1.27.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + start: { kind: "function", metadata: { since: "1.27.0" } }, + }, + }, + RangeTo: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeToInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + Reborrow: { kind: "trait", metadata: { since: "unstable" } }, + Receiver: { kind: "trait", metadata: { since: "unstable" } }, + Rem: { kind: "trait", metadata: { since: "1.0.0" } }, + RemAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Residual: { kind: "trait", metadata: { since: "unstable" } }, + Shl: { kind: "trait", metadata: { since: "1.0.0" } }, + ShlAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Shr: { kind: "trait", metadata: { since: "1.0.0" } }, + ShrAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Sub: { kind: "trait", metadata: { since: "1.0.0" } }, + SubAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Try: { kind: "trait", metadata: { since: "unstable" } }, + Yeet: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/option.ts b/packages/rust/src/builtins/core/option.ts new file mode 100644 index 000000000..d0b93e631 --- /dev/null +++ b/packages/rust/src/builtins/core/option.ts @@ -0,0 +1,68 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_option = { + IntoIter: { kind: "struct", metadata: { since: "1.0.0" } }, + Iter: { kind: "struct", metadata: { since: "1.0.0" } }, + IterMut: { kind: "struct", metadata: { since: "1.0.0" } }, + Option: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + and: { kind: "function", metadata: { since: "1.0.0" } }, + and_then: { kind: "function", metadata: { since: "1.0.0" } }, + as_deref: { kind: "function", metadata: { since: "1.40.0" } }, + as_deref_mut: { kind: "function", metadata: { since: "1.40.0" } }, + as_mut: { kind: "function", metadata: { since: "1.0.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.75.0" } }, + as_pin_mut: { kind: "function", metadata: { since: "1.33.0" } }, + as_pin_ref: { kind: "function", metadata: { since: "1.33.0" } }, + as_ref: { kind: "function", metadata: { since: "1.48.0" } }, + as_slice: { kind: "function", metadata: { since: "1.75.0" } }, + cloned: { kind: "function", metadata: { since: "1.0.0" } }, + copied: { kind: "function", metadata: { since: "1.35.0" } }, + expect: { kind: "function", metadata: { since: "1.0.0" } }, + filter: { kind: "function", metadata: { since: "1.27.0" } }, + flatten: { kind: "function", metadata: { since: "1.40.0" } }, + flatten_mut: { kind: "function", metadata: { since: "unstable" } }, + flatten_ref: { kind: "function", metadata: { since: "unstable" } }, + get_or_insert: { kind: "function", metadata: { since: "1.20.0" } }, + get_or_insert_default: { kind: "function", metadata: { since: "1.83.0" } }, + get_or_insert_with: { kind: "function", metadata: { since: "1.20.0" } }, + get_or_try_insert_with: { kind: "function", metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.53.0" } }, + inspect: { kind: "function", metadata: { since: "1.76.0" } }, + into_flat_iter: { kind: "function", metadata: { since: "unstable" } }, + is_none: { kind: "function", metadata: { since: "1.0.0" } }, + is_none_or: { kind: "function", metadata: { since: "1.82.0" } }, + is_some: { kind: "function", metadata: { since: "1.0.0" } }, + is_some_and: { kind: "function", metadata: { since: "1.70.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + map: { kind: "function", metadata: { since: "1.0.0" } }, + map_or: { kind: "function", metadata: { since: "1.0.0" } }, + map_or_default: { kind: "function", metadata: { since: "unstable" } }, + map_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + ok_or: { kind: "function", metadata: { since: "1.0.0" } }, + ok_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + or: { kind: "function", metadata: { since: "1.0.0" } }, + or_else: { kind: "function", metadata: { since: "1.0.0" } }, + reduce: { kind: "function", metadata: { since: "unstable" } }, + replace: { kind: "function", metadata: { since: "1.31.0" } }, + take: { kind: "function", metadata: { since: "1.0.0" } }, + take_if: { kind: "function", metadata: { since: "1.80.0" } }, + transpose: { kind: "function", metadata: { since: "1.33.0" } }, + unwrap: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_default: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + unzip: { kind: "function", metadata: { since: "1.66.0" } }, + xor: { kind: "function", metadata: { since: "1.37.0" } }, + zip: { kind: "function", metadata: { since: "1.46.0" } }, + zip_with: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + OptionFlatten: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/panic.ts b/packages/rust/src/builtins/core/panic.ts new file mode 100644 index 000000000..b7e279a7b --- /dev/null +++ b/packages/rust/src/builtins/core/panic.ts @@ -0,0 +1,36 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_panic = { + abort_unwind: { kind: "function", metadata: { since: "unstable" } }, + AssertUnwindSafe: { kind: "struct", metadata: { since: "1.9.0" } }, + Location: { + kind: "struct", metadata: { since: "1.10.0" }, + members: { + caller: { kind: "function", associated: true, metadata: { since: "1.46.0" } }, + column: { kind: "function", metadata: { since: "1.25.0" } }, + file: { kind: "function", metadata: { since: "1.10.0" } }, + file_as_c_str: { kind: "function", metadata: { since: "1.92.0" } }, + line: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + PanicInfo: { + kind: "struct", metadata: { since: "1.10.0" }, + members: { + can_unwind: { kind: "function", metadata: { since: "unstable" } }, + location: { kind: "function", metadata: { since: "1.10.0" } }, + message: { kind: "function", metadata: { since: "1.81.0" } }, + payload: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + PanicMessage: { + kind: "struct", metadata: { since: "1.81.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.81.0" } }, + }, + }, + RefUnwindSafe: { kind: "trait", metadata: { since: "1.9.0" } }, + UnwindSafe: { kind: "trait", metadata: { since: "1.9.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/panicking.ts b/packages/rust/src/builtins/core/panicking.ts new file mode 100644 index 000000000..c71d59db7 --- /dev/null +++ b/packages/rust/src/builtins/core/panicking.ts @@ -0,0 +1,41 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_panicking = { + const_panic_fmt: { kind: "function" }, + panic: { kind: "function" }, + panic_display: { kind: "function" }, + panic_fmt: { kind: "function" }, + panic_nounwind: { kind: "function" }, + panic_nounwind_fmt: { kind: "function", metadata: { since: "unstable" } }, + panic_nounwind_nobacktrace: { kind: "function" }, + panic_str_2015: { kind: "function" }, + unreachable_display: { kind: "function" }, +} as const satisfies Record; + +export const mod_panicking_panic_const = { + panic_const_add_overflow: { kind: "function" }, + panic_const_async_fn_resumed: { kind: "function" }, + panic_const_async_fn_resumed_drop: { kind: "function" }, + panic_const_async_fn_resumed_panic: { kind: "function" }, + panic_const_async_gen_fn_resumed: { kind: "function" }, + panic_const_async_gen_fn_resumed_drop: { kind: "function" }, + panic_const_async_gen_fn_resumed_panic: { kind: "function" }, + panic_const_coroutine_resumed: { kind: "function" }, + panic_const_coroutine_resumed_drop: { kind: "function" }, + panic_const_coroutine_resumed_panic: { kind: "function" }, + panic_const_div_by_zero: { kind: "function" }, + panic_const_div_overflow: { kind: "function" }, + panic_const_gen_fn_none: { kind: "function" }, + panic_const_gen_fn_none_drop: { kind: "function" }, + panic_const_gen_fn_none_panic: { kind: "function" }, + panic_const_mul_overflow: { kind: "function" }, + panic_const_neg_overflow: { kind: "function" }, + panic_const_rem_by_zero: { kind: "function" }, + panic_const_rem_overflow: { kind: "function" }, + panic_const_shl_overflow: { kind: "function" }, + panic_const_shr_overflow: { kind: "function" }, + panic_const_sub_overflow: { kind: "function" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/pat.ts b/packages/rust/src/builtins/core/pat.ts new file mode 100644 index 000000000..fddb1a82f --- /dev/null +++ b/packages/rust/src/builtins/core/pat.ts @@ -0,0 +1,8 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_pat = { + RangePattern: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/pin.ts b/packages/rust/src/builtins/core/pin.ts new file mode 100644 index 000000000..754e0aa20 --- /dev/null +++ b/packages/rust/src/builtins/core/pin.ts @@ -0,0 +1,42 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_pin = { + pin: { kind: "symbol", metadata: { since: "1.68.0" } }, + Pin: { + kind: "struct", metadata: { since: "1.33.0" }, + members: { + as_deref_mut: { kind: "function", metadata: { since: "1.84.0" } }, + as_mut: { kind: "function", metadata: { since: "1.33.0" } }, + as_ref: { kind: "function", metadata: { since: "1.33.0" } }, + get_mut: { kind: "function", metadata: { since: "1.33.0" } }, + get_ref: { kind: "function", metadata: { since: "1.84.0" } }, + get_unchecked_mut: { kind: "function", metadata: { since: "1.33.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_ref: { kind: "function", metadata: { since: "1.84.0" } }, + map_unchecked: { kind: "function", metadata: { since: "1.33.0" } }, + map_unchecked_mut: { kind: "function", metadata: { since: "1.33.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + set: { kind: "function", metadata: { since: "1.33.0" } }, + static_mut: { kind: "function", associated: true, metadata: { since: "1.61.0" } }, + static_ref: { kind: "function", associated: true, metadata: { since: "1.61.0" } }, + }, + }, + PinCoerceUnsized: { kind: "trait", metadata: { since: "unstable" } }, + UnsafePinned: { + kind: "struct", metadata: { since: "unstable" }, + members: { + get: { kind: "function", metadata: { since: "unstable" } }, + get_mut_pinned: { kind: "function", metadata: { since: "unstable" } }, + get_mut_unchecked: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + raw_get: { kind: "function", associated: true, metadata: { since: "unstable" } }, + raw_get_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/profiling.ts b/packages/rust/src/builtins/core/profiling.ts new file mode 100644 index 000000000..6527c099e --- /dev/null +++ b/packages/rust/src/builtins/core/profiling.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_profiling = { + compiler_copy: { kind: "function", metadata: { since: "unstable" } }, + compiler_move: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/ptr.ts b/packages/rust/src/builtins/core/ptr.ts new file mode 100644 index 000000000..ba081c842 --- /dev/null +++ b/packages/rust/src/builtins/core/ptr.ts @@ -0,0 +1,115 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ptr = { + addr_eq: { kind: "function", metadata: { since: "1.76.0" } }, + addr_of: { kind: "symbol", metadata: { since: "1.51.0" } }, + addr_of_mut: { kind: "symbol", metadata: { since: "1.51.0" } }, + Alignment: { kind: "type-alias", metadata: { since: "unstable" } }, + copy: { kind: "function", metadata: { since: "1.0.0" } }, + copy_nonoverlapping: { kind: "function", metadata: { since: "1.0.0" } }, + dangling: { kind: "function", metadata: { since: "1.84.0" } }, + dangling_mut: { kind: "function", metadata: { since: "1.84.0" } }, + drop_in_place: { kind: "function", metadata: { since: "1.8.0" } }, + DynMetadata: { + kind: "struct", + members: { + align_of: { kind: "function" }, + layout: { kind: "function" }, + size_of: { kind: "function" }, + }, + }, + eq: { kind: "function", metadata: { since: "1.17.0" } }, + fn_addr_eq: { kind: "function", metadata: { since: "1.85.0" } }, + from_mut: { kind: "function", metadata: { since: "1.76.0" } }, + from_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "unstable" } }, + from_ref: { kind: "function", metadata: { since: "1.76.0" } }, + hash: { kind: "function", metadata: { since: "1.35.0" } }, + metadata: { kind: "function" }, + NonNull: { + kind: "struct", metadata: { since: "1.25.0" }, + members: { + add: { kind: "function", metadata: { since: "1.80.0" } }, + addr: { kind: "function", metadata: { since: "1.84.0" } }, + align_offset: { kind: "function", metadata: { since: "1.80.0" } }, + as_mut: { kind: "function", metadata: { since: "1.25.0" } }, + as_mut_ptr: { kind: "function", metadata: { since: "unstable" } }, + as_non_null_ptr: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.25.0" } }, + as_ref: { kind: "function", metadata: { since: "1.25.0" } }, + as_uninit_mut: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_ref: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_slice: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_slice_mut: { kind: "function", metadata: { since: "unstable" } }, + byte_add: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset_from: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset_from_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + byte_sub: { kind: "function", metadata: { since: "1.80.0" } }, + cast: { kind: "function", metadata: { since: "1.27.0" } }, + cast_array: { kind: "function", metadata: { since: "unstable" } }, + cast_init: { kind: "function", metadata: { since: "unstable" } }, + cast_slice: { kind: "function", metadata: { since: "unstable" } }, + cast_uninit: { kind: "function", metadata: { since: "unstable" } }, + copy_from: { kind: "function", metadata: { since: "1.80.0" } }, + copy_from_nonoverlapping: { kind: "function", metadata: { since: "1.80.0" } }, + copy_to: { kind: "function", metadata: { since: "1.80.0" } }, + copy_to_nonoverlapping: { kind: "function", metadata: { since: "1.80.0" } }, + dangling: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + drop_in_place: { kind: "function", metadata: { since: "1.80.0" } }, + expose_provenance: { kind: "function", metadata: { since: "1.89.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ref: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + get_unchecked_mut: { kind: "function", metadata: { since: "unstable" } }, + is_aligned: { kind: "function", metadata: { since: "1.79.0" } }, + is_aligned_to: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "1.79.0" } }, + len: { kind: "function", metadata: { since: "1.63.0" } }, + map_addr: { kind: "function", metadata: { since: "1.84.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + offset: { kind: "function", metadata: { since: "1.80.0" } }, + offset_from: { kind: "function", metadata: { since: "1.80.0" } }, + offset_from_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + read: { kind: "function", metadata: { since: "1.80.0" } }, + read_unaligned: { kind: "function", metadata: { since: "1.80.0" } }, + read_volatile: { kind: "function", metadata: { since: "1.80.0" } }, + replace: { kind: "function", metadata: { since: "1.80.0" } }, + slice_from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + sub: { kind: "function", metadata: { since: "1.80.0" } }, + swap: { kind: "function", metadata: { since: "1.80.0" } }, + to_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + try_cast_aligned: { kind: "function", metadata: { since: "unstable" } }, + with_addr: { kind: "function", metadata: { since: "1.84.0" } }, + with_exposed_provenance: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + without_provenance: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + write: { kind: "function", metadata: { since: "1.80.0" } }, + write_bytes: { kind: "function", metadata: { since: "1.80.0" } }, + write_unaligned: { kind: "function", metadata: { since: "1.80.0" } }, + write_volatile: { kind: "function", metadata: { since: "1.80.0" } }, + }, + }, + null: { kind: "function", metadata: { since: "1.0.0" } }, + null_mut: { kind: "function", metadata: { since: "1.0.0" } }, + Pointee: { kind: "trait" }, + read: { kind: "function", metadata: { since: "1.0.0" } }, + read_unaligned: { kind: "function", metadata: { since: "1.17.0" } }, + read_volatile: { kind: "function", metadata: { since: "1.9.0" } }, + replace: { kind: "function", metadata: { since: "1.0.0" } }, + slice_from_raw_parts: { kind: "function", metadata: { since: "1.42.0" } }, + slice_from_raw_parts_mut: { kind: "function", metadata: { since: "1.42.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + swap_nonoverlapping: { kind: "function", metadata: { since: "1.27.0" } }, + with_exposed_provenance: { kind: "function", metadata: { since: "1.84.0" } }, + with_exposed_provenance_mut: { kind: "function", metadata: { since: "1.84.0" } }, + without_provenance: { kind: "function", metadata: { since: "1.84.0" } }, + without_provenance_mut: { kind: "function", metadata: { since: "1.84.0" } }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + write_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + write_unaligned: { kind: "function", metadata: { since: "1.17.0" } }, + write_volatile: { kind: "function", metadata: { since: "1.9.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/random.ts b/packages/rust/src/builtins/core/random.ts new file mode 100644 index 000000000..019577f23 --- /dev/null +++ b/packages/rust/src/builtins/core/random.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_random = { + Distribution: { kind: "trait", metadata: { since: "unstable" } }, + RandomSource: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/range.ts b/packages/rust/src/builtins/core/range.ts new file mode 100644 index 000000000..388fc3a82 --- /dev/null +++ b/packages/rust/src/builtins/core/range.ts @@ -0,0 +1,87 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_range = { + Range: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function" }, + }, + }, + RangeFrom: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function" }, + }, + }, + RangeFromIter: { + kind: "struct", + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeInclusive: { + kind: "struct", metadata: { since: "1.95.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.95.0" } }, + iter: { kind: "function", metadata: { since: "1.95.0" } }, + }, + }, + RangeInclusiveIter: { + kind: "struct", metadata: { since: "1.95.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeIter: { + kind: "struct", + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeToInclusive: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_range_legacy = { + Range: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + }, + }, + RangeFrom: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + end: { kind: "function", metadata: { since: "1.27.0" } }, + into_inner: { kind: "function", metadata: { since: "1.27.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + start: { kind: "function", metadata: { since: "1.27.0" } }, + }, + }, + RangeToInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/result.ts b/packages/rust/src/builtins/core/result.ts new file mode 100644 index 000000000..db4aa2033 --- /dev/null +++ b/packages/rust/src/builtins/core/result.ts @@ -0,0 +1,53 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_result = { + IntoIter: { kind: "struct", metadata: { since: "1.0.0" } }, + Iter: { kind: "struct", metadata: { since: "1.0.0" } }, + IterMut: { kind: "struct", metadata: { since: "1.0.0" } }, + Result: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + and: { kind: "function", metadata: { since: "1.0.0" } }, + and_then: { kind: "function", metadata: { since: "1.0.0" } }, + as_deref: { kind: "function", metadata: { since: "1.47.0" } }, + as_deref_mut: { kind: "function", metadata: { since: "1.47.0" } }, + as_mut: { kind: "function", metadata: { since: "1.0.0" } }, + as_ref: { kind: "function", metadata: { since: "1.48.0" } }, + cloned: { kind: "function", metadata: { since: "1.59.0" } }, + copied: { kind: "function", metadata: { since: "1.59.0" } }, + err: { kind: "function", metadata: { since: "1.0.0" } }, + expect: { kind: "function", metadata: { since: "1.4.0" } }, + expect_err: { kind: "function", metadata: { since: "1.17.0" } }, + flatten: { kind: "function", metadata: { since: "1.89.0" } }, + inspect: { kind: "function", metadata: { since: "1.76.0" } }, + inspect_err: { kind: "function", metadata: { since: "1.76.0" } }, + into_err: { kind: "function", metadata: { since: "unstable" } }, + into_ok: { kind: "function", metadata: { since: "unstable" } }, + is_err: { kind: "function", metadata: { since: "1.48.0" } }, + is_err_and: { kind: "function", metadata: { since: "1.70.0" } }, + is_ok: { kind: "function", metadata: { since: "1.48.0" } }, + is_ok_and: { kind: "function", metadata: { since: "1.70.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + map: { kind: "function", metadata: { since: "1.0.0" } }, + map_err: { kind: "function", metadata: { since: "1.0.0" } }, + map_or: { kind: "function", metadata: { since: "1.41.0" } }, + map_or_default: { kind: "function", metadata: { since: "unstable" } }, + map_or_else: { kind: "function", metadata: { since: "1.41.0" } }, + ok: { kind: "function", metadata: { since: "1.0.0" } }, + or: { kind: "function", metadata: { since: "1.0.0" } }, + or_else: { kind: "function", metadata: { since: "1.0.0" } }, + transpose: { kind: "function", metadata: { since: "1.33.0" } }, + unwrap: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_err: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_err_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + unwrap_or: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_default: { kind: "function", metadata: { since: "1.16.0" } }, + unwrap_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/root.ts b/packages/rust/src/builtins/core/root.ts new file mode 100644 index 000000000..8b44408c5 --- /dev/null +++ b/packages/rust/src/builtins/core/root.ts @@ -0,0 +1,44 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_root = { + assert: { kind: "symbol", metadata: { since: "1.0.0" } }, + assert_eq: { kind: "symbol", metadata: { since: "1.0.0" } }, + assert_matches: { kind: "symbol", metadata: { since: "1.95.0" } }, + assert_ne: { kind: "symbol", metadata: { since: "1.13.0" } }, + assert_unsafe_precondition: { kind: "symbol", metadata: { since: "unstable" } }, + cfg: { kind: "symbol", metadata: { since: "1.0.0" } }, + cfg_select: { kind: "symbol", metadata: { since: "1.95.0" } }, + column: { kind: "symbol", metadata: { since: "1.0.0" } }, + compile_error: { kind: "symbol", metadata: { since: "1.20.0" } }, + concat: { kind: "symbol", metadata: { since: "1.0.0" } }, + concat_bytes: { kind: "symbol", metadata: { since: "unstable" } }, + const_format_args: { kind: "symbol", metadata: { since: "unstable" } }, + debug_assert: { kind: "symbol", metadata: { since: "1.0.0" } }, + debug_assert_eq: { kind: "symbol", metadata: { since: "1.0.0" } }, + debug_assert_matches: { kind: "symbol", metadata: { since: "1.95.0" } }, + debug_assert_ne: { kind: "symbol", metadata: { since: "1.13.0" } }, + env: { kind: "symbol", metadata: { since: "1.0.0" } }, + file: { kind: "symbol", metadata: { since: "1.0.0" } }, + format_args: { kind: "symbol", metadata: { since: "1.0.0" } }, + include: { kind: "symbol", metadata: { since: "1.0.0" } }, + include_bytes: { kind: "symbol", metadata: { since: "1.0.0" } }, + include_str: { kind: "symbol", metadata: { since: "1.0.0" } }, + line: { kind: "symbol", metadata: { since: "1.0.0" } }, + log_syntax: { kind: "symbol", metadata: { since: "unstable" } }, + matches: { kind: "symbol", metadata: { since: "1.42.0" } }, + module_path: { kind: "symbol", metadata: { since: "1.0.0" } }, + option_env: { kind: "symbol", metadata: { since: "1.0.0" } }, + panic: { kind: "symbol", metadata: { since: "unstable" } }, + pattern_type: { kind: "symbol", metadata: { since: "unstable" } }, + stringify: { kind: "symbol", metadata: { since: "1.0.0" } }, + todo: { kind: "symbol", metadata: { since: "1.40.0" } }, + trace_macros: { kind: "symbol", metadata: { since: "unstable" } }, + try: { kind: "symbol", metadata: { since: "1.0.0" } }, + unimplemented: { kind: "symbol", metadata: { since: "1.0.0" } }, + unreachable: { kind: "symbol", metadata: { since: "unstable" } }, + write: { kind: "symbol", metadata: { since: "1.0.0" } }, + writeln: { kind: "symbol", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/slice.ts b/packages/rust/src/builtins/core/slice.ts new file mode 100644 index 000000000..4b33e4dac --- /dev/null +++ b/packages/rust/src/builtins/core/slice.ts @@ -0,0 +1,81 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_slice = { + ArrayWindows: { kind: "struct", metadata: { since: "1.94.0" } }, + ChunkBy: { kind: "struct", metadata: { since: "1.77.0" } }, + ChunkByMut: { kind: "struct", metadata: { since: "1.77.0" } }, + Chunks: { kind: "struct", metadata: { since: "1.0.0" } }, + ChunksExact: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + ChunksExactMut: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + into_remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + ChunksMut: { kind: "struct", metadata: { since: "1.0.0" } }, + EscapeAscii: { kind: "struct", metadata: { since: "1.60.0" } }, + from_mut: { kind: "function", metadata: { since: "1.28.0" } }, + from_mut_ptr_range: { kind: "function", metadata: { since: "unstable" } }, + from_ptr_range: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts: { kind: "function", metadata: { since: "1.0.0" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "1.0.0" } }, + from_ref: { kind: "function", metadata: { since: "1.28.0" } }, + GetDisjointMutError: { kind: "enum", metadata: { since: "1.86.0" } }, + GetDisjointMutIndex: { kind: "trait", metadata: { since: "unstable" } }, + Iter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slice: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + IterMut: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.53.0" } }, + into_slice: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + range: { kind: "function", metadata: { since: "unstable" } }, + RChunks: { kind: "struct", metadata: { since: "1.31.0" } }, + RChunksExact: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + RChunksExactMut: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + into_remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + RChunksMut: { kind: "struct", metadata: { since: "1.31.0" } }, + RSplit: { kind: "struct", metadata: { since: "1.27.0" } }, + RSplitMut: { kind: "struct", metadata: { since: "1.27.0" } }, + RSplitN: { kind: "struct", metadata: { since: "1.0.0" } }, + RSplitNMut: { kind: "struct", metadata: { since: "1.0.0" } }, + SliceIndex: { kind: "trait", metadata: { since: "1.28.0" } }, + SlicePattern: { kind: "trait", metadata: { since: "unstable" } }, + Split: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slice: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitInclusive: { kind: "struct", metadata: { since: "1.51.0" } }, + SplitInclusiveMut: { kind: "struct", metadata: { since: "1.51.0" } }, + SplitMut: { kind: "struct", metadata: { since: "1.0.0" } }, + SplitN: { kind: "struct", metadata: { since: "1.0.0" } }, + SplitNMut: { kind: "struct", metadata: { since: "1.0.0" } }, + try_range: { kind: "function", metadata: { since: "unstable" } }, + Windows: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/str.ts b/packages/rust/src/builtins/core/str.ts new file mode 100644 index 000000000..183c990f3 --- /dev/null +++ b/packages/rust/src/builtins/core/str.ts @@ -0,0 +1,130 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_str = { + Bytes: { kind: "struct", metadata: { since: "1.0.0" } }, + CharIndices: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.4.0" } }, + offset: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, + Chars: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + EncodeUtf16: { kind: "struct", metadata: { since: "1.8.0" } }, + EscapeDebug: { kind: "struct", metadata: { since: "1.34.0" } }, + EscapeDefault: { kind: "struct", metadata: { since: "1.34.0" } }, + EscapeUnicode: { kind: "struct", metadata: { since: "1.34.0" } }, + from_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "unstable" } }, + from_utf8: { kind: "function", metadata: { since: "1.0.0" } }, + from_utf8_mut: { kind: "function", metadata: { since: "1.20.0" } }, + from_utf8_unchecked: { kind: "function", metadata: { since: "1.0.0" } }, + from_utf8_unchecked_mut: { kind: "function", metadata: { since: "1.20.0" } }, + FromStr: { kind: "trait", metadata: { since: "1.0.0" } }, + Lines: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + LinesAny: { kind: "struct", metadata: { since: "1.0.0" } }, + Matches: { kind: "struct", metadata: { since: "1.2.0" } }, + MatchIndices: { kind: "struct", metadata: { since: "1.5.0" } }, + next_code_point: { kind: "function", metadata: { since: "unstable" } }, + ParseBoolError: { kind: "struct", metadata: { since: "1.0.0" } }, + RMatches: { kind: "struct", metadata: { since: "1.2.0" } }, + RMatchIndices: { kind: "struct", metadata: { since: "1.5.0" } }, + RSplit: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RSplitN: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RSplitTerminator: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Split: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitAsciiWhitespace: { + kind: "struct", metadata: { since: "1.34.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitInclusive: { + kind: "struct", metadata: { since: "1.51.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitN: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitTerminator: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitWhitespace: { + kind: "struct", metadata: { since: "1.1.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + utf8_char_width: { kind: "function", metadata: { since: "unstable" } }, + Utf8Chunk: { + kind: "struct", metadata: { since: "1.79.0" }, + members: { + invalid: { kind: "function", metadata: { since: "1.79.0" } }, + valid: { kind: "function", metadata: { since: "1.79.0" } }, + }, + }, + Utf8Chunks: { kind: "struct", metadata: { since: "1.79.0" } }, + Utf8Error: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + error_len: { kind: "function", metadata: { since: "1.20.0" } }, + valid_up_to: { kind: "function", metadata: { since: "1.5.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_str_pattern = { + CharArrayRefSearcher: { kind: "struct" }, + CharArraySearcher: { kind: "struct" }, + CharPredicateSearcher: { kind: "struct" }, + CharSearcher: { kind: "struct" }, + CharSliceSearcher: { kind: "struct" }, + DoubleEndedSearcher: { kind: "trait" }, + Pattern: { kind: "trait" }, + ReverseSearcher: { kind: "trait" }, + Searcher: { kind: "trait" }, + SearchStep: { kind: "enum" }, + StrSearcher: { kind: "struct" }, + Utf8Pattern: { kind: "enum" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/sync.ts b/packages/rust/src/builtins/core/sync.ts new file mode 100644 index 000000000..fd902055c --- /dev/null +++ b/packages/rust/src/builtins/core/sync.ts @@ -0,0 +1,79 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_sync = { + Exclusive: { + kind: "struct", metadata: { since: "unstable" }, + members: { + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_pin_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "unstable" } }, + get_pin_mut: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_sync_atomic = { + Atomic: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_ptr: { kind: "function", metadata: { since: "1.70.0" } }, + compare_and_swap: { kind: "function", metadata: { since: "1.0.0" } }, + compare_exchange: { kind: "function", metadata: { since: "1.10.0" } }, + compare_exchange_weak: { kind: "function", metadata: { since: "1.10.0" } }, + fetch_add: { kind: "function", metadata: { since: "1.34.0" } }, + fetch_and: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_byte_add: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_byte_sub: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_max: { kind: "function", metadata: { since: "1.45.0" } }, + fetch_min: { kind: "function", metadata: { since: "1.45.0" } }, + fetch_nand: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_not: { kind: "function", metadata: { since: "1.81.0" } }, + fetch_or: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_ptr_add: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_ptr_sub: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_sub: { kind: "function", metadata: { since: "1.34.0" } }, + fetch_update: { kind: "function", metadata: { since: "1.53.0" } }, + fetch_xor: { kind: "function", metadata: { since: "1.0.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.75.0" } }, + get_mut: { kind: "function", metadata: { since: "1.15.0" } }, + get_mut_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "1.15.0" } }, + load: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + store: { kind: "function", metadata: { since: "1.0.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + try_update: { kind: "function", metadata: { since: "1.95.0" } }, + update: { kind: "function", metadata: { since: "1.95.0" } }, + }, + }, + ATOMIC_BOOL_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + ATOMIC_ISIZE_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + ATOMIC_USIZE_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + AtomicBool: { kind: "type-alias", metadata: { since: "1.0.0" } }, + AtomicI128: { kind: "type-alias", metadata: { since: "unstable" } }, + AtomicI16: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI32: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI64: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI8: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicIsize: { kind: "type-alias", metadata: { since: "1.0.0" } }, + AtomicPrimitive: { kind: "trait", metadata: { since: "unstable" } }, + AtomicPtr: { kind: "type-alias", metadata: { since: "1.0.0" } }, + AtomicU128: { kind: "type-alias", metadata: { since: "unstable" } }, + AtomicU16: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicU32: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicU64: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicU8: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicUsize: { kind: "type-alias", metadata: { since: "1.0.0" } }, + compiler_fence: { kind: "function", metadata: { since: "1.21.0" } }, + fence: { kind: "function", metadata: { since: "1.0.0" } }, + Ordering: { kind: "enum", metadata: { since: "1.0.0" } }, + spin_loop_hint: { kind: "function", metadata: { since: "1.24.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/task.ts b/packages/rust/src/builtins/core/task.ts new file mode 100644 index 000000000..21dffb828 --- /dev/null +++ b/packages/rust/src/builtins/core/task.ts @@ -0,0 +1,78 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_task = { + Context: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + ext: { kind: "function", metadata: { since: "unstable" } }, + from_waker: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + local_waker: { kind: "function", metadata: { since: "unstable" } }, + waker: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, + ContextBuilder: { + kind: "struct", metadata: { since: "unstable" }, + members: { + build: { kind: "function", metadata: { since: "unstable" } }, + ext: { kind: "function", metadata: { since: "unstable" } }, + from: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_waker: { kind: "function", associated: true, metadata: { since: "unstable" } }, + local_waker: { kind: "function", metadata: { since: "unstable" } }, + waker: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + LocalWaker: { + kind: "struct", metadata: { since: "unstable" }, + members: { + data: { kind: "function", metadata: { since: "unstable" } }, + from_fn_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + noop: { kind: "function", associated: true, metadata: { since: "unstable" } }, + vtable: { kind: "function", metadata: { since: "unstable" } }, + wake: { kind: "function", metadata: { since: "unstable" } }, + wake_by_ref: { kind: "function", metadata: { since: "unstable" } }, + will_wake: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Poll: { + kind: "enum", metadata: { since: "1.36.0" }, + members: { + is_pending: { kind: "function", metadata: { since: "1.49.0" } }, + is_ready: { kind: "function", metadata: { since: "1.49.0" } }, + map: { kind: "function", metadata: { since: "1.36.0" } }, + map_err: { kind: "function", metadata: { since: "1.36.0" } }, + map_ok: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, + RawWaker: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + RawWakerVTable: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + ready: { kind: "symbol", metadata: { since: "1.64.0" } }, + Waker: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + data: { kind: "function", metadata: { since: "1.83.0" } }, + from_fn_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.83.0" } }, + noop: { kind: "function", associated: true, metadata: { since: "1.85.0" } }, + vtable: { kind: "function", metadata: { since: "1.83.0" } }, + wake: { kind: "function", metadata: { since: "1.36.0" } }, + wake_by_ref: { kind: "function", metadata: { since: "1.36.0" } }, + will_wake: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/time.ts b/packages/rust/src/builtins/core/time.ts new file mode 100644 index 000000000..da82672da --- /dev/null +++ b/packages/rust/src/builtins/core/time.ts @@ -0,0 +1,55 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_time = { + Duration: { + kind: "struct", metadata: { since: "1.3.0" }, + members: { + abs_diff: { kind: "function", metadata: { since: "1.81.0" } }, + as_micros: { kind: "function", metadata: { since: "1.33.0" } }, + as_millis: { kind: "function", metadata: { since: "1.33.0" } }, + as_millis_f32: { kind: "function", metadata: { since: "unstable" } }, + as_millis_f64: { kind: "function", metadata: { since: "unstable" } }, + as_nanos: { kind: "function", metadata: { since: "1.33.0" } }, + as_secs: { kind: "function", metadata: { since: "1.3.0" } }, + as_secs_f32: { kind: "function", metadata: { since: "1.38.0" } }, + as_secs_f64: { kind: "function", metadata: { since: "1.38.0" } }, + checked_add: { kind: "function", metadata: { since: "1.16.0" } }, + checked_div: { kind: "function", metadata: { since: "1.16.0" } }, + checked_mul: { kind: "function", metadata: { since: "1.16.0" } }, + checked_sub: { kind: "function", metadata: { since: "1.16.0" } }, + div_duration_ceil: { kind: "function", metadata: { since: "unstable" } }, + div_duration_f32: { kind: "function", metadata: { since: "1.80.0" } }, + div_duration_f64: { kind: "function", metadata: { since: "1.80.0" } }, + div_duration_floor: { kind: "function", metadata: { since: "unstable" } }, + div_f32: { kind: "function", metadata: { since: "1.38.0" } }, + div_f64: { kind: "function", metadata: { since: "1.38.0" } }, + from_days: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_hours: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_micros: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + from_millis: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + from_mins: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_nanos: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + from_nanos_u128: { kind: "function", associated: true, metadata: { since: "1.93.0" } }, + from_secs: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + from_secs_f32: { kind: "function", associated: true, metadata: { since: "1.38.0" } }, + from_secs_f64: { kind: "function", associated: true, metadata: { since: "1.38.0" } }, + from_weeks: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_zero: { kind: "function", metadata: { since: "1.53.0" } }, + mul_f32: { kind: "function", metadata: { since: "1.38.0" } }, + mul_f64: { kind: "function", metadata: { since: "1.38.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + saturating_add: { kind: "function", metadata: { since: "1.53.0" } }, + saturating_mul: { kind: "function", metadata: { since: "1.53.0" } }, + saturating_sub: { kind: "function", metadata: { since: "1.53.0" } }, + subsec_micros: { kind: "function", metadata: { since: "1.27.0" } }, + subsec_millis: { kind: "function", metadata: { since: "1.27.0" } }, + subsec_nanos: { kind: "function", metadata: { since: "1.3.0" } }, + try_from_secs_f32: { kind: "function", associated: true, metadata: { since: "1.66.0" } }, + try_from_secs_f64: { kind: "function", associated: true, metadata: { since: "1.66.0" } }, + }, + }, + TryFromFloatSecsError: { kind: "struct", metadata: { since: "1.66.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/core/ub_checks.ts b/packages/rust/src/builtins/core/ub_checks.ts new file mode 100644 index 000000000..33b0660b6 --- /dev/null +++ b/packages/rust/src/builtins/core/ub_checks.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ub_checks = { + assert_unsafe_precondition: { kind: "symbol", metadata: { since: "unstable" } }, + check_library_ub: { kind: "function" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/index.ts b/packages/rust/src/builtins/index.ts new file mode 100644 index 000000000..c7aed2ada --- /dev/null +++ b/packages/rust/src/builtins/index.ts @@ -0,0 +1,13 @@ +export { + PRELUDE_TYPES, + PRELUDE_TYPES_2015, + PRELUDE_TYPES_2018, + PRELUDE_TYPES_2021, + PRELUDE_TYPES_2024, +} from "./prelude.js"; +export { alloc } from "./alloc/index.js"; +export type { AllocCrate } from "./alloc/index.js"; +export { core } from "./core/index.js"; +export type { CoreCrate } from "./core/index.js"; +export { std } from "./std/index.js"; +export type { StdCrate } from "./std/index.js"; diff --git a/packages/rust/src/builtins/prelude.ts b/packages/rust/src/builtins/prelude.ts new file mode 100644 index 000000000..b4efde03b --- /dev/null +++ b/packages/rust/src/builtins/prelude.ts @@ -0,0 +1,315 @@ +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +/** + * Types, traits, enum variants, and primitives that are automatically in scope + * via the Rust prelude. References to these do not need `use` statements. + * + * This is the union of all edition preludes (2015–2024) plus primitive types. + */ +export const PRELUDE_TYPES = new Set([ + "AsMut", + "AsRef", + "AsyncFn", + "AsyncFnMut", + "AsyncFnOnce", + "Box", + "Clone", + "Copy", + "Default", + "DoubleEndedIterator", + "Drop", + "Eq", + "Err", + "ExactSizeIterator", + "Extend", + "Fn", + "FnMut", + "FnOnce", + "From", + "FromIterator", + "Future", + "Into", + "IntoFuture", + "IntoIterator", + "Iterator", + "None", + "Ok", + "Option", + "Ord", + "PartialEq", + "PartialOrd", + "Result", + "Send", + "Sized", + "Some", + "String", + "Sync", + "ToOwned", + "ToString", + "TryFrom", + "TryInto", + "Unpin", + "Vec", + "bool", + "char", + "f32", + "f64", + "i128", + "i16", + "i32", + "i64", + "i8", + "isize", + "str", + "u128", + "u16", + "u32", + "u64", + "u8", + "usize", +]); + +/** Prelude types for the 2015 edition. */ +export const PRELUDE_TYPES_2015 = new Set([ + "AsMut", + "AsRef", + "AsyncFn", + "AsyncFnMut", + "AsyncFnOnce", + "Box", + "Clone", + "Copy", + "Default", + "DoubleEndedIterator", + "Drop", + "Eq", + "Err", + "ExactSizeIterator", + "Extend", + "Fn", + "FnMut", + "FnOnce", + "From", + "Into", + "IntoIterator", + "Iterator", + "None", + "Ok", + "Option", + "Ord", + "PartialEq", + "PartialOrd", + "Result", + "Send", + "Sized", + "Some", + "String", + "Sync", + "ToOwned", + "ToString", + "Unpin", + "Vec", + "bool", + "char", + "f32", + "f64", + "i128", + "i16", + "i32", + "i64", + "i8", + "isize", + "str", + "u128", + "u16", + "u32", + "u64", + "u8", + "usize", +]); + +/** Prelude types for the 2018 edition. */ +export const PRELUDE_TYPES_2018 = new Set([ + "AsMut", + "AsRef", + "AsyncFn", + "AsyncFnMut", + "AsyncFnOnce", + "Box", + "Clone", + "Copy", + "Default", + "DoubleEndedIterator", + "Drop", + "Eq", + "Err", + "ExactSizeIterator", + "Extend", + "Fn", + "FnMut", + "FnOnce", + "From", + "Into", + "IntoIterator", + "Iterator", + "None", + "Ok", + "Option", + "Ord", + "PartialEq", + "PartialOrd", + "Result", + "Send", + "Sized", + "Some", + "String", + "Sync", + "ToOwned", + "ToString", + "Unpin", + "Vec", + "bool", + "char", + "f32", + "f64", + "i128", + "i16", + "i32", + "i64", + "i8", + "isize", + "str", + "u128", + "u16", + "u32", + "u64", + "u8", + "usize", +]); + +/** Prelude types for the 2021 edition. */ +export const PRELUDE_TYPES_2021 = new Set([ + "AsMut", + "AsRef", + "AsyncFn", + "AsyncFnMut", + "AsyncFnOnce", + "Box", + "Clone", + "Copy", + "Default", + "DoubleEndedIterator", + "Drop", + "Eq", + "Err", + "ExactSizeIterator", + "Extend", + "Fn", + "FnMut", + "FnOnce", + "From", + "FromIterator", + "Into", + "IntoIterator", + "Iterator", + "None", + "Ok", + "Option", + "Ord", + "PartialEq", + "PartialOrd", + "Result", + "Send", + "Sized", + "Some", + "String", + "Sync", + "ToOwned", + "ToString", + "TryFrom", + "TryInto", + "Unpin", + "Vec", + "bool", + "char", + "f32", + "f64", + "i128", + "i16", + "i32", + "i64", + "i8", + "isize", + "str", + "u128", + "u16", + "u32", + "u64", + "u8", + "usize", +]); + +/** Prelude types for the 2024 edition. */ +export const PRELUDE_TYPES_2024 = new Set([ + "AsMut", + "AsRef", + "AsyncFn", + "AsyncFnMut", + "AsyncFnOnce", + "Box", + "Clone", + "Copy", + "Default", + "DoubleEndedIterator", + "Drop", + "Eq", + "Err", + "ExactSizeIterator", + "Extend", + "Fn", + "FnMut", + "FnOnce", + "From", + "FromIterator", + "Future", + "Into", + "IntoFuture", + "IntoIterator", + "Iterator", + "None", + "Ok", + "Option", + "Ord", + "PartialEq", + "PartialOrd", + "Result", + "Send", + "Sized", + "Some", + "String", + "Sync", + "ToOwned", + "ToString", + "TryFrom", + "TryInto", + "Unpin", + "Vec", + "bool", + "char", + "f32", + "f64", + "i128", + "i16", + "i32", + "i64", + "i8", + "isize", + "str", + "u128", + "u16", + "u32", + "u64", + "u8", + "usize", +]); diff --git a/packages/rust/src/builtins/std/alloc.ts b/packages/rust/src/builtins/std/alloc.ts new file mode 100644 index 000000000..0655a01c5 --- /dev/null +++ b/packages/rust/src/builtins/std/alloc.ts @@ -0,0 +1,46 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_alloc = { + alloc: { kind: "function", metadata: { since: "1.28.0" } }, + alloc_zeroed: { kind: "function", metadata: { since: "1.28.0" } }, + Allocator: { kind: "trait", metadata: { since: "unstable" } }, + AllocError: { kind: "struct", metadata: { since: "unstable" } }, + dealloc: { kind: "function", metadata: { since: "1.28.0" } }, + Global: { kind: "struct", metadata: { since: "unstable" } }, + GlobalAlloc: { kind: "trait", metadata: { since: "1.28.0" } }, + handle_alloc_error: { kind: "function", metadata: { since: "1.28.0" } }, + Layout: { + kind: "struct", metadata: { since: "1.28.0" }, + members: { + adjust_alignment_to: { kind: "function", metadata: { since: "unstable" } }, + align: { kind: "function", metadata: { since: "1.28.0" } }, + align_to: { kind: "function", metadata: { since: "1.44.0" } }, + alignment: { kind: "function", metadata: { since: "unstable" } }, + array: { kind: "function", associated: true, metadata: { since: "1.44.0" } }, + dangling_ptr: { kind: "function", metadata: { since: "1.95.0" } }, + extend: { kind: "function", metadata: { since: "1.44.0" } }, + extend_packed: { kind: "function", metadata: { since: "1.95.0" } }, + for_value: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + for_value_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_size_align: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + from_size_align_unchecked: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + from_size_alignment: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_size_alignment_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + pad_to_align: { kind: "function", metadata: { since: "1.44.0" } }, + padding_needed_for: { kind: "function", metadata: { since: "unstable" } }, + repeat: { kind: "function", metadata: { since: "1.95.0" } }, + repeat_packed: { kind: "function", metadata: { since: "1.95.0" } }, + size: { kind: "function", metadata: { since: "1.28.0" } }, + }, + }, + LayoutErr: { kind: "type-alias", metadata: { since: "1.28.0" } }, + LayoutError: { kind: "struct", metadata: { since: "1.50.0" } }, + realloc: { kind: "function", metadata: { since: "1.28.0" } }, + set_alloc_error_hook: { kind: "function", metadata: { since: "unstable" } }, + System: { kind: "struct", metadata: { since: "1.28.0" } }, + take_alloc_error_hook: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/any.ts b/packages/rust/src/builtins/std/any.ts new file mode 100644 index 000000000..79058e3e7 --- /dev/null +++ b/packages/rust/src/builtins/std/any.ts @@ -0,0 +1,21 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_any = { + Any: { kind: "trait" }, + try_as_dyn: { kind: "function", metadata: { since: "unstable" } }, + try_as_dyn_mut: { kind: "function", metadata: { since: "unstable" } }, + type_name: { kind: "function", metadata: { since: "1.38.0" } }, + type_name_of_val: { kind: "function", metadata: { since: "1.76.0" } }, + TypeId: { + kind: "struct", + members: { + info: { kind: "function", metadata: { since: "unstable" } }, + of: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + trait_info_of: { kind: "function", metadata: { since: "unstable" } }, + trait_info_of_trait_type_id: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/array.ts b/packages/rust/src/builtins/std/array.ts new file mode 100644 index 000000000..be5ecf657 --- /dev/null +++ b/packages/rust/src/builtins/std/array.ts @@ -0,0 +1,24 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_array = { + from_fn: { kind: "function", metadata: { since: "1.63.0" } }, + from_mut: { kind: "function", metadata: { since: "1.53.0" } }, + from_ref: { kind: "function", metadata: { since: "1.53.0" } }, + Guard: { kind: "struct" }, + IntoIter: { + kind: "struct", metadata: { since: "1.51.0" }, + members: { + as_mut_slice: { kind: "function", metadata: { since: "1.51.0" } }, + as_slice: { kind: "function", metadata: { since: "1.51.0" } }, + empty: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + repeat: { kind: "function", metadata: { since: "1.91.0" } }, + try_from_fn: { kind: "function", metadata: { since: "unstable" } }, + TryFromSliceError: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/ascii.ts b/packages/rust/src/builtins/std/ascii.ts new file mode 100644 index 000000000..6a07ee9ee --- /dev/null +++ b/packages/rust/src/builtins/std/ascii.ts @@ -0,0 +1,39 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ascii = { + AsciiExt: { kind: "trait", metadata: { since: "1.0.0" } }, + Char: { + kind: "enum", + members: { + as_str: { kind: "function", metadata: { since: "unstable" } }, + digit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + digit_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + eq_ignore_case: { kind: "function", metadata: { since: "unstable" } }, + escape_ascii: { kind: "function", metadata: { since: "unstable" } }, + from_u8: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_u8_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_alphabetic: { kind: "function", metadata: { since: "unstable" } }, + is_alphanumeric: { kind: "function", metadata: { since: "unstable" } }, + is_control: { kind: "function", metadata: { since: "unstable" } }, + is_digit: { kind: "function", metadata: { since: "unstable" } }, + is_graphic: { kind: "function", metadata: { since: "unstable" } }, + is_hexdigit: { kind: "function", metadata: { since: "unstable" } }, + is_lowercase: { kind: "function", metadata: { since: "unstable" } }, + is_octdigit: { kind: "function", metadata: { since: "unstable" } }, + is_punctuation: { kind: "function", metadata: { since: "unstable" } }, + is_uppercase: { kind: "function", metadata: { since: "unstable" } }, + is_whitespace: { kind: "function", metadata: { since: "unstable" } }, + make_lowercase: { kind: "function", metadata: { since: "unstable" } }, + make_uppercase: { kind: "function", metadata: { since: "unstable" } }, + to_char: { kind: "function", metadata: { since: "unstable" } }, + to_lowercase: { kind: "function", metadata: { since: "unstable" } }, + to_u8: { kind: "function", metadata: { since: "unstable" } }, + to_uppercase: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + escape_default: { kind: "function" }, + EscapeDefault: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/backtrace.ts b/packages/rust/src/builtins/std/backtrace.ts new file mode 100644 index 000000000..8f1ecd8fd --- /dev/null +++ b/packages/rust/src/builtins/std/backtrace.ts @@ -0,0 +1,19 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_backtrace = { + Backtrace: { + kind: "struct", metadata: { since: "1.65.0" }, + members: { + capture: { kind: "function", associated: true, metadata: { since: "1.65.0" } }, + disabled: { kind: "function", associated: true, metadata: { since: "1.65.0" } }, + force_capture: { kind: "function", associated: true, metadata: { since: "1.65.0" } }, + frames: { kind: "function", metadata: { since: "unstable" } }, + status: { kind: "function", metadata: { since: "1.65.0" } }, + }, + }, + BacktraceFrame: { kind: "struct", metadata: { since: "unstable" } }, + BacktraceStatus: { kind: "enum", metadata: { since: "1.65.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/borrow.ts b/packages/rust/src/builtins/std/borrow.ts new file mode 100644 index 000000000..dbedf6e75 --- /dev/null +++ b/packages/rust/src/builtins/std/borrow.ts @@ -0,0 +1,19 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_borrow = { + Borrow: { kind: "trait" }, + BorrowMut: { kind: "trait" }, + Cow: { + kind: "enum", + members: { + into_owned: { kind: "function", metadata: { since: "1.0.0" } }, + is_borrowed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_owned: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_mut: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ToOwned: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/boxed.ts b/packages/rust/src/builtins/std/boxed.ts new file mode 100644 index 000000000..d2213b6b0 --- /dev/null +++ b/packages/rust/src/builtins/std/boxed.ts @@ -0,0 +1,69 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_boxed = { + Box: { + kind: "struct", + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_mut_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.0.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + from_non_null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_non_null_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_boxed_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_non_null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_non_null_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_pin: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + leak: { kind: "function", associated: true, metadata: { since: "1.26.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + take: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + write: { kind: "function", associated: true, metadata: { since: "1.87.0" } }, + }, + }, + ThinBox: { + kind: "struct", metadata: { since: "unstable" }, + members: { + new: { kind: "function", associated: true }, + new_unsize: { kind: "function", associated: true }, + try_new: { kind: "function", associated: true }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/cell.ts b/packages/rust/src/builtins/std/cell.ts new file mode 100644 index 000000000..f5a93c71e --- /dev/null +++ b/packages/rust/src/builtins/std/cell.ts @@ -0,0 +1,122 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_cell = { + BorrowError: { kind: "struct" }, + BorrowMutError: { kind: "struct" }, + BorrowRef: { kind: "struct" }, + BorrowRefMut: { kind: "struct" }, + Cell: { + kind: "struct", + members: { + as_array_of_cells: { kind: "function", metadata: { since: "1.91.0" } }, + as_ptr: { kind: "function", metadata: { since: "1.12.0" } }, + as_slice_of_cells: { kind: "function", metadata: { since: "1.37.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.37.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_cloned: { kind: "function" }, + get_mut: { kind: "function", metadata: { since: "1.11.0" } }, + into_inner: { kind: "function", metadata: { since: "1.17.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.17.0" } }, + set: { kind: "function", metadata: { since: "1.0.0" } }, + swap: { kind: "function", metadata: { since: "1.17.0" } }, + take: { kind: "function", metadata: { since: "1.17.0" } }, + update: { kind: "function", metadata: { since: "1.88.0" } }, + }, + }, + CloneFromCell: { kind: "trait" }, + LazyCell: { + kind: "struct", metadata: { since: "1.80.0" }, + members: { + force: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + force_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + }, + }, + OnceCell: { + kind: "struct", metadata: { since: "1.70.0" }, + members: { + get: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut_or_init: { kind: "function", metadata: { since: "unstable" } }, + get_mut_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + get_or_init: { kind: "function", metadata: { since: "1.70.0" } }, + get_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "1.70.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + set: { kind: "function", metadata: { since: "1.70.0" } }, + take: { kind: "function", metadata: { since: "1.70.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Ref: { + kind: "struct", + members: { + clone: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + filter_map: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + leak: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + map_split: { kind: "function", associated: true, metadata: { since: "1.35.0" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RefCell: { + kind: "struct", + members: { + as_ptr: { kind: "function", metadata: { since: "1.12.0" } }, + borrow: { kind: "function", metadata: { since: "1.0.0" } }, + borrow_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.11.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.24.0" } }, + replace_with: { kind: "function", metadata: { since: "1.35.0" } }, + swap: { kind: "function", metadata: { since: "1.24.0" } }, + take: { kind: "function", metadata: { since: "1.50.0" } }, + try_borrow: { kind: "function", metadata: { since: "1.13.0" } }, + try_borrow_mut: { kind: "function", metadata: { since: "1.13.0" } }, + try_borrow_unguarded: { kind: "function", metadata: { since: "1.37.0" } }, + undo_leak: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RefMut: { + kind: "struct", + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "1.63.0" } }, + leak: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + map_split: { kind: "function", associated: true, metadata: { since: "1.35.0" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + SyncUnsafeCell: { + kind: "struct", + members: { + get: { kind: "function" }, + get_mut: { kind: "function" }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true }, + raw_get: { kind: "function", associated: true }, + }, + }, + UnsafeCell: { + kind: "struct", + members: { + as_mut_unchecked: { kind: "function", metadata: { since: "unstable" } }, + as_ref_unchecked: { kind: "function", metadata: { since: "unstable" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.50.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + raw_get: { kind: "function", associated: true, metadata: { since: "1.56.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/char.ts b/packages/rust/src/builtins/std/char.ts new file mode 100644 index 000000000..b9b6a0152 --- /dev/null +++ b/packages/rust/src/builtins/std/char.ts @@ -0,0 +1,34 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_char_ = { + CaseMappingIter: { kind: "struct" }, + CharCase: { kind: "enum" }, + CharTryFromError: { kind: "struct", metadata: { since: "1.34.0" } }, + decode_utf16: { kind: "function", metadata: { since: "1.9.0" } }, + DecodeUtf16: { kind: "struct", metadata: { since: "1.9.0" } }, + DecodeUtf16Error: { + kind: "struct", metadata: { since: "1.9.0" }, + members: { + unpaired_surrogate: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + EscapeDebug: { kind: "struct" }, + EscapeDefault: { kind: "struct" }, + EscapeUnicode: { kind: "struct" }, + from_digit: { kind: "function", metadata: { since: "1.0.0" } }, + from_u32: { kind: "function", metadata: { since: "1.0.0" } }, + from_u32_unchecked: { kind: "function", metadata: { since: "1.5.0" } }, + MAX: { kind: "const", metadata: { since: "1.0.0" } }, + MAX_LEN_UTF16: { kind: "const", metadata: { since: "unstable" } }, + MAX_LEN_UTF8: { kind: "const", metadata: { since: "unstable" } }, + ParseCharError: { kind: "struct", metadata: { since: "1.20.0" } }, + REPLACEMENT_CHARACTER: { kind: "const" }, + ToLowercase: { kind: "struct" }, + ToTitlecase: { kind: "struct" }, + ToUppercase: { kind: "struct" }, + TryFromCharError: { kind: "struct" }, + UNICODE_VERSION: { kind: "const", metadata: { since: "1.45.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/clone.ts b/packages/rust/src/builtins/std/clone.ts new file mode 100644 index 000000000..1f62ceea6 --- /dev/null +++ b/packages/rust/src/builtins/std/clone.ts @@ -0,0 +1,11 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_clone = { + Clone: { kind: "trait" }, + CloneToUninit: { kind: "trait" }, + TrivialClone: { kind: "trait" }, + UseCloned: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/cmp.ts b/packages/rust/src/builtins/std/cmp.ts new file mode 100644 index 000000000..57652f3ad --- /dev/null +++ b/packages/rust/src/builtins/std/cmp.ts @@ -0,0 +1,35 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_cmp = { + Eq: { kind: "trait" }, + max: { kind: "function", metadata: { since: "1.0.0" } }, + max_by: { kind: "function", metadata: { since: "1.53.0" } }, + max_by_key: { kind: "function", metadata: { since: "1.53.0" } }, + min: { kind: "function" }, + min_by: { kind: "function", metadata: { since: "1.53.0" } }, + min_by_key: { kind: "function", metadata: { since: "1.53.0" } }, + minmax: { kind: "function", metadata: { since: "unstable" } }, + minmax_by: { kind: "function", metadata: { since: "unstable" } }, + minmax_by_key: { kind: "function", metadata: { since: "unstable" } }, + Ord: { kind: "trait" }, + Ordering: { + kind: "enum", + members: { + is_eq: { kind: "function", metadata: { since: "1.53.0" } }, + is_ge: { kind: "function", metadata: { since: "1.53.0" } }, + is_gt: { kind: "function", metadata: { since: "1.53.0" } }, + is_le: { kind: "function", metadata: { since: "1.53.0" } }, + is_lt: { kind: "function", metadata: { since: "1.53.0" } }, + is_ne: { kind: "function", metadata: { since: "1.53.0" } }, + reverse: { kind: "function", metadata: { since: "1.48.0" } }, + then: { kind: "function", metadata: { since: "1.48.0" } }, + then_with: { kind: "function", metadata: { since: "1.17.0" } }, + }, + }, + PartialEq: { kind: "trait" }, + PartialOrd: { kind: "trait" }, + Reverse: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/collections.ts b/packages/rust/src/builtins/std/collections.ts new file mode 100644 index 000000000..cd0f2b51b --- /dev/null +++ b/packages/rust/src/builtins/std/collections.ts @@ -0,0 +1,587 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_collections = { + BinaryHeap: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.11.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.80.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + drain_sorted: { kind: "function", metadata: { since: "unstable" } }, + from_raw_vec: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_iter_sorted: { kind: "function", metadata: { since: "unstable" } }, + into_sorted_vec: { kind: "function", metadata: { since: "1.5.0" } }, + into_vec: { kind: "function", metadata: { since: "1.5.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.12.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "unstable" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.70.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + BTreeMap: { + kind: "struct", + members: { + append: { kind: "function", metadata: { since: "1.11.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains_key: { kind: "function", metadata: { since: "1.0.0" } }, + entry: { kind: "function", metadata: { since: "1.0.0" } }, + extract_if: { kind: "function", metadata: { since: "1.91.0" } }, + first_entry: { kind: "function", metadata: { since: "1.66.0" } }, + first_key_value: { kind: "function", metadata: { since: "1.66.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_key_value: { kind: "function", metadata: { since: "1.40.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + into_keys: { kind: "function", metadata: { since: "1.54.0" } }, + into_values: { kind: "function", metadata: { since: "1.54.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + keys: { kind: "function", metadata: { since: "1.0.0" } }, + last_entry: { kind: "function", metadata: { since: "1.66.0" } }, + last_key_value: { kind: "function", metadata: { since: "1.66.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + lower_bound: { kind: "function", metadata: { since: "unstable" } }, + lower_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + merge: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_first: { kind: "function", metadata: { since: "1.66.0" } }, + pop_last: { kind: "function", metadata: { since: "1.66.0" } }, + range: { kind: "function", metadata: { since: "1.17.0" } }, + range_mut: { kind: "function", metadata: { since: "1.17.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + remove_entry: { kind: "function", metadata: { since: "1.45.0" } }, + retain: { kind: "function", metadata: { since: "1.53.0" } }, + split_off: { kind: "function", metadata: { since: "1.11.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + upper_bound: { kind: "function", metadata: { since: "unstable" } }, + upper_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + values: { kind: "function", metadata: { since: "1.0.0" } }, + values_mut: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + BTreeSet: { + kind: "struct", + members: { + append: { kind: "function", metadata: { since: "1.11.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.0.0" } }, + difference: { kind: "function", metadata: { since: "1.0.0" } }, + entry: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.91.0" } }, + first: { kind: "function", metadata: { since: "1.66.0" } }, + get: { kind: "function", metadata: { since: "1.9.0" } }, + get_or_insert: { kind: "function", metadata: { since: "unstable" } }, + get_or_insert_with: { kind: "function", metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + intersection: { kind: "function", metadata: { since: "1.0.0" } }, + is_disjoint: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + is_subset: { kind: "function", metadata: { since: "1.0.0" } }, + is_superset: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + last: { kind: "function", metadata: { since: "1.66.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + lower_bound: { kind: "function", metadata: { since: "unstable" } }, + lower_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_first: { kind: "function", metadata: { since: "1.66.0" } }, + pop_last: { kind: "function", metadata: { since: "1.66.0" } }, + range: { kind: "function", metadata: { since: "1.17.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.9.0" } }, + retain: { kind: "function", metadata: { since: "1.53.0" } }, + split_off: { kind: "function", metadata: { since: "1.11.0" } }, + symmetric_difference: { kind: "function", metadata: { since: "1.0.0" } }, + take: { kind: "function", metadata: { since: "1.9.0" } }, + union: { kind: "function", metadata: { since: "1.0.0" } }, + upper_bound: { kind: "function", metadata: { since: "unstable" } }, + upper_bound_mut: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + HashMap: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains_key: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + entry: { kind: "function", metadata: { since: "1.0.0" } }, + extract_if: { kind: "function", metadata: { since: "1.88.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_disjoint_mut: { kind: "function", metadata: { since: "1.86.0" } }, + get_disjoint_unchecked_mut: { kind: "function", metadata: { since: "1.86.0" } }, + get_key_value: { kind: "function", metadata: { since: "1.40.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + hasher: { kind: "function", metadata: { since: "1.9.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + into_keys: { kind: "function", metadata: { since: "1.54.0" } }, + into_values: { kind: "function", metadata: { since: "1.54.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + keys: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + remove_entry: { kind: "function", metadata: { since: "1.27.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.18.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + values: { kind: "function", metadata: { since: "1.0.0" } }, + values_mut: { kind: "function", metadata: { since: "1.10.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_and_hasher: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + with_capacity_and_hasher_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_hasher: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + with_hasher_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + HashSet: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.0.0" } }, + difference: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + entry: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.88.0" } }, + get: { kind: "function", metadata: { since: "1.9.0" } }, + get_or_insert: { kind: "function", metadata: { since: "unstable" } }, + get_or_insert_with: { kind: "function", metadata: { since: "unstable" } }, + hasher: { kind: "function", metadata: { since: "1.9.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + intersection: { kind: "function", metadata: { since: "1.0.0" } }, + is_disjoint: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + is_subset: { kind: "function", metadata: { since: "1.0.0" } }, + is_superset: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "1.9.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.18.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + symmetric_difference: { kind: "function", metadata: { since: "1.0.0" } }, + take: { kind: "function", metadata: { since: "1.9.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + union: { kind: "function", metadata: { since: "1.0.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_and_hasher: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + with_capacity_and_hasher_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_hasher: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + with_hasher_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + LinkedList: { + kind: "struct", + members: { + append: { kind: "function", metadata: { since: "1.0.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + cursor_back: { kind: "function", metadata: { since: "unstable" } }, + cursor_back_mut: { kind: "function", metadata: { since: "unstable" } }, + cursor_front: { kind: "function", metadata: { since: "unstable" } }, + cursor_front_mut: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + remove: { kind: "function", metadata: { since: "unstable" } }, + retain: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + TryReserveError: { + kind: "struct", + members: { + kind: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + TryReserveErrorKind: { kind: "enum" }, + VecDeque: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_slices: { kind: "function", metadata: { since: "1.5.0" } }, + as_slices: { kind: "function", metadata: { since: "1.5.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + binary_search: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by_key: { kind: "function", metadata: { since: "1.54.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "unstable" } }, + extend_front: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.5.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + make_contiguous: { kind: "function", metadata: { since: "1.48.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + partition_point: { kind: "function", metadata: { since: "1.54.0" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_back_if: { kind: "function", metadata: { since: "1.93.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front_if: { kind: "function", metadata: { since: "1.93.0" } }, + prepend: { kind: "function", metadata: { since: "unstable" } }, + prepend_from_within: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + range: { kind: "function", metadata: { since: "1.51.0" } }, + range_mut: { kind: "function", metadata: { since: "1.51.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.16.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.4.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.36.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.36.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.5.0" } }, + splice: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + swap_remove_back: { kind: "function", metadata: { since: "1.5.0" } }, + swap_remove_front: { kind: "function", metadata: { since: "1.5.0" } }, + truncate: { kind: "function", metadata: { since: "1.16.0" } }, + truncate_front: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_binary_heap = { + BinaryHeap: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.11.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.80.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + drain_sorted: { kind: "function", metadata: { since: "unstable" } }, + from_raw_vec: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_iter_sorted: { kind: "function", metadata: { since: "unstable" } }, + into_sorted_vec: { kind: "function", metadata: { since: "1.5.0" } }, + into_vec: { kind: "function", metadata: { since: "1.5.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.12.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "unstable" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.70.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Drain: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + DrainSorted: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Hole: { kind: "struct" }, + IntoIter: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIterSorted: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Iter: { kind: "struct" }, + PeekMut: { + kind: "struct", + members: { + pop: { kind: "function", associated: true, metadata: { since: "1.18.0" } }, + refresh: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RebuildOnDrop: { kind: "struct" }, +} as const satisfies Record; + +export const mod_collections_hash_map = { + DefaultHasher: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.13.0" } }, + }, + }, + RandomState: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_linked_list = { + Cursor: { + kind: "struct", + members: { + as_list: { kind: "function", metadata: { since: "unstable" } }, + back: { kind: "function", metadata: { since: "unstable" } }, + current: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "unstable" } }, + index: { kind: "function", metadata: { since: "unstable" } }, + move_next: { kind: "function", metadata: { since: "unstable" } }, + move_prev: { kind: "function", metadata: { since: "unstable" } }, + peek_next: { kind: "function", metadata: { since: "unstable" } }, + peek_prev: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + CursorMut: { + kind: "struct", + members: { + as_cursor: { kind: "function", metadata: { since: "unstable" } }, + as_list: { kind: "function", metadata: { since: "unstable" } }, + back: { kind: "function", metadata: { since: "unstable" } }, + back_mut: { kind: "function", metadata: { since: "unstable" } }, + current: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "unstable" } }, + front_mut: { kind: "function", metadata: { since: "unstable" } }, + index: { kind: "function", metadata: { since: "unstable" } }, + insert_after: { kind: "function", metadata: { since: "unstable" } }, + insert_before: { kind: "function", metadata: { since: "unstable" } }, + move_next: { kind: "function", metadata: { since: "unstable" } }, + move_prev: { kind: "function", metadata: { since: "unstable" } }, + peek_next: { kind: "function", metadata: { since: "unstable" } }, + peek_prev: { kind: "function", metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "unstable" } }, + pop_front: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "unstable" } }, + push_front: { kind: "function", metadata: { since: "unstable" } }, + remove_current: { kind: "function", metadata: { since: "unstable" } }, + remove_current_as_list: { kind: "function", metadata: { since: "unstable" } }, + splice_after: { kind: "function", metadata: { since: "unstable" } }, + splice_before: { kind: "function", metadata: { since: "unstable" } }, + split_after: { kind: "function", metadata: { since: "unstable" } }, + split_before: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExtractIf: { kind: "struct" }, + IntoIter: { kind: "struct" }, + Iter: { kind: "struct" }, + IterMut: { kind: "struct" }, + LinkedList: { + kind: "struct", + members: { + append: { kind: "function", metadata: { since: "1.0.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + cursor_back: { kind: "function", metadata: { since: "unstable" } }, + cursor_back_mut: { kind: "function", metadata: { since: "unstable" } }, + cursor_front: { kind: "function", metadata: { since: "unstable" } }, + cursor_front_mut: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + remove: { kind: "function", metadata: { since: "unstable" } }, + retain: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_collections_vec_deque = { + Drain: { kind: "struct", metadata: { since: "1.6.0" } }, + ExtractIf: { + kind: "struct", metadata: { since: "unstable" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIter: { kind: "struct", metadata: { since: "1.0.0" } }, + Iter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slices: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IterMut: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + file_name: { kind: "function", metadata: { since: "1.1.0" } }, + file_type: { kind: "function", metadata: { since: "1.1.0" } }, + metadata: { kind: "function", metadata: { since: "1.1.0" } }, + path: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + Splice: { kind: "struct", metadata: { since: "unstable" } }, + VecDeque: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_slices: { kind: "function", metadata: { since: "1.5.0" } }, + as_slices: { kind: "function", metadata: { since: "1.5.0" } }, + back: { kind: "function", metadata: { since: "1.0.0" } }, + back_mut: { kind: "function", metadata: { since: "1.0.0" } }, + binary_search: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by: { kind: "function", metadata: { since: "1.54.0" } }, + binary_search_by_key: { kind: "function", metadata: { since: "1.54.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + contains: { kind: "function", metadata: { since: "1.12.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "unstable" } }, + extend_front: { kind: "function", metadata: { since: "unstable" } }, + extract_if: { kind: "function", metadata: { since: "unstable" } }, + front: { kind: "function", metadata: { since: "1.0.0" } }, + front_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get: { kind: "function", metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.5.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + make_contiguous: { kind: "function", metadata: { since: "1.48.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + partition_point: { kind: "function", metadata: { since: "1.54.0" } }, + pop_back: { kind: "function", metadata: { since: "1.0.0" } }, + pop_back_if: { kind: "function", metadata: { since: "1.93.0" } }, + pop_front: { kind: "function", metadata: { since: "1.0.0" } }, + pop_front_if: { kind: "function", metadata: { since: "1.93.0" } }, + prepend: { kind: "function", metadata: { since: "unstable" } }, + prepend_from_within: { kind: "function", metadata: { since: "unstable" } }, + push_back: { kind: "function", metadata: { since: "1.0.0" } }, + push_back_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_front: { kind: "function", metadata: { since: "1.0.0" } }, + push_front_mut: { kind: "function", metadata: { since: "1.95.0" } }, + range: { kind: "function", metadata: { since: "1.51.0" } }, + range_mut: { kind: "function", metadata: { since: "1.51.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.16.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.4.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.36.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.36.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.5.0" } }, + splice: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + swap_remove_back: { kind: "function", metadata: { since: "1.5.0" } }, + swap_remove_front: { kind: "function", metadata: { since: "1.5.0" } }, + truncate: { kind: "function", metadata: { since: "1.16.0" } }, + truncate_front: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/contracts.ts b/packages/rust/src/builtins/std/contracts.ts new file mode 100644 index 000000000..86067df67 --- /dev/null +++ b/packages/rust/src/builtins/std/contracts.ts @@ -0,0 +1,8 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_contracts = { + build_check_ensures: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/convert.ts b/packages/rust/src/builtins/std/convert.ts new file mode 100644 index 000000000..991017412 --- /dev/null +++ b/packages/rust/src/builtins/std/convert.ts @@ -0,0 +1,16 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_convert = { + AsMut: { kind: "trait" }, + AsRef: { kind: "trait" }, + FloatToInt: { kind: "trait", metadata: { since: "unstable" } }, + From: { kind: "trait" }, + identity: { kind: "function", metadata: { since: "1.33.0" } }, + Infallible: { kind: "enum" }, + Into: { kind: "trait" }, + TryFrom: { kind: "trait" }, + TryInto: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/default.ts b/packages/rust/src/builtins/std/default.ts new file mode 100644 index 000000000..b64fcedfa --- /dev/null +++ b/packages/rust/src/builtins/std/default.ts @@ -0,0 +1,8 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_default_ = { + Default: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/env.ts b/packages/rust/src/builtins/std/env.ts new file mode 100644 index 000000000..10426819b --- /dev/null +++ b/packages/rust/src/builtins/std/env.ts @@ -0,0 +1,40 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_env = { + args: { kind: "function", metadata: { since: "1.0.0" } }, + Args: { kind: "struct", metadata: { since: "1.0.0" } }, + args_os: { kind: "function", metadata: { since: "1.0.0" } }, + ArgsOs: { kind: "struct", metadata: { since: "1.0.0" } }, + current_dir: { kind: "function", metadata: { since: "1.0.0" } }, + current_exe: { kind: "function", metadata: { since: "1.0.0" } }, + home_dir: { kind: "function", metadata: { since: "1.0.0" } }, + join_paths: { kind: "function", metadata: { since: "1.0.0" } }, + JoinPathsError: { kind: "struct", metadata: { since: "1.0.0" } }, + remove_var: { kind: "function", metadata: { since: "1.0.0" } }, + set_current_dir: { kind: "function", metadata: { since: "1.0.0" } }, + set_var: { kind: "function", metadata: { since: "1.0.0" } }, + split_paths: { kind: "function", metadata: { since: "1.0.0" } }, + SplitPaths: { kind: "struct", metadata: { since: "1.0.0" } }, + temp_dir: { kind: "function", metadata: { since: "1.0.0" } }, + var: { kind: "function", metadata: { since: "1.0.0" } }, + var_os: { kind: "function", metadata: { since: "1.0.0" } }, + VarError: { kind: "enum", metadata: { since: "1.0.0" } }, + vars: { kind: "function", metadata: { since: "1.0.0" } }, + Vars: { kind: "struct", metadata: { since: "1.0.0" } }, + vars_os: { kind: "function", metadata: { since: "1.0.0" } }, + VarsOs: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; + +export const mod_env_consts = { + ARCH: { kind: "const", metadata: { since: "1.0.0" } }, + DLL_EXTENSION: { kind: "const", metadata: { since: "1.0.0" } }, + DLL_PREFIX: { kind: "const", metadata: { since: "1.0.0" } }, + DLL_SUFFIX: { kind: "const", metadata: { since: "1.0.0" } }, + EXE_EXTENSION: { kind: "const", metadata: { since: "1.0.0" } }, + EXE_SUFFIX: { kind: "const", metadata: { since: "1.0.0" } }, + FAMILY: { kind: "const", metadata: { since: "1.0.0" } }, + OS: { kind: "const", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/error.ts b/packages/rust/src/builtins/std/error.ts new file mode 100644 index 000000000..4410c190b --- /dev/null +++ b/packages/rust/src/builtins/std/error.ts @@ -0,0 +1,30 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_error = { + Error: { kind: "trait" }, + Report: { + kind: "struct", metadata: { since: "unstable" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pretty: { kind: "function", metadata: { since: "unstable" } }, + show_backtrace: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Request: { + kind: "struct", + members: { + provide_ref: { kind: "function", metadata: { since: "unstable" } }, + provide_ref_with: { kind: "function", metadata: { since: "unstable" } }, + provide_value: { kind: "function", metadata: { since: "unstable" } }, + provide_value_with: { kind: "function", metadata: { since: "unstable" } }, + would_be_satisfied_by_ref_of: { kind: "function", metadata: { since: "unstable" } }, + would_be_satisfied_by_value_of: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + request_ref: { kind: "function" }, + request_value: { kind: "function" }, + Source: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/f32.ts b/packages/rust/src/builtins/std/f32.ts new file mode 100644 index 000000000..9d66f6479 --- /dev/null +++ b/packages/rust/src/builtins/std/f32.ts @@ -0,0 +1,67 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_f32 = { + DIGITS: { kind: "const" }, + EPSILON: { kind: "const" }, + INFINITY: { kind: "const" }, + MANTISSA_DIGITS: { kind: "const" }, + MAX: { kind: "const" }, + MAX_10_EXP: { kind: "const" }, + MAX_EXP: { kind: "const" }, + MIN: { kind: "const" }, + MIN_10_EXP: { kind: "const" }, + MIN_EXP: { kind: "const" }, + MIN_POSITIVE: { kind: "const" }, + NAN: { kind: "const" }, + NEG_INFINITY: { kind: "const" }, + RADIX: { kind: "const" }, +} as const satisfies Record; + +export const mod_f32_consts = { + E: { kind: "const", metadata: { since: "1.0.0" } }, + EULER_GAMMA: { kind: "const", metadata: { since: "1.94.0" } }, + FRAC_1_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_2_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_2_SQRT_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_3: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_4: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_6: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_8: { kind: "const", metadata: { since: "1.0.0" } }, + GOLDEN_RATIO: { kind: "const", metadata: { since: "1.94.0" } }, + LN_10: { kind: "const", metadata: { since: "1.0.0" } }, + LN_2: { kind: "const", metadata: { since: "1.0.0" } }, + LOG10_2: { kind: "const", metadata: { since: "1.43.0" } }, + LOG10_E: { kind: "const", metadata: { since: "1.0.0" } }, + LOG2_10: { kind: "const", metadata: { since: "1.43.0" } }, + LOG2_E: { kind: "const", metadata: { since: "1.0.0" } }, + PI: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + TAU: { kind: "const", metadata: { since: "1.47.0" } }, +} as const satisfies Record; + +export const mod_f32_math = { + abs_sub: { kind: "function", metadata: { since: "1.0.0" } }, + cbrt: { kind: "function", metadata: { since: "unstable" } }, + ceil: { kind: "function", metadata: { since: "unstable" } }, + div_euclid: { kind: "function", metadata: { since: "unstable" } }, + floor: { kind: "function", metadata: { since: "unstable" } }, + fract: { kind: "function", metadata: { since: "unstable" } }, + mul_add: { kind: "function", metadata: { since: "unstable" } }, + powi: { kind: "function", metadata: { since: "unstable" } }, + rem_euclid: { kind: "function", metadata: { since: "unstable" } }, + round: { kind: "function", metadata: { since: "unstable" } }, + round_ties_even: { kind: "function", metadata: { since: "unstable" } }, + sqrt: { kind: "function", metadata: { since: "unstable" } }, + trunc: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/f64.ts b/packages/rust/src/builtins/std/f64.ts new file mode 100644 index 000000000..ddfbfcaf9 --- /dev/null +++ b/packages/rust/src/builtins/std/f64.ts @@ -0,0 +1,67 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_f64 = { + DIGITS: { kind: "const" }, + EPSILON: { kind: "const" }, + INFINITY: { kind: "const" }, + MANTISSA_DIGITS: { kind: "const" }, + MAX: { kind: "const" }, + MAX_10_EXP: { kind: "const" }, + MAX_EXP: { kind: "const" }, + MIN: { kind: "const" }, + MIN_10_EXP: { kind: "const" }, + MIN_EXP: { kind: "const" }, + MIN_POSITIVE: { kind: "const" }, + NAN: { kind: "const" }, + NEG_INFINITY: { kind: "const" }, + RADIX: { kind: "const" }, +} as const satisfies Record; + +export const mod_f64_consts = { + E: { kind: "const", metadata: { since: "1.0.0" } }, + EULER_GAMMA: { kind: "const", metadata: { since: "1.94.0" } }, + FRAC_1_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_1_SQRT_2PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + FRAC_1_SQRT_PI: { kind: "const", metadata: { since: "unstable" } }, + FRAC_2_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_2_SQRT_PI: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_2: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_3: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_4: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_6: { kind: "const", metadata: { since: "1.0.0" } }, + FRAC_PI_8: { kind: "const", metadata: { since: "1.0.0" } }, + GOLDEN_RATIO: { kind: "const", metadata: { since: "1.94.0" } }, + LN_10: { kind: "const", metadata: { since: "1.0.0" } }, + LN_2: { kind: "const", metadata: { since: "1.0.0" } }, + LOG10_2: { kind: "const", metadata: { since: "1.43.0" } }, + LOG10_E: { kind: "const", metadata: { since: "1.0.0" } }, + LOG2_10: { kind: "const", metadata: { since: "1.43.0" } }, + LOG2_E: { kind: "const", metadata: { since: "1.0.0" } }, + PI: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_2: { kind: "const", metadata: { since: "1.0.0" } }, + SQRT_3: { kind: "const", metadata: { since: "unstable" } }, + SQRT_5: { kind: "const", metadata: { since: "unstable" } }, + TAU: { kind: "const", metadata: { since: "1.47.0" } }, +} as const satisfies Record; + +export const mod_f64_math = { + abs_sub: { kind: "function", metadata: { since: "unstable" } }, + cbrt: { kind: "function", metadata: { since: "unstable" } }, + ceil: { kind: "function", metadata: { since: "unstable" } }, + div_euclid: { kind: "function", metadata: { since: "unstable" } }, + floor: { kind: "function", metadata: { since: "unstable" } }, + fract: { kind: "function", metadata: { since: "unstable" } }, + mul_add: { kind: "function", metadata: { since: "unstable" } }, + powi: { kind: "function", metadata: { since: "unstable" } }, + rem_euclid: { kind: "function", metadata: { since: "unstable" } }, + round: { kind: "function", metadata: { since: "unstable" } }, + round_ties_even: { kind: "function", metadata: { since: "unstable" } }, + sqrt: { kind: "function", metadata: { since: "unstable" } }, + trunc: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/ffi.ts b/packages/rust/src/builtins/std/ffi.ts new file mode 100644 index 000000000..362fe7ab8 --- /dev/null +++ b/packages/rust/src/builtins/std/ffi.ts @@ -0,0 +1,259 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ffi = { + c_char: { kind: "type-alias" }, + c_double: { kind: "type-alias" }, + c_float: { kind: "type-alias" }, + c_int: { kind: "type-alias" }, + c_long: { kind: "type-alias" }, + c_longlong: { kind: "type-alias" }, + c_ptrdiff_t: { kind: "type-alias" }, + c_schar: { kind: "type-alias" }, + c_short: { kind: "type-alias" }, + c_size_t: { kind: "type-alias" }, + c_ssize_t: { kind: "type-alias" }, + c_uchar: { kind: "type-alias" }, + c_uint: { kind: "type-alias" }, + c_ulong: { kind: "type-alias" }, + c_ulonglong: { kind: "type-alias" }, + c_ushort: { kind: "type-alias" }, + c_void: { kind: "enum" }, + CStr: { + kind: "struct", + members: { + as_c_str: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.0.0" } }, + bytes: { kind: "function", metadata: { since: "unstable" } }, + count_bytes: { kind: "function", metadata: { since: "1.79.0" } }, + display: { kind: "function", metadata: { since: "unstable" } }, + from_bytes_until_nul: { kind: "function", associated: true, metadata: { since: "1.69.0" } }, + from_bytes_with_nul: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_bytes_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.71.0" } }, + to_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + to_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + CString: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + as_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_vec_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_vec_with_nul: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + from_vec_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + into_boxed_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.7.0" } }, + into_bytes_with_nul: { kind: "function", metadata: { since: "1.7.0" } }, + into_raw: { kind: "function", metadata: { since: "1.4.0" } }, + into_string: { kind: "function", metadata: { since: "1.7.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + FromBytesUntilNulError: { kind: "struct" }, + FromBytesWithNulError: { kind: "enum" }, + FromVecWithNulError: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, + IntoStringError: { + kind: "struct", + members: { + into_cstring: { kind: "function", metadata: { since: "1.7.0" } }, + utf8_error: { kind: "function", metadata: { since: "1.7.0" } }, + }, + }, + NulError: { + kind: "struct", + members: { + into_vec: { kind: "function", metadata: { since: "1.0.0" } }, + nul_position: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + OsStr: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_encoded_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + as_os_str: { kind: "function", metadata: { since: "unstable" } }, + display: { kind: "function", metadata: { since: "1.87.0" } }, + eq_ignore_ascii_case: { kind: "function", metadata: { since: "1.53.0" } }, + from_encoded_bytes_unchecked: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + into_os_string: { kind: "function", metadata: { since: "1.20.0" } }, + is_ascii: { kind: "function", metadata: { since: "1.53.0" } }, + is_empty: { kind: "function", metadata: { since: "1.9.0" } }, + len: { kind: "function", metadata: { since: "1.9.0" } }, + make_ascii_lowercase: { kind: "function", metadata: { since: "1.53.0" } }, + make_ascii_uppercase: { kind: "function", metadata: { since: "1.53.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + slice_encoded_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_ascii_lowercase: { kind: "function", metadata: { since: "1.53.0" } }, + to_ascii_uppercase: { kind: "function", metadata: { since: "1.53.0" } }, + to_os_string: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.0.0" } }, + to_string_lossy: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + OsString: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_os_str: { kind: "function", metadata: { since: "1.0.0" } }, + capacity: { kind: "function", metadata: { since: "1.9.0" } }, + clear: { kind: "function", metadata: { since: "1.9.0" } }, + from_encoded_bytes_unchecked: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + into_boxed_os_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_encoded_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + into_string: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.89.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.9.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.9.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.19.0" } }, + truncate: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.9.0" } }, + }, + }, + VaArgSafe: { kind: "trait" }, + VaList: { + kind: "struct", + members: { + arg: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_ffi_c_str = { + Bytes: { kind: "struct", metadata: { since: "unstable" } }, + CStr: { + kind: "struct", + members: { + as_c_str: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.0.0" } }, + bytes: { kind: "function", metadata: { since: "unstable" } }, + count_bytes: { kind: "function", metadata: { since: "1.79.0" } }, + display: { kind: "function", metadata: { since: "unstable" } }, + from_bytes_until_nul: { kind: "function", associated: true, metadata: { since: "1.69.0" } }, + from_bytes_with_nul: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_bytes_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + is_empty: { kind: "function", metadata: { since: "1.71.0" } }, + to_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + to_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + CString: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_bytes_with_nul: { kind: "function", metadata: { since: "1.0.0" } }, + as_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_vec_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_vec_with_nul: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + from_vec_with_nul_unchecked: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + into_boxed_c_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.7.0" } }, + into_bytes_with_nul: { kind: "function", metadata: { since: "1.7.0" } }, + into_raw: { kind: "function", metadata: { since: "1.4.0" } }, + into_string: { kind: "function", metadata: { since: "1.7.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + FromBytesUntilNulError: { kind: "struct" }, + FromBytesWithNulError: { kind: "enum" }, + FromVecWithNulError: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, + IntoStringError: { + kind: "struct", + members: { + into_cstring: { kind: "function", metadata: { since: "1.7.0" } }, + utf8_error: { kind: "function", metadata: { since: "1.7.0" } }, + }, + }, + NulError: { + kind: "struct", + members: { + into_vec: { kind: "function", metadata: { since: "1.0.0" } }, + nul_position: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_ffi_os_str = { + Display: { kind: "struct", metadata: { since: "1.87.0" } }, + OsStr: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_encoded_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + as_os_str: { kind: "function", metadata: { since: "unstable" } }, + display: { kind: "function", metadata: { since: "1.87.0" } }, + eq_ignore_ascii_case: { kind: "function", metadata: { since: "1.53.0" } }, + from_encoded_bytes_unchecked: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + into_os_string: { kind: "function", metadata: { since: "1.20.0" } }, + is_ascii: { kind: "function", metadata: { since: "1.53.0" } }, + is_empty: { kind: "function", metadata: { since: "1.9.0" } }, + len: { kind: "function", metadata: { since: "1.9.0" } }, + make_ascii_lowercase: { kind: "function", metadata: { since: "1.53.0" } }, + make_ascii_uppercase: { kind: "function", metadata: { since: "1.53.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + slice_encoded_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_ascii_lowercase: { kind: "function", metadata: { since: "1.53.0" } }, + to_ascii_uppercase: { kind: "function", metadata: { since: "1.53.0" } }, + to_os_string: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.0.0" } }, + to_string_lossy: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + OsString: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_os_str: { kind: "function", metadata: { since: "1.0.0" } }, + capacity: { kind: "function", metadata: { since: "1.9.0" } }, + clear: { kind: "function", metadata: { since: "1.9.0" } }, + from_encoded_bytes_unchecked: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + into_boxed_os_str: { kind: "function", metadata: { since: "1.20.0" } }, + into_encoded_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + into_string: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.89.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.9.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.9.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.19.0" } }, + truncate: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.9.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_ffi_va_list = { + VaArgSafe: { kind: "trait" }, + VaList: { + kind: "struct", + members: { + arg: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/fmt.ts b/packages/rust/src/builtins/std/fmt.ts new file mode 100644 index 000000000..ca24759cd --- /dev/null +++ b/packages/rust/src/builtins/std/fmt.ts @@ -0,0 +1,142 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_fmt = { + Alignment: { kind: "enum" }, + Arguments: { + kind: "struct", + members: { + as_str: { kind: "function", metadata: { since: "1.52.0" } }, + from_str: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Binary: { kind: "trait" }, + Debug: { kind: "trait" }, + DebugAsHex: { kind: "enum" }, + DebugList: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + entry_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + DebugMap: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + key: { kind: "function", metadata: { since: "1.42.0" } }, + key_with: { kind: "function", metadata: { since: "unstable" } }, + value: { kind: "function", metadata: { since: "1.42.0" } }, + value_with: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + DebugSet: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + entries: { kind: "function", metadata: { since: "1.2.0" } }, + entry: { kind: "function", metadata: { since: "1.2.0" } }, + entry_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + DebugStruct: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + field: { kind: "function", metadata: { since: "1.2.0" } }, + field_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.53.0" } }, + }, + }, + DebugTuple: { + kind: "struct", metadata: { since: "1.2.0" }, + members: { + field: { kind: "function", metadata: { since: "1.2.0" } }, + field_with: { kind: "function", metadata: { since: "unstable" } }, + finish: { kind: "function", metadata: { since: "1.2.0" } }, + finish_non_exhaustive: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + Display: { kind: "trait" }, + Error: { kind: "struct" }, + format: { kind: "function", metadata: { since: "1.0.0" } }, + Formatter: { + kind: "struct", + members: { + align: { kind: "function", metadata: { since: "1.28.0" } }, + alternate: { kind: "function", metadata: { since: "1.5.0" } }, + debug_list: { kind: "function", metadata: { since: "1.2.0" } }, + debug_map: { kind: "function", metadata: { since: "1.2.0" } }, + debug_set: { kind: "function", metadata: { since: "1.2.0" } }, + debug_struct: { kind: "function", metadata: { since: "1.2.0" } }, + debug_tuple: { kind: "function", metadata: { since: "1.2.0" } }, + fill: { kind: "function", metadata: { since: "1.5.0" } }, + flags: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + options: { kind: "function", metadata: { since: "unstable" } }, + pad: { kind: "function", metadata: { since: "1.0.0" } }, + pad_integral: { kind: "function", metadata: { since: "1.0.0" } }, + precision: { kind: "function", metadata: { since: "1.5.0" } }, + sign: { kind: "function", metadata: { since: "unstable" } }, + sign_aware_zero_pad: { kind: "function", metadata: { since: "1.5.0" } }, + sign_minus: { kind: "function", metadata: { since: "1.5.0" } }, + sign_plus: { kind: "function", metadata: { since: "1.5.0" } }, + width: { kind: "function", metadata: { since: "1.5.0" } }, + with_options: { kind: "function", metadata: { since: "unstable" } }, + write_fmt: { kind: "function", metadata: { since: "1.0.0" } }, + write_str: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + FormattingOptions: { + kind: "struct", + members: { + align: { kind: "function", metadata: { since: "unstable" } }, + alternate: { kind: "function", metadata: { since: "unstable" } }, + create_formatter: { kind: "function", metadata: { since: "unstable" } }, + debug_as_hex: { kind: "function", metadata: { since: "unstable" } }, + fill: { kind: "function", metadata: { since: "unstable" } }, + get_align: { kind: "function", metadata: { since: "unstable" } }, + get_alternate: { kind: "function", metadata: { since: "unstable" } }, + get_debug_as_hex: { kind: "function", metadata: { since: "unstable" } }, + get_fill: { kind: "function", metadata: { since: "unstable" } }, + get_precision: { kind: "function", metadata: { since: "unstable" } }, + get_sign: { kind: "function", metadata: { since: "unstable" } }, + get_sign_aware_zero_pad: { kind: "function", metadata: { since: "unstable" } }, + get_width: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + precision: { kind: "function", metadata: { since: "unstable" } }, + sign: { kind: "function", metadata: { since: "unstable" } }, + sign_aware_zero_pad: { kind: "function", metadata: { since: "unstable" } }, + width: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + from_fn: { kind: "function", metadata: { since: "1.93.0" } }, + FromFn: { kind: "struct", metadata: { since: "1.93.0" } }, + LowerExp: { kind: "trait" }, + LowerHex: { kind: "trait" }, + NumBuffer: { + kind: "struct", metadata: { since: "unstable" }, + members: { + capacity: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + NumBufferTrait: { kind: "trait", metadata: { since: "unstable" } }, + Octal: { kind: "trait" }, + Pointer: { kind: "trait" }, + Result: { kind: "type-alias" }, + Sign: { kind: "enum" }, + UpperExp: { kind: "trait" }, + UpperHex: { kind: "trait" }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + Write: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/fs.ts b/packages/rust/src/builtins/std/fs.ts new file mode 100644 index 000000000..7c4233693 --- /dev/null +++ b/packages/rust/src/builtins/std/fs.ts @@ -0,0 +1,129 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_fs = { + canonicalize: { kind: "function", metadata: { since: "1.5.0" } }, + copy: { kind: "function", metadata: { since: "1.0.0" } }, + create_dir: { kind: "function", metadata: { since: "1.0.0" } }, + create_dir_all: { kind: "function", metadata: { since: "1.0.0" } }, + Dir: { + kind: "struct", metadata: { since: "unstable" }, + members: { + open: { kind: "function", associated: true, metadata: { since: "unstable" } }, + open_file: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + DirBuilder: { + kind: "struct", metadata: { since: "1.6.0" }, + members: { + create: { kind: "function", metadata: { since: "1.6.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.6.0" } }, + recursive: { kind: "function", metadata: { since: "1.6.0" } }, + }, + }, + DirEntry: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + file_name: { kind: "function", metadata: { since: "1.1.0" } }, + file_type: { kind: "function", metadata: { since: "1.1.0" } }, + metadata: { kind: "function", metadata: { since: "1.1.0" } }, + path: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + exists: { kind: "function", metadata: { since: "1.81.0" } }, + File: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + create: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + create_buffered: { kind: "function", associated: true, metadata: { since: "unstable" } }, + create_new: { kind: "function", associated: true, metadata: { since: "1.77.0" } }, + lock: { kind: "function", metadata: { since: "1.89.0" } }, + lock_shared: { kind: "function", metadata: { since: "1.89.0" } }, + metadata: { kind: "function", metadata: { since: "1.0.0" } }, + open: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + open_buffered: { kind: "function", associated: true, metadata: { since: "unstable" } }, + options: { kind: "function", associated: true, metadata: { since: "1.58.0" } }, + set_len: { kind: "function", metadata: { since: "1.0.0" } }, + set_modified: { kind: "function", metadata: { since: "1.75.0" } }, + set_permissions: { kind: "function", metadata: { since: "1.16.0" } }, + set_times: { kind: "function", metadata: { since: "1.75.0" } }, + sync_all: { kind: "function", metadata: { since: "1.0.0" } }, + sync_data: { kind: "function", metadata: { since: "1.0.0" } }, + try_clone: { kind: "function", metadata: { since: "1.9.0" } }, + try_lock: { kind: "function", metadata: { since: "1.89.0" } }, + try_lock_shared: { kind: "function", metadata: { since: "1.89.0" } }, + unlock: { kind: "function", metadata: { since: "1.89.0" } }, + }, + }, + FileTimes: { + kind: "struct", metadata: { since: "1.75.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.75.0" } }, + set_accessed: { kind: "function", metadata: { since: "1.75.0" } }, + set_modified: { kind: "function", metadata: { since: "1.75.0" } }, + }, + }, + FileType: { + kind: "struct", metadata: { since: "1.1.0" }, + members: { + is_dir: { kind: "function", metadata: { since: "1.1.0" } }, + is_file: { kind: "function", metadata: { since: "1.1.0" } }, + is_symlink: { kind: "function", metadata: { since: "1.1.0" } }, + }, + }, + hard_link: { kind: "function", metadata: { since: "1.0.0" } }, + metadata: { kind: "function", metadata: { since: "1.0.0" } }, + Metadata: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + accessed: { kind: "function", metadata: { since: "1.10.0" } }, + created: { kind: "function", metadata: { since: "1.10.0" } }, + file_type: { kind: "function", metadata: { since: "1.1.0" } }, + is_dir: { kind: "function", metadata: { since: "1.0.0" } }, + is_file: { kind: "function", metadata: { since: "1.0.0" } }, + is_symlink: { kind: "function", metadata: { since: "1.58.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + modified: { kind: "function", metadata: { since: "1.10.0" } }, + permissions: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + OpenOptions: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + append: { kind: "function", metadata: { since: "1.0.0" } }, + create: { kind: "function", metadata: { since: "1.0.0" } }, + create_new: { kind: "function", metadata: { since: "1.9.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + open: { kind: "function", metadata: { since: "1.0.0" } }, + read: { kind: "function", metadata: { since: "1.0.0" } }, + truncate: { kind: "function", metadata: { since: "1.0.0" } }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + Permissions: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + readonly: { kind: "function", metadata: { since: "1.0.0" } }, + set_readonly: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + read: { kind: "function", metadata: { since: "1.26.0" } }, + read_dir: { kind: "function", metadata: { since: "1.0.0" } }, + read_link: { kind: "function", metadata: { since: "1.0.0" } }, + read_to_string: { kind: "function", metadata: { since: "1.26.0" } }, + ReadDir: { kind: "struct", metadata: { since: "1.0.0" } }, + remove_dir: { kind: "function", metadata: { since: "1.0.0" } }, + remove_dir_all: { kind: "function", metadata: { since: "1.0.0" } }, + remove_file: { kind: "function", metadata: { since: "1.0.0" } }, + rename: { kind: "function", metadata: { since: "1.0.0" } }, + set_permissions: { kind: "function", metadata: { since: "1.1.0" } }, + set_permissions_nofollow: { kind: "function", metadata: { since: "unstable" } }, + set_times: { kind: "function", metadata: { since: "unstable" } }, + set_times_nofollow: { kind: "function", metadata: { since: "unstable" } }, + soft_link: { kind: "function", metadata: { since: "1.0.0" } }, + symlink_metadata: { kind: "function", metadata: { since: "1.1.0" } }, + TryLockError: { kind: "enum", metadata: { since: "1.89.0" } }, + write: { kind: "function", metadata: { since: "1.26.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/future.ts b/packages/rust/src/builtins/std/future.ts new file mode 100644 index 000000000..f92f90ffa --- /dev/null +++ b/packages/rust/src/builtins/std/future.ts @@ -0,0 +1,24 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_future = { + async_drop_in_place: { kind: "function", metadata: { since: "unstable" } }, + AsyncDrop: { kind: "trait", metadata: { since: "unstable" } }, + Future: { kind: "trait", metadata: { since: "1.36.0" } }, + IntoFuture: { kind: "trait", metadata: { since: "1.64.0" } }, + join: { kind: "symbol", metadata: { since: "unstable" } }, + pending: { kind: "function", metadata: { since: "1.48.0" } }, + Pending: { kind: "struct", metadata: { since: "1.48.0" } }, + poll_fn: { kind: "function", metadata: { since: "1.64.0" } }, + PollFn: { kind: "struct", metadata: { since: "1.64.0" } }, + ready: { kind: "function", metadata: { since: "1.48.0" } }, + Ready: { + kind: "struct", metadata: { since: "1.48.0" }, + members: { + into_inner: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, + ResumeTy: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/hash.ts b/packages/rust/src/builtins/std/hash.ts new file mode 100644 index 000000000..91364b485 --- /dev/null +++ b/packages/rust/src/builtins/std/hash.ts @@ -0,0 +1,35 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_hash = { + BuildHasher: { kind: "trait", metadata: { since: "1.7.0" } }, + BuildHasherDefault: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.85.0" } }, + }, + }, + DefaultHasher: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.13.0" } }, + }, + }, + Hash: { kind: "trait", metadata: { since: "1.0.0" } }, + Hasher: { kind: "trait", metadata: { since: "1.0.0" } }, + RandomState: { + kind: "struct", metadata: { since: "1.7.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + }, + }, + SipHasher: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_with_keys: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/index.ts b/packages/rust/src/builtins/std/index.ts new file mode 100644 index 000000000..7ea6776d1 --- /dev/null +++ b/packages/rust/src/builtins/std/index.ts @@ -0,0 +1,156 @@ +import { type SymbolCreator } from "@alloy-js/core"; +import { + type CrateDescriptor, + type CrateRef, + createCrate, + type ExternalCrate, +} from "../../create-crate.js"; +import { mod_alloc } from "./alloc.js"; +import { mod_any } from "./any.js"; +import { mod_array } from "./array.js"; +import { mod_ascii } from "./ascii.js"; +import { mod_backtrace } from "./backtrace.js"; +import { mod_borrow } from "./borrow.js"; +import { mod_boxed } from "./boxed.js"; +import { mod_cell } from "./cell.js"; +import { mod_char_ } from "./char.js"; +import { mod_clone } from "./clone.js"; +import { mod_cmp } from "./cmp.js"; +import { mod_collections, mod_collections_binary_heap, mod_collections_hash_map, mod_collections_linked_list, mod_collections_vec_deque } from "./collections.js"; +import { mod_contracts } from "./contracts.js"; +import { mod_convert } from "./convert.js"; +import { mod_default_ } from "./default.js"; +import { mod_env, mod_env_consts } from "./env.js"; +import { mod_error } from "./error.js"; +import { mod_f32, mod_f32_consts, mod_f32_math } from "./f32.js"; +import { mod_f64, mod_f64_consts, mod_f64_math } from "./f64.js"; +import { mod_ffi, mod_ffi_c_str, mod_ffi_os_str, mod_ffi_va_list } from "./ffi.js"; +import { mod_fmt } from "./fmt.js"; +import { mod_fs } from "./fs.js"; +import { mod_future } from "./future.js"; +import { mod_hash } from "./hash.js"; +import { mod_io, mod_io_prelude } from "./io.js"; +import { mod_iter } from "./iter.js"; +import { mod_marker } from "./marker.js"; +import { mod_mem, mod_mem_type_info } from "./mem.js"; +import { mod_net } from "./net.js"; +import { mod_num } from "./num.js"; +import { mod_ops } from "./ops.js"; +import { mod_option } from "./option.js"; +import { mod_panic } from "./panic.js"; +import { mod_panicking, mod_panicking_panic_const } from "./panicking.js"; +import { mod_pat } from "./pat.js"; +import { mod_path } from "./path.js"; +import { mod_pin } from "./pin.js"; +import { mod_process } from "./process.js"; +import { mod_profiling } from "./profiling.js"; +import { mod_ptr } from "./ptr.js"; +import { mod_random } from "./random.js"; +import { mod_range, mod_range_legacy } from "./range.js"; +import { mod_rc } from "./rc.js"; +import { mod_result } from "./result.js"; +import { mod_root } from "./root.js"; +import { mod_slice } from "./slice.js"; +import { mod_str, mod_str_pattern } from "./str.js"; +import { mod_string } from "./string.js"; +import { mod_sync, mod_sync_atomic, mod_sync_mpmc, mod_sync_mpsc, mod_sync_nonpoison, mod_sync_oneshot, mod_sync_poison } from "./sync.js"; +import { mod_task } from "./task.js"; +import { mod_thread } from "./thread.js"; +import { mod_time } from "./time.js"; +import { mod_ub_checks } from "./ub_checks.js"; +import { mod_vec } from "./vec.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +const stdDescriptor = { + name: "std", + builtin: true, + modules: { + "alloc": mod_alloc, + "any": mod_any, + "array": mod_array, + "ascii": mod_ascii, + "backtrace": mod_backtrace, + "borrow": mod_borrow, + "boxed": mod_boxed, + "cell": mod_cell, + "char": mod_char_, + "clone": mod_clone, + "cmp": mod_cmp, + "collections": mod_collections, + "collections::binary_heap": mod_collections_binary_heap, + "collections::hash_map": mod_collections_hash_map, + "collections::linked_list": mod_collections_linked_list, + "collections::vec_deque": mod_collections_vec_deque, + "contracts": mod_contracts, + "convert": mod_convert, + "default": mod_default_, + "env": mod_env, + "env::consts": mod_env_consts, + "error": mod_error, + "f32": mod_f32, + "f32::consts": mod_f32_consts, + "f32::math": mod_f32_math, + "f64": mod_f64, + "f64::consts": mod_f64_consts, + "f64::math": mod_f64_math, + "ffi": mod_ffi, + "ffi::c_str": mod_ffi_c_str, + "ffi::os_str": mod_ffi_os_str, + "ffi::va_list": mod_ffi_va_list, + "fmt": mod_fmt, + "fs": mod_fs, + "future": mod_future, + "hash": mod_hash, + "io": mod_io, + "io::prelude": mod_io_prelude, + "iter": mod_iter, + "marker": mod_marker, + "mem": mod_mem, + "mem::type_info": mod_mem_type_info, + "net": mod_net, + "num": mod_num, + "ops": mod_ops, + "option": mod_option, + "panic": mod_panic, + "panicking": mod_panicking, + "panicking::panic_const": mod_panicking_panic_const, + "pat": mod_pat, + "path": mod_path, + "pin": mod_pin, + "process": mod_process, + "profiling": mod_profiling, + "ptr": mod_ptr, + "random": mod_random, + "range": mod_range, + "range::legacy": mod_range_legacy, + "rc": mod_rc, + "result": mod_result, + "": mod_root, + "slice": mod_slice, + "str": mod_str, + "str::pattern": mod_str_pattern, + "string": mod_string, + "sync": mod_sync, + "sync::atomic": mod_sync_atomic, + "sync::mpmc": mod_sync_mpmc, + "sync::mpsc": mod_sync_mpsc, + "sync::nonpoison": mod_sync_nonpoison, + "sync::oneshot": mod_sync_oneshot, + "sync::poison": mod_sync_poison, + "task": mod_task, + "thread": mod_thread, + "time": mod_time, + "ub_checks": mod_ub_checks, + "vec": mod_vec, + }, +} as const satisfies CrateDescriptor; + +/** + * The `std` crate descriptor. + */ +export type StdCrate = CrateRef & + SymbolCreator & + ExternalCrate; +export const std: StdCrate = createCrate(stdDescriptor); diff --git a/packages/rust/src/builtins/std/io.ts b/packages/rust/src/builtins/std/io.ts new file mode 100644 index 000000000..0babbeefc --- /dev/null +++ b/packages/rust/src/builtins/std/io.ts @@ -0,0 +1,222 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_io = { + BorrowedBuf: { + kind: "struct", + members: { + capacity: { kind: "function" }, + clear: { kind: "function" }, + filled: { kind: "function" }, + filled_mut: { kind: "function" }, + init_len: { kind: "function" }, + into_filled: { kind: "function" }, + into_filled_mut: { kind: "function" }, + len: { kind: "function" }, + set_init: { kind: "function" }, + unfilled: { kind: "function" }, + }, + }, + BorrowedCursor: { + kind: "struct", + members: { + advance: { kind: "function" }, + advance_unchecked: { kind: "function" }, + append: { kind: "function" }, + as_mut: { kind: "function" }, + capacity: { kind: "function" }, + ensure_init: { kind: "function" }, + init_mut: { kind: "function" }, + reborrow: { kind: "function" }, + set_init: { kind: "function" }, + with_unfilled_buf: { kind: "function" }, + written: { kind: "function" }, + }, + }, + BufRead: { kind: "trait", metadata: { since: "1.0.0" } }, + BufReader: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + buffer: { kind: "function", metadata: { since: "1.37.0" } }, + capacity: { kind: "function", metadata: { since: "1.46.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_ref: { kind: "function", metadata: { since: "1.0.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + peek: { kind: "function", metadata: { since: "unstable" } }, + seek_relative: { kind: "function", metadata: { since: "1.53.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + BufWriter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + buffer: { kind: "function", metadata: { since: "1.37.0" } }, + capacity: { kind: "function", metadata: { since: "1.46.0" } }, + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_ref: { kind: "function", metadata: { since: "1.0.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + into_parts: { kind: "function", metadata: { since: "1.56.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + Bytes: { kind: "struct", metadata: { since: "1.0.0" } }, + Chain: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.20.0" } }, + get_ref: { kind: "function", metadata: { since: "1.20.0" } }, + into_inner: { kind: "function", metadata: { since: "1.20.0" } }, + }, + }, + const_error: { kind: "symbol", metadata: { since: "unstable" } }, + copy: { kind: "function", metadata: { since: "1.0.0" } }, + Cursor: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_ref: { kind: "function", metadata: { since: "1.0.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + position: { kind: "function", metadata: { since: "1.0.0" } }, + set_position: { kind: "function", metadata: { since: "1.0.0" } }, + split: { kind: "function", metadata: { since: "unstable" } }, + split_mut: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + empty: { kind: "function", metadata: { since: "1.0.0" } }, + Empty: { kind: "struct", metadata: { since: "1.0.0" } }, + Error: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + downcast: { kind: "function", metadata: { since: "1.79.0" } }, + from_raw_os_error: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + get_mut: { kind: "function", metadata: { since: "1.3.0" } }, + get_ref: { kind: "function", metadata: { since: "1.3.0" } }, + into_inner: { kind: "function", metadata: { since: "1.3.0" } }, + kind: { kind: "function", metadata: { since: "1.0.0" } }, + last_os_error: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + other: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + raw_os_error: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ErrorKind: { kind: "enum", metadata: { since: "1.0.0" } }, + IntoInnerError: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + error: { kind: "function", metadata: { since: "1.0.0" } }, + into_error: { kind: "function", metadata: { since: "1.55.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + into_parts: { kind: "function", metadata: { since: "1.55.0" } }, + }, + }, + IoSlice: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + advance: { kind: "function", metadata: { since: "1.81.0" } }, + advance_slices: { kind: "function", associated: true, metadata: { since: "1.81.0" } }, + as_slice: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + IoSliceMut: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + advance: { kind: "function", metadata: { since: "1.81.0" } }, + advance_slices: { kind: "function", associated: true, metadata: { since: "1.81.0" } }, + into_slice: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + IsTerminal: { kind: "trait", metadata: { since: "1.70.0" } }, + Lines: { kind: "struct", metadata: { since: "1.0.0" } }, + LineWriter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.0.0" } }, + get_ref: { kind: "function", metadata: { since: "1.0.0" } }, + into_inner: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + pipe: { kind: "function", metadata: { since: "1.87.0" } }, + PipeReader: { + kind: "struct", metadata: { since: "1.87.0" }, + members: { + try_clone: { kind: "function", metadata: { since: "1.87.0" } }, + }, + }, + PipeWriter: { + kind: "struct", metadata: { since: "1.87.0" }, + members: { + try_clone: { kind: "function", metadata: { since: "1.87.0" } }, + }, + }, + RawOsError: { kind: "type-alias", metadata: { since: "unstable" } }, + Read: { kind: "trait", metadata: { since: "1.0.0" } }, + read_to_string: { kind: "function", metadata: { since: "1.65.0" } }, + repeat: { kind: "function", metadata: { since: "1.0.0" } }, + Repeat: { kind: "struct", metadata: { since: "1.0.0" } }, + Result: { kind: "type-alias", metadata: { since: "1.0.0" } }, + Seek: { kind: "trait", metadata: { since: "1.0.0" } }, + SeekFrom: { kind: "enum", metadata: { since: "1.0.0" } }, + sink: { kind: "function", metadata: { since: "1.0.0" } }, + Sink: { kind: "struct", metadata: { since: "1.0.0" } }, + Split: { kind: "struct", metadata: { since: "1.0.0" } }, + stderr: { kind: "function", metadata: { since: "1.0.0" } }, + Stderr: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + lock: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + StderrLock: { kind: "struct", metadata: { since: "1.0.0" } }, + stdin: { kind: "function", metadata: { since: "1.0.0" } }, + Stdin: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + lines: { kind: "function", metadata: { since: "1.62.0" } }, + lock: { kind: "function", metadata: { since: "1.0.0" } }, + read_line: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + StdinLock: { kind: "struct", metadata: { since: "1.0.0" } }, + stdout: { kind: "function", metadata: { since: "1.0.0" } }, + Stdout: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + lock: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + StdoutLock: { kind: "struct", metadata: { since: "1.0.0" } }, + Take: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.20.0" } }, + get_ref: { kind: "function", metadata: { since: "1.20.0" } }, + into_inner: { kind: "function", metadata: { since: "1.15.0" } }, + limit: { kind: "function", metadata: { since: "1.0.0" } }, + position: { kind: "function", metadata: { since: "unstable" } }, + set_limit: { kind: "function", metadata: { since: "1.27.0" } }, + }, + }, + Write: { kind: "trait", metadata: { since: "1.0.0" } }, + WriterPanicked: { + kind: "struct", metadata: { since: "1.56.0" }, + members: { + into_inner: { kind: "function", metadata: { since: "1.56.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_io_prelude = { + BufRead: { kind: "trait", metadata: { since: "1.0.0" } }, + Read: { kind: "trait", metadata: { since: "1.0.0" } }, + Seek: { kind: "trait", metadata: { since: "1.0.0" } }, + Write: { kind: "trait", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/iter.ts b/packages/rust/src/builtins/std/iter.ts new file mode 100644 index 000000000..ba269e57d --- /dev/null +++ b/packages/rust/src/builtins/std/iter.ts @@ -0,0 +1,92 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_iter = { + ArrayChunks: { + kind: "struct", metadata: { since: "unstable" }, + members: { + into_remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ByRefSized: { kind: "struct", metadata: { since: "unstable" } }, + chain: { kind: "function", metadata: { since: "1.91.0" } }, + Chain: { kind: "struct", metadata: { since: "1.0.0" } }, + Cloned: { kind: "struct", metadata: { since: "1.1.0" } }, + Copied: { kind: "struct", metadata: { since: "1.36.0" } }, + Cycle: { kind: "struct", metadata: { since: "1.0.0" } }, + DoubleEndedIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + empty: { kind: "function", metadata: { since: "1.2.0" } }, + Empty: { kind: "struct", metadata: { since: "1.2.0" } }, + Enumerate: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + next_index: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExactSizeIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Extend: { kind: "trait", metadata: { since: "1.0.0" } }, + Filter: { kind: "struct", metadata: { since: "1.0.0" } }, + FilterMap: { kind: "struct", metadata: { since: "1.0.0" } }, + FlatMap: { kind: "struct", metadata: { since: "1.0.0" } }, + Flatten: { kind: "struct", metadata: { since: "1.29.0" } }, + from_coroutine: { kind: "function", metadata: { since: "unstable" } }, + from_fn: { kind: "function", metadata: { since: "1.34.0" } }, + FromCoroutine: { kind: "struct", metadata: { since: "unstable" } }, + FromFn: { kind: "struct", metadata: { since: "1.34.0" } }, + FromIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Fuse: { kind: "struct", metadata: { since: "1.0.0" } }, + FusedIterator: { kind: "trait", metadata: { since: "1.26.0" } }, + Inspect: { kind: "struct", metadata: { since: "1.0.0" } }, + Intersperse: { kind: "struct", metadata: { since: "unstable" } }, + IntersperseWith: { kind: "struct", metadata: { since: "unstable" } }, + IntoIterator: { kind: "trait", metadata: { since: "1.0.0" } }, + iter: { kind: "symbol", metadata: { since: "unstable" } }, + Iterator: { kind: "trait", metadata: { since: "1.0.0" } }, + Map: { kind: "struct", metadata: { since: "1.0.0" } }, + MapWhile: { kind: "struct", metadata: { since: "1.57.0" } }, + MapWindows: { kind: "struct", metadata: { since: "unstable" } }, + once: { kind: "function", metadata: { since: "1.2.0" } }, + Once: { kind: "struct", metadata: { since: "1.2.0" } }, + once_with: { kind: "function", metadata: { since: "1.43.0" } }, + OnceWith: { kind: "struct", metadata: { since: "1.43.0" } }, + Peekable: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + next_if: { kind: "function", metadata: { since: "1.51.0" } }, + next_if_eq: { kind: "function", metadata: { since: "1.51.0" } }, + next_if_map: { kind: "function", metadata: { since: "1.94.0" } }, + next_if_map_mut: { kind: "function", metadata: { since: "1.94.0" } }, + peek: { kind: "function", metadata: { since: "1.0.0" } }, + peek_mut: { kind: "function", metadata: { since: "1.53.0" } }, + }, + }, + Product: { kind: "trait", metadata: { since: "1.12.0" } }, + repeat: { kind: "function", metadata: { since: "1.0.0" } }, + Repeat: { kind: "struct", metadata: { since: "1.0.0" } }, + repeat_n: { kind: "function", metadata: { since: "1.82.0" } }, + repeat_with: { kind: "function", metadata: { since: "1.28.0" } }, + RepeatN: { kind: "struct", metadata: { since: "1.82.0" } }, + RepeatWith: { kind: "struct", metadata: { since: "1.28.0" } }, + Rev: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + into_inner: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Scan: { kind: "struct", metadata: { since: "1.0.0" } }, + Skip: { kind: "struct", metadata: { since: "1.0.0" } }, + SkipWhile: { kind: "struct", metadata: { since: "1.0.0" } }, + Step: { kind: "trait", metadata: { since: "unstable" } }, + StepBy: { kind: "struct", metadata: { since: "1.28.0" } }, + successors: { kind: "function", metadata: { since: "1.34.0" } }, + Successors: { kind: "struct", metadata: { since: "1.34.0" } }, + Sum: { kind: "trait", metadata: { since: "1.12.0" } }, + Take: { kind: "struct", metadata: { since: "1.0.0" } }, + TakeWhile: { kind: "struct", metadata: { since: "1.0.0" } }, + TrustedLen: { kind: "trait", metadata: { since: "unstable" } }, + TrustedStep: { kind: "trait", metadata: { since: "unstable" } }, + zip: { kind: "function", metadata: { since: "1.59.0" } }, + Zip: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/marker.ts b/packages/rust/src/builtins/std/marker.ts new file mode 100644 index 000000000..b9f5f1c99 --- /dev/null +++ b/packages/rust/src/builtins/std/marker.ts @@ -0,0 +1,63 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_marker = { + ConstParamTy_: { kind: "trait" }, + Copy: { kind: "trait" }, + Destruct: { kind: "trait", metadata: { since: "unstable" } }, + DiscriminantKind: { kind: "trait", metadata: { since: "unstable" } }, + FnPtr: { kind: "trait" }, + Freeze: { kind: "trait" }, + MetaSized: { kind: "trait" }, + PhantomContravariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomContravariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomCovariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomCovariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomData: { kind: "struct" }, + PhantomInvariant: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomInvariantLifetime: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + }, + }, + PhantomPinned: { kind: "struct" }, + PointeeSized: { kind: "trait", metadata: { since: "unstable" } }, + Send: { kind: "trait" }, + Sized: { kind: "trait" }, + StructuralPartialEq: { kind: "trait" }, + Sync: { kind: "trait" }, + Tuple: { kind: "trait" }, + Unpin: { kind: "trait" }, + UnsafeUnpin: { kind: "trait" }, + Unsize: { kind: "trait" }, + variance: { kind: "function" }, + Variance: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/mem.ts b/packages/rust/src/builtins/std/mem.ts new file mode 100644 index 000000000..85d303f4a --- /dev/null +++ b/packages/rust/src/builtins/std/mem.ts @@ -0,0 +1,120 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_mem = { + align_of: { kind: "function" }, + align_of_val: { kind: "function" }, + align_of_val_raw: { kind: "function", metadata: { since: "unstable" } }, + Alignment: { + kind: "struct", metadata: { since: "unstable" }, + members: { + as_nonzero: { kind: "function", metadata: { since: "unstable" } }, + as_nonzero_usize: { kind: "function", metadata: { since: "unstable" } }, + as_usize: { kind: "function", metadata: { since: "unstable" } }, + log2: { kind: "function", metadata: { since: "unstable" } }, + mask: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of_val: { kind: "function", associated: true, metadata: { since: "unstable" } }, + of_val_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Assume: { + kind: "struct", metadata: { since: "unstable" }, + members: { + and: { kind: "function", metadata: { since: "unstable" } }, + but_not: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + conjure_zst: { kind: "function", metadata: { since: "unstable" } }, + copy: { kind: "function", metadata: { since: "unstable" } }, + discriminant: { kind: "function", metadata: { since: "1.21.0" } }, + Discriminant: { kind: "struct" }, + drop: { kind: "function" }, + DropGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + dismiss: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + forget: { kind: "function" }, + forget_unsized: { kind: "function", metadata: { since: "unstable" } }, + ManuallyDrop: { + kind: "struct", metadata: { since: "1.20.0" }, + members: { + drop: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.20.0" } }, + take: { kind: "function", associated: true, metadata: { since: "1.42.0" } }, + }, + }, + MaybeDangling: { + kind: "struct", + members: { + as_mut: { kind: "function" }, + as_ref: { kind: "function" }, + into_inner: { kind: "function" }, + new: { kind: "function", associated: true }, + }, + }, + min_align_of: { kind: "function", metadata: { since: "1.0.0" } }, + min_align_of_val: { kind: "function", metadata: { since: "1.0.0" } }, + needs_drop: { kind: "function" }, + offset_of: { kind: "symbol", metadata: { since: "1.77.0" } }, + replace: { kind: "function" }, + size_of: { kind: "function" }, + size_of_val: { kind: "function" }, + size_of_val_raw: { kind: "function", metadata: { since: "unstable" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + take: { kind: "function" }, + transmute: { kind: "function", metadata: { since: "1.0.0" } }, + transmute_copy: { kind: "function" }, + TransmuteFrom: { kind: "trait", metadata: { since: "unstable" } }, + uninitialized: { kind: "function", metadata: { since: "1.0.0" } }, + variant_count: { kind: "function", metadata: { since: "unstable" } }, + zeroed: { kind: "function" }, +} as const satisfies Record; + +export const mod_mem_type_info = { + Abi: { kind: "enum", metadata: { since: "unstable" } }, + Array: { kind: "struct", metadata: { since: "unstable" } }, + Bool: { kind: "struct", metadata: { since: "unstable" } }, + Char: { kind: "struct", metadata: { since: "unstable" } }, + Const: { kind: "struct", metadata: { since: "unstable" } }, + DynTrait: { kind: "struct", metadata: { since: "unstable" } }, + DynTraitPredicate: { kind: "struct", metadata: { since: "unstable" } }, + Enum: { kind: "struct", metadata: { since: "unstable" } }, + Field: { kind: "struct", metadata: { since: "unstable" } }, + Float: { kind: "struct", metadata: { since: "unstable" } }, + FnPtr: { kind: "struct", metadata: { since: "unstable" } }, + Generic: { kind: "enum", metadata: { since: "unstable" } }, + GenericType: { kind: "struct", metadata: { since: "unstable" } }, + Int: { kind: "struct", metadata: { since: "unstable" } }, + Lifetime: { kind: "struct", metadata: { since: "unstable" } }, + Pointer: { kind: "struct", metadata: { since: "unstable" } }, + Reference: { kind: "struct", metadata: { since: "unstable" } }, + Slice: { kind: "struct", metadata: { since: "unstable" } }, + Str: { kind: "struct", metadata: { since: "unstable" } }, + Struct: { kind: "struct", metadata: { since: "unstable" } }, + Trait: { kind: "struct", metadata: { since: "unstable" } }, + TraitImpl: { + kind: "struct", metadata: { since: "unstable" }, + members: { + get_vtable: { kind: "function" }, + }, + }, + Tuple: { kind: "struct", metadata: { since: "unstable" } }, + Type: { + kind: "struct", metadata: { since: "unstable" }, + members: { + of: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + TypeKind: { kind: "enum", metadata: { since: "unstable" } }, + Union: { kind: "struct", metadata: { since: "unstable" } }, + Variant: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/net.ts b/packages/rust/src/builtins/std/net.ts new file mode 100644 index 000000000..09d5b43e5 --- /dev/null +++ b/packages/rust/src/builtins/std/net.ts @@ -0,0 +1,200 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_net = { + AddrParseError: { kind: "struct" }, + hostname: { kind: "function", metadata: { since: "unstable" } }, + Incoming: { kind: "struct", metadata: { since: "1.0.0" } }, + IntoIncoming: { kind: "struct", metadata: { since: "unstable" } }, + IpAddr: { + kind: "enum", + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_documentation: { kind: "function", metadata: { since: "unstable" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_ipv4: { kind: "function", metadata: { since: "1.50.0" } }, + is_ipv6: { kind: "function", metadata: { since: "1.50.0" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_unspecified: { kind: "function", metadata: { since: "1.50.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_canonical: { kind: "function", metadata: { since: "1.75.0" } }, + }, + }, + Ipv4Addr: { + kind: "struct", + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + from_bits: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + from_octets: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_broadcast: { kind: "function", metadata: { since: "1.50.0" } }, + is_documentation: { kind: "function", metadata: { since: "1.50.0" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_link_local: { kind: "function", metadata: { since: "1.50.0" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_private: { kind: "function", metadata: { since: "1.50.0" } }, + is_reserved: { kind: "function", metadata: { since: "unstable" } }, + is_shared: { kind: "function", metadata: { since: "unstable" } }, + is_unspecified: { kind: "function", metadata: { since: "1.32.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.32.0" } }, + octets: { kind: "function", metadata: { since: "1.50.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + to_bits: { kind: "function", metadata: { since: "1.80.0" } }, + to_ipv6_compatible: { kind: "function", metadata: { since: "1.50.0" } }, + to_ipv6_mapped: { kind: "function", metadata: { since: "1.50.0" } }, + }, + }, + Ipv6Addr: { + kind: "struct", + members: { + as_octets: { kind: "function", metadata: { since: "unstable" } }, + from_bits: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + from_octets: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_segments: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + is_benchmarking: { kind: "function", metadata: { since: "unstable" } }, + is_documentation: { kind: "function", metadata: { since: "unstable" } }, + is_global: { kind: "function", metadata: { since: "unstable" } }, + is_ipv4_mapped: { kind: "function", metadata: { since: "unstable" } }, + is_loopback: { kind: "function", metadata: { since: "1.50.0" } }, + is_multicast: { kind: "function", metadata: { since: "1.50.0" } }, + is_unicast: { kind: "function", metadata: { since: "unstable" } }, + is_unicast_global: { kind: "function", metadata: { since: "unstable" } }, + is_unicast_link_local: { kind: "function", metadata: { since: "1.84.0" } }, + is_unique_local: { kind: "function", metadata: { since: "1.84.0" } }, + is_unspecified: { kind: "function", metadata: { since: "1.50.0" } }, + multicast_scope: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.32.0" } }, + octets: { kind: "function", metadata: { since: "1.32.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + segments: { kind: "function", metadata: { since: "1.50.0" } }, + to_bits: { kind: "function", metadata: { since: "1.80.0" } }, + to_canonical: { kind: "function", metadata: { since: "1.75.0" } }, + to_ipv4: { kind: "function", metadata: { since: "1.50.0" } }, + to_ipv4_mapped: { kind: "function", metadata: { since: "1.63.0" } }, + }, + }, + Ipv6MulticastScope: { kind: "enum" }, + Shutdown: { kind: "enum", metadata: { since: "1.0.0" } }, + SocketAddr: { + kind: "enum", + members: { + ip: { kind: "function", metadata: { since: "1.7.0" } }, + is_ipv4: { kind: "function", metadata: { since: "1.16.0" } }, + is_ipv6: { kind: "function", metadata: { since: "1.16.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.7.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + SocketAddrV4: { + kind: "struct", + members: { + ip: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + SocketAddrV6: { + kind: "struct", + members: { + flowinfo: { kind: "function", metadata: { since: "1.0.0" } }, + ip: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + parse_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + port: { kind: "function", metadata: { since: "1.0.0" } }, + scope_id: { kind: "function", metadata: { since: "1.0.0" } }, + set_flowinfo: { kind: "function", metadata: { since: "1.9.0" } }, + set_ip: { kind: "function", metadata: { since: "1.9.0" } }, + set_port: { kind: "function", metadata: { since: "1.9.0" } }, + set_scope_id: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + TcpListener: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + accept: { kind: "function", metadata: { since: "1.0.0" } }, + bind: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + incoming: { kind: "function", metadata: { since: "1.0.0" } }, + into_incoming: { kind: "function", metadata: { since: "unstable" } }, + local_addr: { kind: "function", metadata: { since: "1.0.0" } }, + only_v6: { kind: "function", metadata: { since: "1.9.0" } }, + set_nonblocking: { kind: "function", metadata: { since: "1.9.0" } }, + set_only_v6: { kind: "function", metadata: { since: "1.9.0" } }, + set_ttl: { kind: "function", metadata: { since: "1.9.0" } }, + take_error: { kind: "function", metadata: { since: "1.9.0" } }, + try_clone: { kind: "function", metadata: { since: "1.0.0" } }, + ttl: { kind: "function", metadata: { since: "1.9.0" } }, + }, + }, + TcpStream: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + connect: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + connect_timeout: { kind: "function", associated: true, metadata: { since: "1.21.0" } }, + linger: { kind: "function", metadata: { since: "unstable" } }, + local_addr: { kind: "function", metadata: { since: "1.0.0" } }, + nodelay: { kind: "function", metadata: { since: "1.9.0" } }, + peek: { kind: "function", metadata: { since: "1.18.0" } }, + peer_addr: { kind: "function", metadata: { since: "1.0.0" } }, + read_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + set_linger: { kind: "function", metadata: { since: "unstable" } }, + set_nodelay: { kind: "function", metadata: { since: "1.9.0" } }, + set_nonblocking: { kind: "function", metadata: { since: "1.9.0" } }, + set_read_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + set_ttl: { kind: "function", metadata: { since: "1.9.0" } }, + set_write_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + shutdown: { kind: "function", metadata: { since: "1.0.0" } }, + take_error: { kind: "function", metadata: { since: "1.9.0" } }, + try_clone: { kind: "function", metadata: { since: "1.0.0" } }, + ttl: { kind: "function", metadata: { since: "1.9.0" } }, + write_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + ToSocketAddrs: { kind: "trait", metadata: { since: "1.0.0" } }, + UdpSocket: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + bind: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + broadcast: { kind: "function", metadata: { since: "1.9.0" } }, + connect: { kind: "function", metadata: { since: "1.9.0" } }, + join_multicast_v4: { kind: "function", metadata: { since: "1.9.0" } }, + join_multicast_v6: { kind: "function", metadata: { since: "1.9.0" } }, + leave_multicast_v4: { kind: "function", metadata: { since: "1.9.0" } }, + leave_multicast_v6: { kind: "function", metadata: { since: "1.9.0" } }, + local_addr: { kind: "function", metadata: { since: "1.0.0" } }, + multicast_loop_v4: { kind: "function", metadata: { since: "1.9.0" } }, + multicast_loop_v6: { kind: "function", metadata: { since: "1.9.0" } }, + multicast_ttl_v4: { kind: "function", metadata: { since: "1.9.0" } }, + peek: { kind: "function", metadata: { since: "1.18.0" } }, + peek_from: { kind: "function", metadata: { since: "1.18.0" } }, + peer_addr: { kind: "function", metadata: { since: "1.40.0" } }, + read_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + recv: { kind: "function", metadata: { since: "1.9.0" } }, + recv_from: { kind: "function", metadata: { since: "1.0.0" } }, + send: { kind: "function", metadata: { since: "1.9.0" } }, + send_to: { kind: "function", metadata: { since: "1.0.0" } }, + set_broadcast: { kind: "function", metadata: { since: "1.9.0" } }, + set_multicast_loop_v4: { kind: "function", metadata: { since: "1.9.0" } }, + set_multicast_loop_v6: { kind: "function", metadata: { since: "1.9.0" } }, + set_multicast_ttl_v4: { kind: "function", metadata: { since: "1.9.0" } }, + set_nonblocking: { kind: "function", metadata: { since: "1.9.0" } }, + set_read_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + set_ttl: { kind: "function", metadata: { since: "1.9.0" } }, + set_write_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + take_error: { kind: "function", metadata: { since: "1.9.0" } }, + try_clone: { kind: "function", metadata: { since: "1.0.0" } }, + ttl: { kind: "function", metadata: { since: "1.9.0" } }, + write_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/num.ts b/packages/rust/src/builtins/std/num.ts new file mode 100644 index 000000000..a6f00b74f --- /dev/null +++ b/packages/rust/src/builtins/std/num.ts @@ -0,0 +1,135 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_num = { + FpCategory: { kind: "enum" }, + IntErrorKind: { kind: "enum" }, + NonZero: { + kind: "struct", + members: { + abs: { kind: "function", metadata: { since: "1.64.0" } }, + bit_width: { kind: "function", metadata: { since: "unstable" } }, + cast_signed: { kind: "function", metadata: { since: "1.87.0" } }, + cast_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + checked_abs: { kind: "function", metadata: { since: "1.64.0" } }, + checked_add: { kind: "function", metadata: { since: "1.64.0" } }, + checked_mul: { kind: "function", metadata: { since: "1.64.0" } }, + checked_neg: { kind: "function", metadata: { since: "1.71.0" } }, + checked_next_power_of_two: { kind: "function", metadata: { since: "1.64.0" } }, + checked_pow: { kind: "function", metadata: { since: "1.64.0" } }, + count_ones: { kind: "function", metadata: { since: "1.86.0" } }, + div_ceil: { kind: "function", metadata: { since: "1.92.0" } }, + from_ascii: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ascii_radix: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_str_radix: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get: { kind: "function", metadata: { since: "1.28.0" } }, + highest_one: { kind: "function", metadata: { since: "unstable" } }, + ilog10: { kind: "function", metadata: { since: "1.67.0" } }, + ilog2: { kind: "function", metadata: { since: "1.67.0" } }, + is_negative: { kind: "function", metadata: { since: "1.71.0" } }, + is_positive: { kind: "function", metadata: { since: "1.71.0" } }, + is_power_of_two: { kind: "function", metadata: { since: "1.59.0" } }, + isolate_highest_one: { kind: "function", metadata: { since: "unstable" } }, + isolate_lowest_one: { kind: "function", metadata: { since: "unstable" } }, + isqrt: { kind: "function", metadata: { since: "1.84.0" } }, + leading_zeros: { kind: "function", metadata: { since: "1.53.0" } }, + lowest_one: { kind: "function", metadata: { since: "unstable" } }, + midpoint: { kind: "function", metadata: { since: "1.85.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.28.0" } }, + overflowing_abs: { kind: "function", metadata: { since: "1.64.0" } }, + overflowing_neg: { kind: "function", metadata: { since: "1.71.0" } }, + reverse_bits: { kind: "function", metadata: { since: "unstable" } }, + rotate_left: { kind: "function", metadata: { since: "unstable" } }, + rotate_right: { kind: "function", metadata: { since: "unstable" } }, + saturating_abs: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_add: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_mul: { kind: "function", metadata: { since: "1.64.0" } }, + saturating_neg: { kind: "function", metadata: { since: "1.71.0" } }, + saturating_pow: { kind: "function", metadata: { since: "1.64.0" } }, + swap_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_be: { kind: "function", metadata: { since: "unstable" } }, + to_le: { kind: "function", metadata: { since: "unstable" } }, + trailing_zeros: { kind: "function", metadata: { since: "1.53.0" } }, + unchecked_add: { kind: "function", metadata: { since: "unstable" } }, + unchecked_mul: { kind: "function", metadata: { since: "unstable" } }, + unsigned_abs: { kind: "function", metadata: { since: "1.64.0" } }, + wrapping_abs: { kind: "function", metadata: { since: "1.64.0" } }, + wrapping_neg: { kind: "function", metadata: { since: "1.71.0" } }, + }, + }, + NonZeroI128: { kind: "type-alias" }, + NonZeroI16: { kind: "type-alias" }, + NonZeroI32: { kind: "type-alias" }, + NonZeroI64: { kind: "type-alias" }, + NonZeroI8: { kind: "type-alias" }, + NonZeroIsize: { kind: "type-alias" }, + NonZeroU128: { kind: "type-alias" }, + NonZeroU16: { kind: "type-alias" }, + NonZeroU32: { kind: "type-alias" }, + NonZeroU64: { kind: "type-alias" }, + NonZeroU8: { kind: "type-alias" }, + NonZeroUsize: { kind: "type-alias" }, + ParseFloatError: { kind: "struct" }, + ParseIntError: { + kind: "struct", + members: { + kind: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, + Saturating: { + kind: "struct", + members: { + abs: { kind: "function", metadata: { since: "1.74.0" } }, + count_ones: { kind: "function", metadata: { since: "1.74.0" } }, + count_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + from_be: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + from_le: { kind: "function", associated: true, metadata: { since: "1.74.0" } }, + is_negative: { kind: "function", metadata: { since: "1.74.0" } }, + is_positive: { kind: "function", metadata: { since: "1.74.0" } }, + is_power_of_two: { kind: "function", metadata: { since: "1.74.0" } }, + leading_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + pow: { kind: "function", metadata: { since: "1.74.0" } }, + reverse_bits: { kind: "function", metadata: { since: "1.74.0" } }, + rotate_left: { kind: "function", metadata: { since: "1.74.0" } }, + rotate_right: { kind: "function", metadata: { since: "1.74.0" } }, + signum: { kind: "function", metadata: { since: "1.74.0" } }, + swap_bytes: { kind: "function", metadata: { since: "1.74.0" } }, + to_be: { kind: "function", metadata: { since: "1.74.0" } }, + to_le: { kind: "function", metadata: { since: "1.74.0" } }, + trailing_zeros: { kind: "function", metadata: { since: "1.74.0" } }, + }, + }, + TryFromIntError: { kind: "struct" }, + Wrapping: { + kind: "struct", + members: { + abs: { kind: "function", metadata: { since: "unstable" } }, + count_ones: { kind: "function", metadata: { since: "unstable" } }, + count_zeros: { kind: "function", metadata: { since: "unstable" } }, + from_be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_negative: { kind: "function", metadata: { since: "unstable" } }, + is_positive: { kind: "function", metadata: { since: "unstable" } }, + is_power_of_two: { kind: "function", metadata: { since: "unstable" } }, + leading_zeros: { kind: "function", metadata: { since: "unstable" } }, + next_power_of_two: { kind: "function", metadata: { since: "unstable" } }, + pow: { kind: "function", metadata: { since: "unstable" } }, + reverse_bits: { kind: "function", metadata: { since: "1.37.0" } }, + rotate_left: { kind: "function", metadata: { since: "unstable" } }, + rotate_right: { kind: "function", metadata: { since: "unstable" } }, + signum: { kind: "function", metadata: { since: "unstable" } }, + swap_bytes: { kind: "function", metadata: { since: "unstable" } }, + to_be: { kind: "function", metadata: { since: "unstable" } }, + to_le: { kind: "function", metadata: { since: "unstable" } }, + trailing_zeros: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ZeroablePrimitive: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/ops.ts b/packages/rust/src/builtins/std/ops.ts new file mode 100644 index 000000000..ab63e148a --- /dev/null +++ b/packages/rust/src/builtins/std/ops.ts @@ -0,0 +1,117 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ops = { + Add: { kind: "trait", metadata: { since: "1.0.0" } }, + AddAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + AsyncFn: { kind: "trait", metadata: { since: "1.85.0" } }, + AsyncFnMut: { kind: "trait", metadata: { since: "1.85.0" } }, + AsyncFnOnce: { kind: "trait", metadata: { since: "1.85.0" } }, + BitAnd: { kind: "trait", metadata: { since: "1.0.0" } }, + BitAndAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + BitOr: { kind: "trait", metadata: { since: "1.0.0" } }, + BitOrAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + BitXor: { kind: "trait", metadata: { since: "1.0.0" } }, + BitXorAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Bound: { + kind: "enum", metadata: { since: "1.17.0" }, + members: { + as_mut: { kind: "function", metadata: { since: "unstable" } }, + as_ref: { kind: "function", metadata: { since: "1.65.0" } }, + cloned: { kind: "function", metadata: { since: "1.55.0" } }, + copied: { kind: "function", metadata: { since: "unstable" } }, + map: { kind: "function", metadata: { since: "1.77.0" } }, + }, + }, + CoerceShared: { kind: "trait", metadata: { since: "unstable" } }, + CoerceUnsized: { kind: "trait", metadata: { since: "unstable" } }, + ControlFlow: { + kind: "enum", metadata: { since: "1.55.0" }, + members: { + break_ok: { kind: "function", metadata: { since: "unstable" } }, + break_value: { kind: "function", metadata: { since: "1.83.0" } }, + continue_ok: { kind: "function", metadata: { since: "unstable" } }, + continue_value: { kind: "function", metadata: { since: "1.83.0" } }, + into_value: { kind: "function", metadata: { since: "unstable" } }, + is_break: { kind: "function", metadata: { since: "1.59.0" } }, + is_continue: { kind: "function", metadata: { since: "1.59.0" } }, + map_break: { kind: "function", metadata: { since: "1.83.0" } }, + map_continue: { kind: "function", metadata: { since: "1.83.0" } }, + }, + }, + Coroutine: { kind: "trait", metadata: { since: "unstable" } }, + CoroutineState: { kind: "enum", metadata: { since: "unstable" } }, + Deref: { kind: "trait", metadata: { since: "1.0.0" } }, + DerefMut: { kind: "trait", metadata: { since: "1.0.0" } }, + DerefPure: { kind: "trait", metadata: { since: "unstable" } }, + DispatchFromDyn: { kind: "trait", metadata: { since: "unstable" } }, + Div: { kind: "trait", metadata: { since: "1.0.0" } }, + DivAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Drop: { kind: "trait", metadata: { since: "1.0.0" } }, + Fn: { kind: "trait", metadata: { since: "1.0.0" } }, + FnMut: { kind: "trait", metadata: { since: "1.0.0" } }, + FnOnce: { kind: "trait", metadata: { since: "1.0.0" } }, + FromResidual: { kind: "trait", metadata: { since: "unstable" } }, + Index: { kind: "trait", metadata: { since: "1.0.0" } }, + IndexMut: { kind: "trait", metadata: { since: "1.0.0" } }, + IntoBounds: { kind: "trait", metadata: { since: "unstable" } }, + Mul: { kind: "trait", metadata: { since: "1.0.0" } }, + MulAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Neg: { kind: "trait", metadata: { since: "1.0.0" } }, + Not: { kind: "trait", metadata: { since: "1.0.0" } }, + OneSidedRange: { kind: "trait", metadata: { since: "unstable" } }, + OneSidedRangeBound: { kind: "enum", metadata: { since: "unstable" } }, + Range: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + }, + }, + RangeBounds: { kind: "trait", metadata: { since: "1.28.0" } }, + RangeFrom: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeFull: { kind: "struct", metadata: { since: "1.0.0" } }, + RangeInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + end: { kind: "function", metadata: { since: "1.27.0" } }, + into_inner: { kind: "function", metadata: { since: "1.27.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + start: { kind: "function", metadata: { since: "1.27.0" } }, + }, + }, + RangeTo: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeToInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + Reborrow: { kind: "trait", metadata: { since: "unstable" } }, + Receiver: { kind: "trait", metadata: { since: "unstable" } }, + Rem: { kind: "trait", metadata: { since: "1.0.0" } }, + RemAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Residual: { kind: "trait", metadata: { since: "unstable" } }, + Shl: { kind: "trait", metadata: { since: "1.0.0" } }, + ShlAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Shr: { kind: "trait", metadata: { since: "1.0.0" } }, + ShrAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Sub: { kind: "trait", metadata: { since: "1.0.0" } }, + SubAssign: { kind: "trait", metadata: { since: "1.8.0" } }, + Try: { kind: "trait", metadata: { since: "unstable" } }, + Yeet: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/option.ts b/packages/rust/src/builtins/std/option.ts new file mode 100644 index 000000000..f35d5bf86 --- /dev/null +++ b/packages/rust/src/builtins/std/option.ts @@ -0,0 +1,69 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_option = { + IntoIter: { kind: "struct" }, + Item: { kind: "struct" }, + Iter: { kind: "struct" }, + IterMut: { kind: "struct" }, + Option: { + kind: "enum", + members: { + and: { kind: "function", metadata: { since: "1.0.0" } }, + and_then: { kind: "function", metadata: { since: "1.0.0" } }, + as_deref: { kind: "function", metadata: { since: "1.40.0" } }, + as_deref_mut: { kind: "function", metadata: { since: "1.40.0" } }, + as_mut: { kind: "function", metadata: { since: "1.0.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.75.0" } }, + as_pin_mut: { kind: "function", metadata: { since: "1.33.0" } }, + as_pin_ref: { kind: "function", metadata: { since: "1.33.0" } }, + as_ref: { kind: "function", metadata: { since: "1.48.0" } }, + as_slice: { kind: "function", metadata: { since: "1.75.0" } }, + cloned: { kind: "function", metadata: { since: "1.0.0" } }, + copied: { kind: "function", metadata: { since: "1.35.0" } }, + expect: { kind: "function", metadata: { since: "1.0.0" } }, + filter: { kind: "function", metadata: { since: "1.27.0" } }, + flatten: { kind: "function", metadata: { since: "1.40.0" } }, + flatten_mut: { kind: "function", metadata: { since: "unstable" } }, + flatten_ref: { kind: "function", metadata: { since: "unstable" } }, + get_or_insert: { kind: "function", metadata: { since: "1.20.0" } }, + get_or_insert_default: { kind: "function", metadata: { since: "1.83.0" } }, + get_or_insert_with: { kind: "function", metadata: { since: "1.20.0" } }, + get_or_try_insert_with: { kind: "function", metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.53.0" } }, + inspect: { kind: "function", metadata: { since: "1.76.0" } }, + into_flat_iter: { kind: "function", metadata: { since: "unstable" } }, + is_none: { kind: "function", metadata: { since: "1.0.0" } }, + is_none_or: { kind: "function", metadata: { since: "1.82.0" } }, + is_some: { kind: "function", metadata: { since: "1.0.0" } }, + is_some_and: { kind: "function", metadata: { since: "1.70.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + map: { kind: "function", metadata: { since: "1.0.0" } }, + map_or: { kind: "function", metadata: { since: "1.0.0" } }, + map_or_default: { kind: "function", metadata: { since: "unstable" } }, + map_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + ok_or: { kind: "function", metadata: { since: "1.0.0" } }, + ok_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + or: { kind: "function", metadata: { since: "1.0.0" } }, + or_else: { kind: "function", metadata: { since: "1.0.0" } }, + reduce: { kind: "function", metadata: { since: "unstable" } }, + replace: { kind: "function", metadata: { since: "1.31.0" } }, + take: { kind: "function", metadata: { since: "1.0.0" } }, + take_if: { kind: "function", metadata: { since: "1.80.0" } }, + transpose: { kind: "function", metadata: { since: "1.33.0" } }, + unwrap: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_default: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + unzip: { kind: "function", metadata: { since: "1.66.0" } }, + xor: { kind: "function", metadata: { since: "1.37.0" } }, + zip: { kind: "function", metadata: { since: "1.46.0" } }, + zip_with: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + OptionFlatten: { kind: "struct" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/panic.ts b/packages/rust/src/builtins/std/panic.ts new file mode 100644 index 000000000..406e15c9f --- /dev/null +++ b/packages/rust/src/builtins/std/panic.ts @@ -0,0 +1,55 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_panic = { + abort_unwind: { kind: "function" }, + always_abort: { kind: "function", metadata: { since: "unstable" } }, + AssertUnwindSafe: { kind: "struct" }, + BacktraceStyle: { kind: "enum", metadata: { since: "unstable" } }, + catch_unwind: { kind: "function", metadata: { since: "1.9.0" } }, + get_backtrace_style: { kind: "function", metadata: { since: "unstable" } }, + Location: { + kind: "struct", + members: { + caller: { kind: "function", associated: true, metadata: { since: "1.46.0" } }, + column: { kind: "function", metadata: { since: "1.25.0" } }, + file: { kind: "function", metadata: { since: "1.10.0" } }, + file_as_c_str: { kind: "function", metadata: { since: "1.92.0" } }, + line: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + panic_any: { kind: "function", metadata: { since: "1.51.0" } }, + PanicHookInfo: { + kind: "struct", metadata: { since: "1.81.0" }, + members: { + can_unwind: { kind: "function", metadata: { since: "unstable" } }, + location: { kind: "function", metadata: { since: "1.10.0" } }, + payload: { kind: "function", metadata: { since: "1.10.0" } }, + payload_as_str: { kind: "function", metadata: { since: "1.91.0" } }, + }, + }, + PanicInfo: { + kind: "type-alias", metadata: { since: "1.10.0" }, + members: { + can_unwind: { kind: "function", metadata: { since: "unstable" } }, + location: { kind: "function", metadata: { since: "1.10.0" } }, + message: { kind: "function", metadata: { since: "1.81.0" } }, + payload: { kind: "function", metadata: { since: "1.10.0" } }, + }, + }, + PanicMessage: { + kind: "struct", metadata: { since: "1.81.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.81.0" } }, + }, + }, + RefUnwindSafe: { kind: "trait" }, + resume_unwind: { kind: "function", metadata: { since: "1.9.0" } }, + set_backtrace_style: { kind: "function", metadata: { since: "unstable" } }, + set_hook: { kind: "function", metadata: { since: "1.10.0" } }, + take_hook: { kind: "function", metadata: { since: "1.10.0" } }, + UnwindSafe: { kind: "trait" }, + update_hook: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/panicking.ts b/packages/rust/src/builtins/std/panicking.ts new file mode 100644 index 000000000..c71d59db7 --- /dev/null +++ b/packages/rust/src/builtins/std/panicking.ts @@ -0,0 +1,41 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_panicking = { + const_panic_fmt: { kind: "function" }, + panic: { kind: "function" }, + panic_display: { kind: "function" }, + panic_fmt: { kind: "function" }, + panic_nounwind: { kind: "function" }, + panic_nounwind_fmt: { kind: "function", metadata: { since: "unstable" } }, + panic_nounwind_nobacktrace: { kind: "function" }, + panic_str_2015: { kind: "function" }, + unreachable_display: { kind: "function" }, +} as const satisfies Record; + +export const mod_panicking_panic_const = { + panic_const_add_overflow: { kind: "function" }, + panic_const_async_fn_resumed: { kind: "function" }, + panic_const_async_fn_resumed_drop: { kind: "function" }, + panic_const_async_fn_resumed_panic: { kind: "function" }, + panic_const_async_gen_fn_resumed: { kind: "function" }, + panic_const_async_gen_fn_resumed_drop: { kind: "function" }, + panic_const_async_gen_fn_resumed_panic: { kind: "function" }, + panic_const_coroutine_resumed: { kind: "function" }, + panic_const_coroutine_resumed_drop: { kind: "function" }, + panic_const_coroutine_resumed_panic: { kind: "function" }, + panic_const_div_by_zero: { kind: "function" }, + panic_const_div_overflow: { kind: "function" }, + panic_const_gen_fn_none: { kind: "function" }, + panic_const_gen_fn_none_drop: { kind: "function" }, + panic_const_gen_fn_none_panic: { kind: "function" }, + panic_const_mul_overflow: { kind: "function" }, + panic_const_neg_overflow: { kind: "function" }, + panic_const_rem_by_zero: { kind: "function" }, + panic_const_rem_overflow: { kind: "function" }, + panic_const_shl_overflow: { kind: "function" }, + panic_const_shr_overflow: { kind: "function" }, + panic_const_sub_overflow: { kind: "function" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/pat.ts b/packages/rust/src/builtins/std/pat.ts new file mode 100644 index 000000000..8a6d8dcb9 --- /dev/null +++ b/packages/rust/src/builtins/std/pat.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_pat = { + pattern_type: { kind: "symbol" }, + RangePattern: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/path.ts b/packages/rust/src/builtins/std/path.ts new file mode 100644 index 000000000..83621e133 --- /dev/null +++ b/packages/rust/src/builtins/std/path.ts @@ -0,0 +1,125 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_path = { + absolute: { kind: "function", metadata: { since: "1.79.0" } }, + Ancestors: { kind: "struct", metadata: { since: "1.28.0" } }, + Component: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + as_os_str: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + Components: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_path: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + Display: { kind: "struct", metadata: { since: "1.0.0" } }, + is_separator: { kind: "function", metadata: { since: "1.0.0" } }, + Iter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_path: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + MAIN_SEPARATOR: { kind: "const", metadata: { since: "1.0.0" } }, + MAIN_SEPARATOR_STR: { kind: "const", metadata: { since: "1.68.0" } }, + NormalizeError: { kind: "struct", metadata: { since: "unstable" } }, + Path: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + absolute: { kind: "function", metadata: { since: "unstable" } }, + ancestors: { kind: "function", metadata: { since: "1.28.0" } }, + as_mut_os_str: { kind: "function", metadata: { since: "1.70.0" } }, + as_os_str: { kind: "function", metadata: { since: "1.0.0" } }, + as_path: { kind: "function", metadata: { since: "unstable" } }, + canonicalize: { kind: "function", metadata: { since: "1.5.0" } }, + components: { kind: "function", metadata: { since: "1.0.0" } }, + display: { kind: "function", metadata: { since: "1.0.0" } }, + ends_with: { kind: "function", metadata: { since: "1.0.0" } }, + exists: { kind: "function", metadata: { since: "1.5.0" } }, + extension: { kind: "function", metadata: { since: "1.0.0" } }, + file_name: { kind: "function", metadata: { since: "1.0.0" } }, + file_prefix: { kind: "function", metadata: { since: "1.91.0" } }, + file_stem: { kind: "function", metadata: { since: "1.0.0" } }, + has_root: { kind: "function", metadata: { since: "1.0.0" } }, + has_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + into_path_buf: { kind: "function", metadata: { since: "1.20.0" } }, + is_absolute: { kind: "function", metadata: { since: "1.0.0" } }, + is_dir: { kind: "function", metadata: { since: "1.5.0" } }, + is_empty: { kind: "function", metadata: { since: "unstable" } }, + is_file: { kind: "function", metadata: { since: "1.5.0" } }, + is_relative: { kind: "function", metadata: { since: "1.0.0" } }, + is_symlink: { kind: "function", metadata: { since: "1.58.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + join: { kind: "function", metadata: { since: "1.0.0" } }, + metadata: { kind: "function", metadata: { since: "1.5.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + normalize_lexically: { kind: "function", metadata: { since: "unstable" } }, + parent: { kind: "function", metadata: { since: "1.0.0" } }, + read_dir: { kind: "function", metadata: { since: "1.5.0" } }, + read_link: { kind: "function", metadata: { since: "1.5.0" } }, + starts_with: { kind: "function", metadata: { since: "1.0.0" } }, + strip_prefix: { kind: "function", metadata: { since: "1.7.0" } }, + symlink_metadata: { kind: "function", metadata: { since: "1.5.0" } }, + to_path_buf: { kind: "function", metadata: { since: "1.0.0" } }, + to_str: { kind: "function", metadata: { since: "1.0.0" } }, + to_string_lossy: { kind: "function", metadata: { since: "1.0.0" } }, + trim_prefix: { kind: "function", metadata: { since: "unstable" } }, + trim_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + try_exists: { kind: "function", metadata: { since: "1.63.0" } }, + with_added_extension: { kind: "function", metadata: { since: "1.91.0" } }, + with_extension: { kind: "function", metadata: { since: "1.0.0" } }, + with_file_name: { kind: "function", metadata: { since: "1.0.0" } }, + with_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + PathBuf: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + add_extension: { kind: "function", metadata: { since: "1.91.0" } }, + as_mut_os_string: { kind: "function", metadata: { since: "1.70.0" } }, + as_path: { kind: "function", metadata: { since: "1.0.0" } }, + capacity: { kind: "function", metadata: { since: "1.44.0" } }, + clear: { kind: "function", metadata: { since: "1.44.0" } }, + into_boxed_path: { kind: "function", metadata: { since: "1.20.0" } }, + into_os_string: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.89.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + push_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + reserve: { kind: "function", metadata: { since: "1.44.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.44.0" } }, + set_extension: { kind: "function", metadata: { since: "1.0.0" } }, + set_file_name: { kind: "function", metadata: { since: "1.0.0" } }, + set_trailing_sep: { kind: "function", metadata: { since: "unstable" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.44.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.63.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.63.0" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.44.0" } }, + }, + }, + Prefix: { + kind: "enum", metadata: { since: "1.0.0" }, + members: { + is_verbatim: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + PrefixComponent: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_os_str: { kind: "function", metadata: { since: "1.0.0" } }, + kind: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + SEPARATORS: { kind: "const", metadata: { since: "unstable" } }, + SEPARATORS_STR: { kind: "const", metadata: { since: "unstable" } }, + StripPrefixError: { kind: "struct", metadata: { since: "1.7.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/pin.ts b/packages/rust/src/builtins/std/pin.ts new file mode 100644 index 000000000..e9aba119a --- /dev/null +++ b/packages/rust/src/builtins/std/pin.ts @@ -0,0 +1,42 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_pin = { + pin: { kind: "symbol", metadata: { since: "1.68.0" } }, + Pin: { + kind: "struct", + members: { + as_deref_mut: { kind: "function", metadata: { since: "1.84.0" } }, + as_mut: { kind: "function", metadata: { since: "1.33.0" } }, + as_ref: { kind: "function", metadata: { since: "1.33.0" } }, + get_mut: { kind: "function", metadata: { since: "1.33.0" } }, + get_ref: { kind: "function", metadata: { since: "1.84.0" } }, + get_unchecked_mut: { kind: "function", metadata: { since: "1.33.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_ref: { kind: "function", metadata: { since: "1.84.0" } }, + map_unchecked: { kind: "function", metadata: { since: "1.33.0" } }, + map_unchecked_mut: { kind: "function", metadata: { since: "1.33.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.84.0" } }, + set: { kind: "function", metadata: { since: "1.33.0" } }, + static_mut: { kind: "function", associated: true, metadata: { since: "1.61.0" } }, + static_ref: { kind: "function", associated: true, metadata: { since: "1.61.0" } }, + }, + }, + PinCoerceUnsized: { kind: "trait" }, + UnsafePinned: { + kind: "struct", metadata: { since: "unstable" }, + members: { + get: { kind: "function", metadata: { since: "unstable" } }, + get_mut_pinned: { kind: "function", metadata: { since: "unstable" } }, + get_mut_unchecked: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + raw_get: { kind: "function", associated: true, metadata: { since: "unstable" } }, + raw_get_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/process.ts b/packages/rust/src/builtins/std/process.ts new file mode 100644 index 000000000..e85b8f1c0 --- /dev/null +++ b/packages/rust/src/builtins/std/process.ts @@ -0,0 +1,87 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_process = { + abort: { kind: "function", metadata: { since: "1.17.0" } }, + Child: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + id: { kind: "function", metadata: { since: "1.3.0" } }, + kill: { kind: "function", metadata: { since: "1.0.0" } }, + try_wait: { kind: "function", metadata: { since: "1.18.0" } }, + wait: { kind: "function", metadata: { since: "1.0.0" } }, + wait_with_output: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ChildStderr: { kind: "struct", metadata: { since: "1.0.0" } }, + ChildStdin: { kind: "struct", metadata: { since: "1.0.0" } }, + ChildStdout: { kind: "struct", metadata: { since: "1.0.0" } }, + Command: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + arg: { kind: "function", metadata: { since: "1.0.0" } }, + args: { kind: "function", metadata: { since: "1.0.0" } }, + current_dir: { kind: "function", metadata: { since: "1.0.0" } }, + env: { kind: "function", metadata: { since: "1.0.0" } }, + env_clear: { kind: "function", metadata: { since: "1.0.0" } }, + env_remove: { kind: "function", metadata: { since: "1.0.0" } }, + envs: { kind: "function", metadata: { since: "1.19.0" } }, + get_args: { kind: "function", metadata: { since: "1.57.0" } }, + get_current_dir: { kind: "function", metadata: { since: "1.57.0" } }, + get_env_clear: { kind: "function", metadata: { since: "unstable" } }, + get_envs: { kind: "function", metadata: { since: "1.57.0" } }, + get_program: { kind: "function", metadata: { since: "1.57.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + output: { kind: "function", metadata: { since: "1.0.0" } }, + spawn: { kind: "function", metadata: { since: "1.0.0" } }, + status: { kind: "function", metadata: { since: "1.0.0" } }, + stderr: { kind: "function", metadata: { since: "1.0.0" } }, + stdin: { kind: "function", metadata: { since: "1.0.0" } }, + stdout: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + CommandArgs: { kind: "struct", metadata: { since: "1.57.0" } }, + CommandEnvs: { kind: "struct", metadata: { since: "1.57.0" } }, + exit: { kind: "function", metadata: { since: "1.0.0" } }, + ExitCode: { + kind: "struct", metadata: { since: "1.61.0" }, + members: { + exit_process: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExitStatus: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + code: { kind: "function", metadata: { since: "1.0.0" } }, + exit_ok: { kind: "function", metadata: { since: "unstable" } }, + success: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ExitStatusError: { + kind: "struct", metadata: { since: "unstable" }, + members: { + code: { kind: "function" }, + code_nonzero: { kind: "function" }, + into_status: { kind: "function" }, + }, + }, + id: { kind: "function", metadata: { since: "1.26.0" } }, + Output: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + exit_ok: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Stdio: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + inherit: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + makes_pipe: { kind: "function", metadata: { since: "unstable" } }, + null: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + piped: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + Termination: { kind: "trait", metadata: { since: "1.61.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/profiling.ts b/packages/rust/src/builtins/std/profiling.ts new file mode 100644 index 000000000..6527c099e --- /dev/null +++ b/packages/rust/src/builtins/std/profiling.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_profiling = { + compiler_copy: { kind: "function", metadata: { since: "unstable" } }, + compiler_move: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/ptr.ts b/packages/rust/src/builtins/std/ptr.ts new file mode 100644 index 000000000..0e3542c9d --- /dev/null +++ b/packages/rust/src/builtins/std/ptr.ts @@ -0,0 +1,115 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ptr = { + addr_eq: { kind: "function", metadata: { since: "1.76.0" } }, + addr_of: { kind: "symbol", metadata: { since: "1.51.0" } }, + addr_of_mut: { kind: "symbol" }, + Alignment: { kind: "type-alias", metadata: { since: "unstable" } }, + copy: { kind: "function" }, + copy_nonoverlapping: { kind: "function" }, + dangling: { kind: "function", metadata: { since: "1.84.0" } }, + dangling_mut: { kind: "function", metadata: { since: "1.84.0" } }, + drop_in_place: { kind: "function" }, + DynMetadata: { + kind: "struct", + members: { + align_of: { kind: "function" }, + layout: { kind: "function" }, + size_of: { kind: "function" }, + }, + }, + eq: { kind: "function" }, + fn_addr_eq: { kind: "function", metadata: { since: "1.85.0" } }, + from_mut: { kind: "function", metadata: { since: "1.76.0" } }, + from_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "unstable" } }, + from_ref: { kind: "function", metadata: { since: "1.76.0" } }, + hash: { kind: "function", metadata: { since: "1.35.0" } }, + metadata: { kind: "function" }, + NonNull: { + kind: "struct", metadata: { since: "1.25.0" }, + members: { + add: { kind: "function", metadata: { since: "1.80.0" } }, + addr: { kind: "function", metadata: { since: "1.84.0" } }, + align_offset: { kind: "function", metadata: { since: "1.80.0" } }, + as_mut: { kind: "function", metadata: { since: "1.25.0" } }, + as_mut_ptr: { kind: "function", metadata: { since: "unstable" } }, + as_non_null_ptr: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.25.0" } }, + as_ref: { kind: "function", metadata: { since: "1.25.0" } }, + as_uninit_mut: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_ref: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_slice: { kind: "function", metadata: { since: "unstable" } }, + as_uninit_slice_mut: { kind: "function", metadata: { since: "unstable" } }, + byte_add: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset_from: { kind: "function", metadata: { since: "1.80.0" } }, + byte_offset_from_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + byte_sub: { kind: "function", metadata: { since: "1.80.0" } }, + cast: { kind: "function", metadata: { since: "1.27.0" } }, + cast_array: { kind: "function", metadata: { since: "unstable" } }, + cast_init: { kind: "function", metadata: { since: "unstable" } }, + cast_slice: { kind: "function", metadata: { since: "unstable" } }, + cast_uninit: { kind: "function", metadata: { since: "unstable" } }, + copy_from: { kind: "function", metadata: { since: "1.80.0" } }, + copy_from_nonoverlapping: { kind: "function", metadata: { since: "1.80.0" } }, + copy_to: { kind: "function", metadata: { since: "1.80.0" } }, + copy_to_nonoverlapping: { kind: "function", metadata: { since: "1.80.0" } }, + dangling: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + drop_in_place: { kind: "function", metadata: { since: "1.80.0" } }, + expose_provenance: { kind: "function", metadata: { since: "1.89.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ref: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + get_unchecked_mut: { kind: "function", metadata: { since: "unstable" } }, + is_aligned: { kind: "function", metadata: { since: "1.79.0" } }, + is_aligned_to: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "1.79.0" } }, + len: { kind: "function", metadata: { since: "1.63.0" } }, + map_addr: { kind: "function", metadata: { since: "1.84.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + new_unchecked: { kind: "function", associated: true, metadata: { since: "1.25.0" } }, + offset: { kind: "function", metadata: { since: "1.80.0" } }, + offset_from: { kind: "function", metadata: { since: "1.80.0" } }, + offset_from_unsigned: { kind: "function", metadata: { since: "1.87.0" } }, + read: { kind: "function", metadata: { since: "1.80.0" } }, + read_unaligned: { kind: "function", metadata: { since: "1.80.0" } }, + read_volatile: { kind: "function", metadata: { since: "1.80.0" } }, + replace: { kind: "function", metadata: { since: "1.80.0" } }, + slice_from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + sub: { kind: "function", metadata: { since: "1.80.0" } }, + swap: { kind: "function", metadata: { since: "1.80.0" } }, + to_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + try_cast_aligned: { kind: "function", metadata: { since: "unstable" } }, + with_addr: { kind: "function", metadata: { since: "1.84.0" } }, + with_exposed_provenance: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + without_provenance: { kind: "function", associated: true, metadata: { since: "1.89.0" } }, + write: { kind: "function", metadata: { since: "1.80.0" } }, + write_bytes: { kind: "function", metadata: { since: "1.80.0" } }, + write_unaligned: { kind: "function", metadata: { since: "1.80.0" } }, + write_volatile: { kind: "function", metadata: { since: "1.80.0" } }, + }, + }, + null: { kind: "function" }, + null_mut: { kind: "function" }, + Pointee: { kind: "trait" }, + read: { kind: "function" }, + read_unaligned: { kind: "function" }, + read_volatile: { kind: "function" }, + replace: { kind: "function" }, + slice_from_raw_parts: { kind: "function", metadata: { since: "1.42.0" } }, + slice_from_raw_parts_mut: { kind: "function", metadata: { since: "1.42.0" } }, + swap: { kind: "function" }, + swap_nonoverlapping: { kind: "function", metadata: { since: "1.27.0" } }, + with_exposed_provenance: { kind: "function" }, + with_exposed_provenance_mut: { kind: "function" }, + without_provenance: { kind: "function" }, + without_provenance_mut: { kind: "function" }, + write: { kind: "function" }, + write_bytes: { kind: "function" }, + write_unaligned: { kind: "function" }, + write_volatile: { kind: "function" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/random.ts b/packages/rust/src/builtins/std/random.ts new file mode 100644 index 000000000..94a5fd880 --- /dev/null +++ b/packages/rust/src/builtins/std/random.ts @@ -0,0 +1,11 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_random = { + DefaultRandomSource: { kind: "struct", metadata: { since: "unstable" } }, + Distribution: { kind: "trait", metadata: { since: "unstable" } }, + random: { kind: "function", metadata: { since: "unstable" } }, + RandomSource: { kind: "trait", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/range.ts b/packages/rust/src/builtins/std/range.ts new file mode 100644 index 000000000..5a6356a1d --- /dev/null +++ b/packages/rust/src/builtins/std/range.ts @@ -0,0 +1,87 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_range = { + Range: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function" }, + }, + }, + RangeFrom: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function" }, + }, + }, + RangeFromIter: { + kind: "struct", + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeInclusive: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "1.95.0" } }, + is_empty: { kind: "function", metadata: { since: "1.95.0" } }, + iter: { kind: "function", metadata: { since: "1.95.0" } }, + }, + }, + RangeInclusiveIter: { + kind: "struct", metadata: { since: "1.95.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeIter: { + kind: "struct", + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RangeToInclusive: { + kind: "struct", + members: { + contains: { kind: "function", metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; + +export const mod_range_legacy = { + Range: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + }, + }, + RangeFrom: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, + RangeInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + end: { kind: "function", metadata: { since: "1.27.0" } }, + into_inner: { kind: "function", metadata: { since: "1.27.0" } }, + is_empty: { kind: "function", metadata: { since: "1.47.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + start: { kind: "function", metadata: { since: "1.27.0" } }, + }, + }, + RangeToInclusive: { + kind: "struct", metadata: { since: "1.26.0" }, + members: { + contains: { kind: "function", metadata: { since: "1.35.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/rc.ts b/packages/rust/src/builtins/std/rc.ts new file mode 100644 index 000000000..86aec460d --- /dev/null +++ b/packages/rust/src/builtins/std/rc.ts @@ -0,0 +1,91 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_rc = { + Rc: { + kind: "struct", + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + decrement_strong_count: { kind: "function", associated: true, metadata: { since: "1.53.0" } }, + decrement_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.29.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + downgrade: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + get_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + increment_strong_count: { kind: "function", associated: true, metadata: { since: "1.53.0" } }, + increment_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + make_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_cyclic: { kind: "function", associated: true, metadata: { since: "1.60.0" } }, + new_cyclic_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + strong_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_unwrap: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + unwrap_or_clone: { kind: "function", associated: true, metadata: { since: "1.76.0" } }, + weak_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + }, + }, + UniqueRc: { + kind: "struct", + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_rc: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + UniqueRcUninit: { kind: "struct" }, + Weak: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.45.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_raw: { kind: "function", metadata: { since: "1.45.0" } }, + into_raw_with_allocator: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", metadata: { since: "1.39.0" } }, + strong_count: { kind: "function", metadata: { since: "1.41.0" } }, + upgrade: { kind: "function", metadata: { since: "1.4.0" } }, + weak_count: { kind: "function", metadata: { since: "1.41.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/result.ts b/packages/rust/src/builtins/std/result.ts new file mode 100644 index 000000000..62d7c6eeb --- /dev/null +++ b/packages/rust/src/builtins/std/result.ts @@ -0,0 +1,53 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_result = { + IntoIter: { kind: "struct" }, + Iter: { kind: "struct" }, + IterMut: { kind: "struct" }, + Result: { + kind: "enum", + members: { + and: { kind: "function", metadata: { since: "1.0.0" } }, + and_then: { kind: "function", metadata: { since: "1.0.0" } }, + as_deref: { kind: "function", metadata: { since: "1.47.0" } }, + as_deref_mut: { kind: "function", metadata: { since: "1.47.0" } }, + as_mut: { kind: "function", metadata: { since: "1.0.0" } }, + as_ref: { kind: "function", metadata: { since: "1.48.0" } }, + cloned: { kind: "function", metadata: { since: "1.59.0" } }, + copied: { kind: "function", metadata: { since: "1.59.0" } }, + err: { kind: "function", metadata: { since: "1.0.0" } }, + expect: { kind: "function", metadata: { since: "1.4.0" } }, + expect_err: { kind: "function", metadata: { since: "1.17.0" } }, + flatten: { kind: "function", metadata: { since: "1.89.0" } }, + inspect: { kind: "function", metadata: { since: "1.76.0" } }, + inspect_err: { kind: "function", metadata: { since: "1.76.0" } }, + into_err: { kind: "function", metadata: { since: "unstable" } }, + into_ok: { kind: "function", metadata: { since: "unstable" } }, + is_err: { kind: "function", metadata: { since: "1.48.0" } }, + is_err_and: { kind: "function", metadata: { since: "1.70.0" } }, + is_ok: { kind: "function", metadata: { since: "1.48.0" } }, + is_ok_and: { kind: "function", metadata: { since: "1.70.0" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + iter_mut: { kind: "function", metadata: { since: "1.0.0" } }, + map: { kind: "function", metadata: { since: "1.0.0" } }, + map_err: { kind: "function", metadata: { since: "1.0.0" } }, + map_or: { kind: "function", metadata: { since: "1.41.0" } }, + map_or_default: { kind: "function", metadata: { since: "unstable" } }, + map_or_else: { kind: "function", metadata: { since: "1.41.0" } }, + ok: { kind: "function", metadata: { since: "1.0.0" } }, + or: { kind: "function", metadata: { since: "1.0.0" } }, + or_else: { kind: "function", metadata: { since: "1.0.0" } }, + transpose: { kind: "function", metadata: { since: "1.33.0" } }, + unwrap: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_err: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_err_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + unwrap_or: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_or_default: { kind: "function", metadata: { since: "1.16.0" } }, + unwrap_or_else: { kind: "function", metadata: { since: "1.0.0" } }, + unwrap_unchecked: { kind: "function", metadata: { since: "1.58.0" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/root.ts b/packages/rust/src/builtins/std/root.ts new file mode 100644 index 000000000..43d85add7 --- /dev/null +++ b/packages/rust/src/builtins/std/root.ts @@ -0,0 +1,68 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_root = { + assert: { kind: "symbol" }, + assert_eq: { kind: "symbol" }, + assert_matches: { kind: "symbol" }, + assert_ne: { kind: "symbol" }, + assert_unsafe_precondition: { kind: "symbol", metadata: { since: "unstable" } }, + AsyncFn: { kind: "trait" }, + AsyncFnMut: { kind: "trait" }, + AsyncFnOnce: { kind: "trait" }, + cfg: { kind: "symbol" }, + cfg_select: { kind: "symbol" }, + column: { kind: "symbol" }, + compile_error: { kind: "symbol" }, + concat: { kind: "symbol" }, + concat_bytes: { kind: "symbol" }, + const_format_args: { kind: "symbol" }, + dbg: { kind: "symbol", metadata: { since: "unstable" } }, + debug_assert: { kind: "symbol" }, + debug_assert_eq: { kind: "symbol" }, + debug_assert_matches: { kind: "symbol" }, + debug_assert_ne: { kind: "symbol" }, + deref: { kind: "symbol" }, + DoubleEndedIterator: { kind: "trait" }, + Drop: { kind: "trait" }, + env: { kind: "symbol" }, + eprint: { kind: "symbol", metadata: { since: "1.19.0" } }, + eprintln: { kind: "symbol", metadata: { since: "1.19.0" } }, + ExactSizeIterator: { kind: "trait" }, + Extend: { kind: "trait" }, + file: { kind: "symbol" }, + Fn: { kind: "trait" }, + FnMut: { kind: "trait" }, + FnOnce: { kind: "trait" }, + format: { kind: "symbol" }, + format_args: { kind: "symbol" }, + hash_map: { kind: "symbol", metadata: { since: "unstable" } }, + include: { kind: "symbol" }, + include_bytes: { kind: "symbol" }, + include_str: { kind: "symbol" }, + IntoIterator: { kind: "trait" }, + is_x86_feature_detected: { kind: "symbol" }, + Iterator: { kind: "trait" }, + line: { kind: "symbol" }, + log_syntax: { kind: "symbol" }, + matches: { kind: "symbol" }, + module_path: { kind: "symbol" }, + option_env: { kind: "symbol" }, + panic: { kind: "symbol", metadata: { since: "1.0.0" } }, + pattern_type: { kind: "symbol", metadata: { since: "unstable" } }, + print: { kind: "symbol", metadata: { since: "1.0.0" } }, + println: { kind: "symbol", metadata: { since: "1.0.0" } }, + stringify: { kind: "symbol" }, + thread_local: { kind: "symbol", metadata: { since: "1.0.0" } }, + todo: { kind: "symbol" }, + trace_macros: { kind: "symbol" }, + try: { kind: "symbol" }, + type_ascribe: { kind: "symbol" }, + unimplemented: { kind: "symbol" }, + unreachable: { kind: "symbol" }, + vec: { kind: "symbol" }, + write: { kind: "symbol" }, + writeln: { kind: "symbol" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/slice.ts b/packages/rust/src/builtins/std/slice.ts new file mode 100644 index 000000000..58f5e2f8a --- /dev/null +++ b/packages/rust/src/builtins/std/slice.ts @@ -0,0 +1,83 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_slice = { + ArrayWindows: { kind: "struct", metadata: { since: "1.94.0" } }, + ChunkBy: { kind: "struct", metadata: { since: "1.77.0" } }, + ChunkByMut: { kind: "struct", metadata: { since: "1.77.0" } }, + Chunks: { kind: "struct", metadata: { since: "1.0.0" } }, + ChunksExact: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + ChunksExactMut: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + into_remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + ChunksMut: { kind: "struct", metadata: { since: "1.0.0" } }, + Concat: { kind: "trait" }, + EscapeAscii: { kind: "struct", metadata: { since: "1.60.0" } }, + from_mut: { kind: "function", metadata: { since: "1.28.0" } }, + from_mut_ptr_range: { kind: "function", metadata: { since: "unstable" } }, + from_ptr_range: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts: { kind: "function", metadata: { since: "1.0.0" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "1.0.0" } }, + from_ref: { kind: "function", metadata: { since: "1.28.0" } }, + GetDisjointMutError: { kind: "enum" }, + GetDisjointMutIndex: { kind: "trait" }, + Iter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slice: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + IterMut: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_mut_slice: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.53.0" } }, + into_slice: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + Join: { kind: "trait" }, + range: { kind: "function", metadata: { since: "unstable" } }, + RChunks: { kind: "struct", metadata: { since: "1.31.0" } }, + RChunksExact: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + RChunksExactMut: { + kind: "struct", metadata: { since: "1.31.0" }, + members: { + into_remainder: { kind: "function", metadata: { since: "1.31.0" } }, + }, + }, + RChunksMut: { kind: "struct", metadata: { since: "1.31.0" } }, + RSplit: { kind: "struct", metadata: { since: "1.27.0" } }, + RSplitMut: { kind: "struct", metadata: { since: "1.27.0" } }, + RSplitN: { kind: "struct", metadata: { since: "1.0.0" } }, + RSplitNMut: { kind: "struct", metadata: { since: "1.0.0" } }, + SliceIndex: { kind: "trait", metadata: { since: "1.28.0" } }, + SlicePattern: { kind: "trait" }, + Split: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_slice: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitInclusive: { kind: "struct", metadata: { since: "1.51.0" } }, + SplitInclusiveMut: { kind: "struct", metadata: { since: "1.51.0" } }, + SplitMut: { kind: "struct", metadata: { since: "1.0.0" } }, + SplitN: { kind: "struct", metadata: { since: "1.0.0" } }, + SplitNMut: { kind: "struct", metadata: { since: "1.0.0" } }, + try_range: { kind: "function", metadata: { since: "unstable" } }, + Windows: { kind: "struct", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/str.ts b/packages/rust/src/builtins/std/str.ts new file mode 100644 index 000000000..8525ffdc1 --- /dev/null +++ b/packages/rust/src/builtins/std/str.ts @@ -0,0 +1,145 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_str = { + Bytes: { kind: "struct", metadata: { since: "1.0.0" } }, + BytesIsNotEmpty: { kind: "struct" }, + CharEscapeDebugContinue: { kind: "struct" }, + CharEscapeDefault: { kind: "struct" }, + CharEscapeUnicode: { kind: "struct" }, + CharIndices: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.4.0" } }, + offset: { kind: "function", metadata: { since: "1.82.0" } }, + }, + }, + Chars: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + as_str: { kind: "function", metadata: { since: "1.4.0" } }, + }, + }, + EncodeUtf16: { kind: "struct", metadata: { since: "1.8.0" } }, + EscapeDebug: { kind: "struct", metadata: { since: "1.34.0" } }, + EscapeDefault: { kind: "struct", metadata: { since: "1.34.0" } }, + EscapeUnicode: { kind: "struct", metadata: { since: "1.34.0" } }, + from_boxed_utf8_unchecked: { kind: "function", metadata: { since: "1.20.0" } }, + from_raw_parts: { kind: "function", metadata: { since: "unstable" } }, + from_raw_parts_mut: { kind: "function", metadata: { since: "unstable" } }, + from_utf8: { kind: "function", metadata: { since: "1.0.0" } }, + from_utf8_mut: { kind: "function", metadata: { since: "1.20.0" } }, + from_utf8_unchecked: { kind: "function", metadata: { since: "1.0.0" } }, + from_utf8_unchecked_mut: { kind: "function", metadata: { since: "1.20.0" } }, + FromStr: { kind: "trait", metadata: { since: "1.0.0" } }, + IsAsciiWhitespace: { kind: "struct" }, + IsNotEmpty: { kind: "struct" }, + IsWhitespace: { kind: "struct" }, + Lines: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + LinesAny: { kind: "struct", metadata: { since: "1.0.0" } }, + LinesMap: { kind: "struct" }, + Matches: { kind: "struct", metadata: { since: "1.2.0" } }, + MatchIndices: { kind: "struct", metadata: { since: "1.5.0" } }, + next_code_point: { kind: "function", metadata: { since: "unstable" } }, + ParseBoolError: { kind: "struct", metadata: { since: "1.0.0" } }, + RMatches: { kind: "struct", metadata: { since: "1.2.0" } }, + RMatchIndices: { kind: "struct", metadata: { since: "1.5.0" } }, + RSplit: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RSplitN: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RSplitTerminator: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Split: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitAsciiWhitespace: { + kind: "struct", metadata: { since: "1.34.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitInclusive: { + kind: "struct", metadata: { since: "1.51.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitN: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitTerminator: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SplitWhitespace: { + kind: "struct", metadata: { since: "1.1.0" }, + members: { + remainder: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + UnsafeBytesToStr: { kind: "struct" }, + utf8_char_width: { kind: "function", metadata: { since: "unstable" } }, + Utf8Chunk: { + kind: "struct", metadata: { since: "1.79.0" }, + members: { + invalid: { kind: "function", metadata: { since: "1.79.0" } }, + valid: { kind: "function", metadata: { since: "1.79.0" } }, + }, + }, + Utf8Chunks: { kind: "struct", metadata: { since: "1.79.0" } }, + Utf8Error: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + error_len: { kind: "function", metadata: { since: "1.20.0" } }, + valid_up_to: { kind: "function", metadata: { since: "1.5.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_str_pattern = { + CharArrayRefSearcher: { kind: "struct" }, + CharArraySearcher: { kind: "struct" }, + CharPredicateSearcher: { kind: "struct" }, + CharSearcher: { kind: "struct" }, + CharSliceSearcher: { kind: "struct" }, + DoubleEndedSearcher: { kind: "trait" }, + EmptyNeedle: { kind: "struct" }, + MultiCharEqPattern: { kind: "struct" }, + MultiCharEqSearcher: { kind: "struct" }, + Pattern: { kind: "trait" }, + ReverseSearcher: { kind: "trait" }, + Searcher: { kind: "trait" }, + SearchStep: { kind: "enum" }, + StrSearcher: { kind: "struct" }, + StrSearcherImpl: { kind: "enum" }, + TwoWaySearcher: { kind: "struct" }, + Utf8Pattern: { kind: "enum" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/string.ts b/packages/rust/src/builtins/std/string.ts new file mode 100644 index 000000000..6069b316c --- /dev/null +++ b/packages/rust/src/builtins/std/string.ts @@ -0,0 +1,85 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_string = { + Drain: { + kind: "struct", + members: { + as_str: { kind: "function", metadata: { since: "1.55.0" } }, + }, + }, + FromUtf16Error: { kind: "struct" }, + FromUtf8Error: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.26.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + into_utf8_lossy: { kind: "function", metadata: { since: "unstable" } }, + utf8_error: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + IntoChars: { + kind: "struct", + members: { + as_str: { kind: "function", metadata: { since: "unstable" } }, + into_string: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ParseError: { kind: "type-alias", metadata: { since: "1.5.0" } }, + String: { + kind: "struct", + members: { + as_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + as_mut_str: { kind: "function", metadata: { since: "1.7.0" } }, + as_mut_vec: { kind: "function", metadata: { since: "1.0.0" } }, + as_str: { kind: "function", metadata: { since: "1.7.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "1.87.0" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16_lossy: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf16be: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16be_lossy: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16le: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf16le_lossy: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf8: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf8_lossy: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_utf8_lossy_owned: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_utf8_unchecked: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + insert_str: { kind: "function", metadata: { since: "1.16.0" } }, + into_boxed_str: { kind: "function", metadata: { since: "1.4.0" } }, + into_bytes: { kind: "function", metadata: { since: "1.0.0" } }, + into_chars: { kind: "function", metadata: { since: "unstable" } }, + into_raw_parts: { kind: "function", metadata: { since: "1.93.0" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.72.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + push_str: { kind: "function", metadata: { since: "1.0.0" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + remove_matches: { kind: "function", metadata: { since: "unstable" } }, + replace_first: { kind: "function", metadata: { since: "unstable" } }, + replace_last: { kind: "function", metadata: { since: "unstable" } }, + replace_range: { kind: "function", metadata: { since: "1.27.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + retain: { kind: "function", metadata: { since: "1.26.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + split_off: { kind: "function", metadata: { since: "1.16.0" } }, + truncate: { kind: "function", metadata: { since: "1.0.0" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + }, + }, + ToString: { kind: "trait" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/sync.ts b/packages/rust/src/builtins/std/sync.ts new file mode 100644 index 000000000..88fbc8b71 --- /dev/null +++ b/packages/rust/src/builtins/std/sync.ts @@ -0,0 +1,641 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_sync = { + Arc: { + kind: "struct", + members: { + allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + as_ptr: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + assume_init: { kind: "function", metadata: { since: "1.82.0" } }, + clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + decrement_strong_count: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + decrement_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + downcast: { kind: "function", metadata: { since: "1.29.0" } }, + downcast_unchecked: { kind: "function", metadata: { since: "unstable" } }, + downgrade: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + get_mut_unchecked: { kind: "function", associated: true, metadata: { since: "unstable" } }, + increment_strong_count: { kind: "function", associated: true, metadata: { since: "1.51.0" } }, + increment_strong_count_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_array: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + into_raw: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + into_raw_with_allocator: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_unique: { kind: "function", associated: true, metadata: { since: "unstable" } }, + make_mut: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + new_cyclic: { kind: "function", associated: true, metadata: { since: "1.60.0" } }, + new_cyclic_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_uninit_slice: { kind: "function", associated: true, metadata: { since: "1.82.0" } }, + new_uninit_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_zeroed_slice: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + new_zeroed_slice_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + pin: { kind: "function", associated: true, metadata: { since: "1.33.0" } }, + pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", associated: true, metadata: { since: "1.17.0" } }, + strong_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + try_clone_from_ref: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_clone_from_ref_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_uninit_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_new_zeroed_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_pin: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_pin_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_unwrap: { kind: "function", associated: true, metadata: { since: "1.4.0" } }, + unwrap_or_clone: { kind: "function", associated: true, metadata: { since: "1.76.0" } }, + weak_count: { kind: "function", associated: true, metadata: { since: "1.15.0" } }, + }, + }, + Barrier: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + wait: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + BarrierWaitResult: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + is_leader: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + Condvar: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + notify_all: { kind: "function", metadata: { since: "1.0.0" } }, + notify_one: { kind: "function", metadata: { since: "1.0.0" } }, + wait: { kind: "function", metadata: { since: "1.0.0" } }, + wait_timeout: { kind: "function", metadata: { since: "1.5.0" } }, + wait_timeout_ms: { kind: "function", metadata: { since: "1.0.0" } }, + wait_timeout_while: { kind: "function", metadata: { since: "1.42.0" } }, + wait_while: { kind: "function", metadata: { since: "1.42.0" } }, + }, + }, + Exclusive: { + kind: "struct", + members: { + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_pin_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "unstable" } }, + get_pin_mut: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + LazyLock: { + kind: "struct", metadata: { since: "1.80.0" }, + members: { + force: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + force_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + get_mut: { kind: "function", associated: true, metadata: { since: "1.94.0" } }, + into_inner: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.80.0" } }, + }, + }, + LockResult: { kind: "type-alias", metadata: { since: "1.0.0" } }, + MappedMutexGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockReadGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockWriteGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Mutex: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + clear_poison: { kind: "function", metadata: { since: "1.77.0" } }, + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "1.6.0" } }, + into_inner: { kind: "function", metadata: { since: "1.6.0" } }, + is_poisoned: { kind: "function", metadata: { since: "1.2.0" } }, + lock: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_lock: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + MutexGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Once: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + call_once: { kind: "function", metadata: { since: "1.0.0" } }, + call_once_force: { kind: "function", metadata: { since: "1.51.0" } }, + is_completed: { kind: "function", metadata: { since: "1.43.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.2.0" } }, + wait: { kind: "function", metadata: { since: "1.86.0" } }, + wait_force: { kind: "function", metadata: { since: "1.86.0" } }, + }, + }, + ONCE_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + OnceLock: { + kind: "struct", metadata: { since: "1.70.0" }, + members: { + get: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut: { kind: "function", metadata: { since: "1.70.0" } }, + get_mut_or_init: { kind: "function", metadata: { since: "unstable" } }, + get_mut_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + get_or_init: { kind: "function", metadata: { since: "1.70.0" } }, + get_or_try_init: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "1.70.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.70.0" } }, + set: { kind: "function", metadata: { since: "1.70.0" } }, + take: { kind: "function", metadata: { since: "1.70.0" } }, + try_insert: { kind: "function", metadata: { since: "unstable" } }, + wait: { kind: "function", metadata: { since: "1.86.0" } }, + }, + }, + OnceState: { + kind: "struct", metadata: { since: "1.51.0" }, + members: { + is_poisoned: { kind: "function", metadata: { since: "1.51.0" } }, + }, + }, + PoisonError: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.2.0" } }, + get_ref: { kind: "function", metadata: { since: "1.2.0" } }, + into_inner: { kind: "function", metadata: { since: "1.2.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.2.0" } }, + }, + }, + ReentrantLock: { + kind: "struct", metadata: { since: "unstable" }, + members: { + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function" }, + into_inner: { kind: "function" }, + lock: { kind: "function" }, + new: { kind: "function", associated: true }, + }, + }, + ReentrantLockGuard: { kind: "struct", metadata: { since: "unstable" } }, + RwLock: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + clear_poison: { kind: "function", metadata: { since: "1.77.0" } }, + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "1.6.0" } }, + into_inner: { kind: "function", metadata: { since: "1.6.0" } }, + is_poisoned: { kind: "function", metadata: { since: "1.2.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + read: { kind: "function", metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_read: { kind: "function", metadata: { since: "1.0.0" } }, + try_write: { kind: "function", metadata: { since: "1.0.0" } }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + RwLockReadGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RwLockWriteGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + TryLockError: { kind: "enum", metadata: { since: "1.0.0" } }, + TryLockResult: { kind: "type-alias", metadata: { since: "1.0.0" } }, + UniqueArc: { + kind: "struct", + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_arc: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + WaitTimeoutResult: { + kind: "struct", metadata: { since: "1.5.0" }, + members: { + timed_out: { kind: "function", metadata: { since: "1.5.0" } }, + }, + }, + Weak: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.45.0" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.45.0" } }, + from_raw_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_raw: { kind: "function", metadata: { since: "1.45.0" } }, + into_raw_with_allocator: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "1.10.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + ptr_eq: { kind: "function", metadata: { since: "1.39.0" } }, + strong_count: { kind: "function", metadata: { since: "1.41.0" } }, + upgrade: { kind: "function", metadata: { since: "1.4.0" } }, + weak_count: { kind: "function", metadata: { since: "1.41.0" } }, + }, + }, +} as const satisfies Record; + +export const mod_sync_atomic = { + Atomic: { + kind: "struct", + members: { + as_ptr: { kind: "function", metadata: { since: "1.70.0" } }, + compare_and_swap: { kind: "function", metadata: { since: "1.0.0" } }, + compare_exchange: { kind: "function", metadata: { since: "1.10.0" } }, + compare_exchange_weak: { kind: "function", metadata: { since: "1.10.0" } }, + fetch_add: { kind: "function", metadata: { since: "1.34.0" } }, + fetch_and: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_byte_add: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_byte_sub: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_max: { kind: "function", metadata: { since: "1.45.0" } }, + fetch_min: { kind: "function", metadata: { since: "1.45.0" } }, + fetch_nand: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_not: { kind: "function", metadata: { since: "1.81.0" } }, + fetch_or: { kind: "function", metadata: { since: "1.0.0" } }, + fetch_ptr_add: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_ptr_sub: { kind: "function", metadata: { since: "1.91.0" } }, + fetch_sub: { kind: "function", metadata: { since: "1.34.0" } }, + fetch_update: { kind: "function", metadata: { since: "1.53.0" } }, + fetch_xor: { kind: "function", metadata: { since: "1.0.0" } }, + from_mut: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_mut_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_ptr: { kind: "function", associated: true, metadata: { since: "1.75.0" } }, + get_mut: { kind: "function", metadata: { since: "1.15.0" } }, + get_mut_slice: { kind: "function", associated: true, metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "1.15.0" } }, + load: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + null: { kind: "function", associated: true, metadata: { since: "unstable" } }, + store: { kind: "function", metadata: { since: "1.0.0" } }, + swap: { kind: "function", metadata: { since: "1.0.0" } }, + try_update: { kind: "function", metadata: { since: "1.95.0" } }, + update: { kind: "function", metadata: { since: "1.95.0" } }, + }, + }, + ATOMIC_BOOL_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + ATOMIC_ISIZE_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + ATOMIC_USIZE_INIT: { kind: "const", metadata: { since: "1.0.0" } }, + AtomicBool: { kind: "type-alias" }, + AtomicI128: { kind: "type-alias", metadata: { since: "unstable" } }, + AtomicI16: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI32: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI64: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicI8: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicIsize: { kind: "type-alias" }, + AtomicPrimitive: { kind: "trait" }, + AtomicPtr: { kind: "type-alias" }, + AtomicU128: { kind: "type-alias", metadata: { since: "unstable" } }, + AtomicU16: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicU32: { kind: "type-alias", metadata: { since: "1.34.0" } }, + AtomicU64: { kind: "type-alias" }, + AtomicU8: { kind: "type-alias" }, + AtomicUsize: { kind: "type-alias" }, + compiler_fence: { kind: "function" }, + fence: { kind: "function" }, + Ordering: { kind: "enum" }, + spin_loop_hint: { kind: "function", metadata: { since: "1.24.0" } }, +} as const satisfies Record; + +export const mod_sync_mpmc = { + channel: { kind: "function", metadata: { since: "unstable" } }, + IntoIter: { kind: "struct", metadata: { since: "unstable" } }, + Iter: { kind: "struct", metadata: { since: "unstable" } }, + Receiver: { + kind: "struct", metadata: { since: "unstable" }, + members: { + capacity: { kind: "function", metadata: { since: "unstable" } }, + is_disconnected: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "unstable" } }, + is_full: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function", metadata: { since: "unstable" } }, + len: { kind: "function", metadata: { since: "unstable" } }, + recv: { kind: "function", metadata: { since: "unstable" } }, + recv_deadline: { kind: "function", metadata: { since: "unstable" } }, + recv_timeout: { kind: "function", metadata: { since: "unstable" } }, + same_channel: { kind: "function", metadata: { since: "unstable" } }, + try_iter: { kind: "function", metadata: { since: "unstable" } }, + try_recv: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Sender: { + kind: "struct", metadata: { since: "unstable" }, + members: { + capacity: { kind: "function", metadata: { since: "unstable" } }, + is_disconnected: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "unstable" } }, + is_full: { kind: "function", metadata: { since: "unstable" } }, + len: { kind: "function", metadata: { since: "unstable" } }, + same_channel: { kind: "function", metadata: { since: "unstable" } }, + send: { kind: "function", metadata: { since: "unstable" } }, + send_deadline: { kind: "function", metadata: { since: "unstable" } }, + send_timeout: { kind: "function", metadata: { since: "unstable" } }, + try_send: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + sync_channel: { kind: "function", metadata: { since: "unstable" } }, + TryIter: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; + +export const mod_sync_mpsc = { + channel: { kind: "function", metadata: { since: "1.0.0" } }, + IntoIter: { kind: "struct", metadata: { since: "1.1.0" } }, + Iter: { kind: "struct", metadata: { since: "1.0.0" } }, + Receiver: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + is_disconnected: { kind: "function", metadata: { since: "unstable" } }, + iter: { kind: "function", metadata: { since: "1.0.0" } }, + recv: { kind: "function", metadata: { since: "1.0.0" } }, + recv_deadline: { kind: "function", metadata: { since: "unstable" } }, + recv_timeout: { kind: "function", metadata: { since: "1.12.0" } }, + try_iter: { kind: "function", metadata: { since: "1.15.0" } }, + try_recv: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + RecvError: { kind: "struct", metadata: { since: "1.0.0" } }, + RecvTimeoutError: { kind: "enum", metadata: { since: "1.12.0" } }, + Sender: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + is_disconnected: { kind: "function", metadata: { since: "unstable" } }, + send: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + SendError: { kind: "struct", metadata: { since: "1.0.0" } }, + sync_channel: { kind: "function", metadata: { since: "1.0.0" } }, + SyncSender: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + send: { kind: "function", metadata: { since: "1.0.0" } }, + try_send: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + TryIter: { kind: "struct", metadata: { since: "1.15.0" } }, + TryRecvError: { kind: "enum", metadata: { since: "1.0.0" } }, + TrySendError: { kind: "enum", metadata: { since: "1.0.0" } }, +} as const satisfies Record; + +export const mod_sync_nonpoison = { + Condvar: { + kind: "struct", metadata: { since: "unstable" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + notify_all: { kind: "function", metadata: { since: "unstable" } }, + notify_one: { kind: "function", metadata: { since: "unstable" } }, + wait: { kind: "function", metadata: { since: "unstable" } }, + wait_timeout: { kind: "function", metadata: { since: "unstable" } }, + wait_timeout_while: { kind: "function", metadata: { since: "unstable" } }, + wait_while: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + MappedMutexGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockReadGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockWriteGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Mutex: { + kind: "struct", metadata: { since: "unstable" }, + members: { + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + lock: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_lock: { kind: "function", metadata: { since: "unstable" } }, + with_mut: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + MutexGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RwLock: { + kind: "struct", metadata: { since: "unstable" }, + members: { + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "unstable" } }, + into_inner: { kind: "function", metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + read: { kind: "function", metadata: { since: "unstable" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_read: { kind: "function", metadata: { since: "unstable" } }, + try_write: { kind: "function", metadata: { since: "unstable" } }, + with: { kind: "function", metadata: { since: "unstable" } }, + with_mut: { kind: "function", metadata: { since: "unstable" } }, + write: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RwLockReadGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RwLockWriteGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "unstable" } }, + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + TryLockResult: { kind: "type-alias", metadata: { since: "unstable" } }, + WouldBlock: { kind: "struct", metadata: { since: "unstable" } }, +} as const satisfies Record; + +export const mod_sync_oneshot = { + channel: { kind: "function", metadata: { since: "unstable" } }, + Receiver: { + kind: "struct", metadata: { since: "unstable" }, + members: { + recv: { kind: "function", metadata: { since: "unstable" } }, + recv_deadline: { kind: "function", metadata: { since: "unstable" } }, + recv_timeout: { kind: "function", metadata: { since: "unstable" } }, + try_recv: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + RecvTimeoutError: { kind: "enum", metadata: { since: "unstable" } }, + Sender: { + kind: "struct", metadata: { since: "unstable" }, + members: { + send: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + TryRecvError: { kind: "enum", metadata: { since: "unstable" } }, +} as const satisfies Record; + +export const mod_sync_poison = { + Condvar: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + notify_all: { kind: "function", metadata: { since: "1.0.0" } }, + notify_one: { kind: "function", metadata: { since: "1.0.0" } }, + wait: { kind: "function", metadata: { since: "1.0.0" } }, + wait_timeout: { kind: "function", metadata: { since: "1.5.0" } }, + wait_timeout_ms: { kind: "function", metadata: { since: "1.0.0" } }, + wait_timeout_while: { kind: "function", metadata: { since: "1.42.0" } }, + wait_while: { kind: "function", metadata: { since: "1.42.0" } }, + }, + }, + LockResult: { kind: "type-alias", metadata: { since: "1.0.0" } }, + MappedMutexGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockReadGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + MappedRwLockWriteGuard: { + kind: "struct", metadata: { since: "unstable" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Mutex: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + clear_poison: { kind: "function", metadata: { since: "1.77.0" } }, + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "1.6.0" } }, + into_inner: { kind: "function", metadata: { since: "1.6.0" } }, + is_poisoned: { kind: "function", metadata: { since: "1.2.0" } }, + lock: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_lock: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + MutexGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + PoisonError: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get_mut: { kind: "function", metadata: { since: "1.2.0" } }, + get_ref: { kind: "function", metadata: { since: "1.2.0" } }, + into_inner: { kind: "function", metadata: { since: "1.2.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.2.0" } }, + }, + }, + RwLock: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + clear_poison: { kind: "function", metadata: { since: "1.77.0" } }, + data_ptr: { kind: "function", metadata: { since: "unstable" } }, + get_cloned: { kind: "function", metadata: { since: "unstable" } }, + get_mut: { kind: "function", metadata: { since: "1.6.0" } }, + into_inner: { kind: "function", metadata: { since: "1.6.0" } }, + is_poisoned: { kind: "function", metadata: { since: "1.2.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + read: { kind: "function", metadata: { since: "1.0.0" } }, + replace: { kind: "function", metadata: { since: "unstable" } }, + set: { kind: "function", metadata: { since: "unstable" } }, + try_read: { kind: "function", metadata: { since: "1.0.0" } }, + try_write: { kind: "function", metadata: { since: "1.0.0" } }, + write: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + RwLockReadGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + RwLockWriteGuard: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + downgrade: { kind: "function", associated: true, metadata: { since: "1.92.0" } }, + filter_map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + map: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + TryLockError: { kind: "enum", metadata: { since: "1.0.0" } }, + TryLockResult: { kind: "type-alias", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/task.ts b/packages/rust/src/builtins/std/task.ts new file mode 100644 index 000000000..c86155c7a --- /dev/null +++ b/packages/rust/src/builtins/std/task.ts @@ -0,0 +1,82 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_task = { + Context: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + ext: { kind: "function", metadata: { since: "unstable" } }, + from_waker: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + local_waker: { kind: "function", metadata: { since: "unstable" } }, + waker: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, + ContextBuilder: { + kind: "struct", metadata: { since: "unstable" }, + members: { + build: { kind: "function", metadata: { since: "unstable" } }, + ext: { kind: "function", metadata: { since: "unstable" } }, + from: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_waker: { kind: "function", associated: true, metadata: { since: "unstable" } }, + local_waker: { kind: "function", metadata: { since: "unstable" } }, + waker: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + local_waker_fn: { kind: "function", metadata: { since: "unstable" } }, + LocalWake: { kind: "trait", metadata: { since: "unstable" } }, + LocalWaker: { + kind: "struct", metadata: { since: "unstable" }, + members: { + data: { kind: "function", metadata: { since: "unstable" } }, + from_fn_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + new: { kind: "function", associated: true, metadata: { since: "unstable" } }, + noop: { kind: "function", associated: true, metadata: { since: "unstable" } }, + vtable: { kind: "function", metadata: { since: "unstable" } }, + wake: { kind: "function", metadata: { since: "unstable" } }, + wake_by_ref: { kind: "function", metadata: { since: "unstable" } }, + will_wake: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + Poll: { + kind: "enum", metadata: { since: "1.36.0" }, + members: { + is_pending: { kind: "function", metadata: { since: "1.49.0" } }, + is_ready: { kind: "function", metadata: { since: "1.49.0" } }, + map: { kind: "function", metadata: { since: "1.36.0" } }, + map_err: { kind: "function", metadata: { since: "1.36.0" } }, + map_ok: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, + RawWaker: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + RawWakerVTable: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + new: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + }, + }, + ready: { kind: "symbol", metadata: { since: "1.64.0" } }, + Wake: { kind: "trait", metadata: { since: "1.51.0" } }, + Waker: { + kind: "struct", metadata: { since: "1.36.0" }, + members: { + data: { kind: "function", metadata: { since: "1.83.0" } }, + from_fn_ptr: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw: { kind: "function", associated: true, metadata: { since: "1.36.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.83.0" } }, + noop: { kind: "function", associated: true, metadata: { since: "1.85.0" } }, + vtable: { kind: "function", metadata: { since: "1.83.0" } }, + wake: { kind: "function", metadata: { since: "1.36.0" } }, + wake_by_ref: { kind: "function", metadata: { since: "1.36.0" } }, + will_wake: { kind: "function", metadata: { since: "1.36.0" } }, + }, + }, + waker_fn: { kind: "function", metadata: { since: "unstable" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/thread.ts b/packages/rust/src/builtins/std/thread.ts new file mode 100644 index 000000000..e99a32ed8 --- /dev/null +++ b/packages/rust/src/builtins/std/thread.ts @@ -0,0 +1,87 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_thread = { + AccessError: { kind: "struct", metadata: { since: "1.26.0" } }, + add_spawn_hook: { kind: "function", metadata: { since: "unstable" } }, + available_parallelism: { kind: "function", metadata: { since: "1.59.0" } }, + Builder: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + name: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + no_hooks: { kind: "function", metadata: { since: "unstable" } }, + spawn: { kind: "function", metadata: { since: "1.0.0" } }, + spawn_scoped: { kind: "function", metadata: { since: "1.63.0" } }, + spawn_unchecked: { kind: "function", metadata: { since: "1.82.0" } }, + stack_size: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + current: { kind: "function", metadata: { since: "1.0.0" } }, + current_id: { kind: "function", metadata: { since: "unstable" } }, + JoinHandle: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + is_finished: { kind: "function", metadata: { since: "1.61.0" } }, + join: { kind: "function", metadata: { since: "1.0.0" } }, + thread: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + LocalKey: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + get: { kind: "function", metadata: { since: "1.73.0" } }, + replace: { kind: "function", metadata: { since: "1.73.0" } }, + set: { kind: "function", metadata: { since: "1.73.0" } }, + take: { kind: "function", metadata: { since: "1.73.0" } }, + try_with: { kind: "function", metadata: { since: "1.26.0" } }, + update: { kind: "function", metadata: { since: "unstable" } }, + with: { kind: "function", metadata: { since: "1.0.0" } }, + with_borrow: { kind: "function", metadata: { since: "1.73.0" } }, + with_borrow_mut: { kind: "function", metadata: { since: "1.73.0" } }, + }, + }, + panicking: { kind: "function", metadata: { since: "1.0.0" } }, + park: { kind: "function", metadata: { since: "1.0.0" } }, + park_timeout: { kind: "function", metadata: { since: "1.4.0" } }, + park_timeout_ms: { kind: "function", metadata: { since: "1.0.0" } }, + Result: { kind: "type-alias", metadata: { since: "1.0.0" } }, + scope: { kind: "function", metadata: { since: "1.63.0" } }, + Scope: { + kind: "struct", metadata: { since: "1.63.0" }, + members: { + spawn: { kind: "function", metadata: { since: "1.63.0" } }, + }, + }, + ScopedJoinHandle: { + kind: "struct", metadata: { since: "1.63.0" }, + members: { + is_finished: { kind: "function", metadata: { since: "1.63.0" } }, + join: { kind: "function", metadata: { since: "1.63.0" } }, + thread: { kind: "function", metadata: { since: "1.63.0" } }, + }, + }, + sleep: { kind: "function", metadata: { since: "1.4.0" } }, + sleep_ms: { kind: "function", metadata: { since: "1.0.0" } }, + sleep_until: { kind: "function", metadata: { since: "unstable" } }, + spawn: { kind: "function", metadata: { since: "1.0.0" } }, + Thread: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + from_raw: { kind: "function", associated: true, metadata: { since: "unstable" } }, + id: { kind: "function", metadata: { since: "1.19.0" } }, + into_raw: { kind: "function", metadata: { since: "unstable" } }, + name: { kind: "function", metadata: { since: "1.0.0" } }, + unpark: { kind: "function", metadata: { since: "1.0.0" } }, + }, + }, + ThreadId: { + kind: "struct", metadata: { since: "1.19.0" }, + members: { + as_u64: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + yield_now: { kind: "function", metadata: { since: "1.0.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/time.ts b/packages/rust/src/builtins/std/time.ts new file mode 100644 index 000000000..a6765679b --- /dev/null +++ b/packages/rust/src/builtins/std/time.ts @@ -0,0 +1,87 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_time = { + Duration: { + kind: "struct", + members: { + abs_diff: { kind: "function", metadata: { since: "1.81.0" } }, + as_micros: { kind: "function", metadata: { since: "1.33.0" } }, + as_millis: { kind: "function", metadata: { since: "1.33.0" } }, + as_millis_f32: { kind: "function", metadata: { since: "unstable" } }, + as_millis_f64: { kind: "function", metadata: { since: "unstable" } }, + as_nanos: { kind: "function", metadata: { since: "1.33.0" } }, + as_secs: { kind: "function", metadata: { since: "1.3.0" } }, + as_secs_f32: { kind: "function", metadata: { since: "1.38.0" } }, + as_secs_f64: { kind: "function", metadata: { since: "1.38.0" } }, + checked_add: { kind: "function", metadata: { since: "1.16.0" } }, + checked_div: { kind: "function", metadata: { since: "1.16.0" } }, + checked_mul: { kind: "function", metadata: { since: "1.16.0" } }, + checked_sub: { kind: "function", metadata: { since: "1.16.0" } }, + div_duration_ceil: { kind: "function", metadata: { since: "unstable" } }, + div_duration_f32: { kind: "function", metadata: { since: "1.80.0" } }, + div_duration_f64: { kind: "function", metadata: { since: "1.80.0" } }, + div_duration_floor: { kind: "function", metadata: { since: "unstable" } }, + div_f32: { kind: "function", metadata: { since: "1.38.0" } }, + div_f64: { kind: "function", metadata: { since: "1.38.0" } }, + from_days: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_hours: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_micros: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + from_millis: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + from_mins: { kind: "function", associated: true, metadata: { since: "1.91.0" } }, + from_nanos: { kind: "function", associated: true, metadata: { since: "1.27.0" } }, + from_nanos_u128: { kind: "function", associated: true, metadata: { since: "1.93.0" } }, + from_secs: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + from_secs_f32: { kind: "function", associated: true, metadata: { since: "1.38.0" } }, + from_secs_f64: { kind: "function", associated: true, metadata: { since: "1.38.0" } }, + from_weeks: { kind: "function", associated: true, metadata: { since: "unstable" } }, + is_zero: { kind: "function", metadata: { since: "1.53.0" } }, + mul_f32: { kind: "function", metadata: { since: "1.38.0" } }, + mul_f64: { kind: "function", metadata: { since: "1.38.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.3.0" } }, + saturating_add: { kind: "function", metadata: { since: "1.53.0" } }, + saturating_mul: { kind: "function", metadata: { since: "1.53.0" } }, + saturating_sub: { kind: "function", metadata: { since: "1.53.0" } }, + subsec_micros: { kind: "function", metadata: { since: "1.27.0" } }, + subsec_millis: { kind: "function", metadata: { since: "1.27.0" } }, + subsec_nanos: { kind: "function", metadata: { since: "1.3.0" } }, + try_from_secs_f32: { kind: "function", associated: true, metadata: { since: "1.66.0" } }, + try_from_secs_f64: { kind: "function", associated: true, metadata: { since: "1.66.0" } }, + }, + }, + Instant: { + kind: "struct", metadata: { since: "1.8.0" }, + members: { + checked_add: { kind: "function", metadata: { since: "1.34.0" } }, + checked_duration_since: { kind: "function", metadata: { since: "1.39.0" } }, + checked_sub: { kind: "function", metadata: { since: "1.34.0" } }, + duration_since: { kind: "function", metadata: { since: "1.8.0" } }, + elapsed: { kind: "function", metadata: { since: "1.8.0" } }, + now: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + saturating_duration_since: { kind: "function", metadata: { since: "1.39.0" } }, + }, + }, + SystemTime: { + kind: "struct", metadata: { since: "1.8.0" }, + members: { + checked_add: { kind: "function", metadata: { since: "1.34.0" } }, + checked_sub: { kind: "function", metadata: { since: "1.34.0" } }, + duration_since: { kind: "function", metadata: { since: "1.8.0" } }, + elapsed: { kind: "function", metadata: { since: "1.8.0" } }, + now: { kind: "function", associated: true, metadata: { since: "1.8.0" } }, + saturating_add: { kind: "function", metadata: { since: "unstable" } }, + saturating_duration_since: { kind: "function", metadata: { since: "unstable" } }, + saturating_sub: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + SystemTimeError: { + kind: "struct", metadata: { since: "1.8.0" }, + members: { + duration: { kind: "function", metadata: { since: "1.8.0" } }, + }, + }, + TryFromFloatSecsError: { kind: "struct" }, + UNIX_EPOCH: { kind: "const", metadata: { since: "1.8.0" } }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/ub_checks.ts b/packages/rust/src/builtins/std/ub_checks.ts new file mode 100644 index 000000000..33b0660b6 --- /dev/null +++ b/packages/rust/src/builtins/std/ub_checks.ts @@ -0,0 +1,9 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_ub_checks = { + assert_unsafe_precondition: { kind: "symbol", metadata: { since: "unstable" } }, + check_library_ub: { kind: "function" }, +} as const satisfies Record; diff --git a/packages/rust/src/builtins/std/vec.ts b/packages/rust/src/builtins/std/vec.ts new file mode 100644 index 000000000..c8ddce7f4 --- /dev/null +++ b/packages/rust/src/builtins/std/vec.ts @@ -0,0 +1,110 @@ +import type { SymbolDescriptor } from "../../create-crate.js"; + +// Generated by scripts/generate-crate-descriptor.ts +// Source: rustdoc JSON format version 57 + +export const mod_vec = { + Drain: { + kind: "struct", metadata: { since: "1.6.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_slice: { kind: "function", metadata: { since: "1.46.0" } }, + keep_rest: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + ExtractIf: { + kind: "struct", metadata: { since: "1.87.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + }, + }, + IntoIter: { + kind: "struct", metadata: { since: "1.0.0" }, + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.15.0" } }, + as_slice: { kind: "function", metadata: { since: "1.15.0" } }, + }, + }, + PeekMut: { + kind: "struct", metadata: { since: "unstable" }, + members: { + pop: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, + Recyclable: { kind: "trait" }, + Splice: { kind: "struct", metadata: { since: "1.21.0" } }, + Vec: { + kind: "struct", + members: { + allocator: { kind: "function", metadata: { since: "unstable" } }, + append: { kind: "function", metadata: { since: "1.4.0" } }, + as_mut_ptr: { kind: "function", metadata: { since: "1.37.0" } }, + as_mut_slice: { kind: "function", metadata: { since: "1.7.0" } }, + as_non_null: { kind: "function", metadata: { since: "unstable" } }, + as_ptr: { kind: "function", metadata: { since: "1.37.0" } }, + as_slice: { kind: "function", metadata: { since: "1.7.0" } }, + capacity: { kind: "function", metadata: { since: "1.0.0" } }, + clear: { kind: "function", metadata: { since: "1.0.0" } }, + const_make_global: { kind: "function", metadata: { since: "unstable" } }, + dedup: { kind: "function", metadata: { since: "1.0.0" } }, + dedup_by: { kind: "function", metadata: { since: "1.16.0" } }, + dedup_by_key: { kind: "function", metadata: { since: "1.16.0" } }, + drain: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_slice: { kind: "function", metadata: { since: "1.6.0" } }, + extend_from_within: { kind: "function", metadata: { since: "1.53.0" } }, + extract_if: { kind: "function", metadata: { since: "1.87.0" } }, + from_fn: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_parts: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_parts_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + from_raw_parts: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + from_raw_parts_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + insert: { kind: "function", metadata: { since: "1.0.0" } }, + insert_mut: { kind: "function", metadata: { since: "1.95.0" } }, + into_boxed_slice: { kind: "function", metadata: { since: "1.0.0" } }, + into_chunks: { kind: "function", metadata: { since: "unstable" } }, + into_flattened: { kind: "function", metadata: { since: "1.80.0" } }, + into_parts: { kind: "function", metadata: { since: "unstable" } }, + into_parts_with_alloc: { kind: "function", metadata: { since: "unstable" } }, + into_raw_parts: { kind: "function", metadata: { since: "1.93.0" } }, + into_raw_parts_with_alloc: { kind: "function", metadata: { since: "unstable" } }, + is_empty: { kind: "function", metadata: { since: "1.0.0" } }, + leak: { kind: "function", metadata: { since: "1.47.0" } }, + len: { kind: "function", metadata: { since: "1.0.0" } }, + new: { kind: "function", associated: true, metadata: { since: "1.39.0" } }, + new_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + peek_mut: { kind: "function", metadata: { since: "unstable" } }, + pop: { kind: "function", metadata: { since: "1.0.0" } }, + pop_if: { kind: "function", metadata: { since: "1.86.0" } }, + push: { kind: "function", metadata: { since: "1.0.0" } }, + push_mut: { kind: "function", metadata: { since: "1.95.0" } }, + push_within_capacity: { kind: "function", metadata: { since: "unstable" } }, + recycle: { kind: "function", metadata: { since: "unstable" } }, + remove: { kind: "function", metadata: { since: "1.0.0" } }, + reserve: { kind: "function", metadata: { since: "1.0.0" } }, + reserve_exact: { kind: "function", metadata: { since: "1.0.0" } }, + resize: { kind: "function", metadata: { since: "1.5.0" } }, + resize_with: { kind: "function", metadata: { since: "1.33.0" } }, + retain: { kind: "function", metadata: { since: "1.0.0" } }, + retain_mut: { kind: "function", metadata: { since: "1.61.0" } }, + set_len: { kind: "function", metadata: { since: "1.0.0" } }, + shrink_to: { kind: "function", metadata: { since: "1.56.0" } }, + shrink_to_fit: { kind: "function", metadata: { since: "1.0.0" } }, + spare_capacity_mut: { kind: "function", metadata: { since: "1.60.0" } }, + splice: { kind: "function", metadata: { since: "1.21.0" } }, + split_at_spare_mut: { kind: "function", metadata: { since: "unstable" } }, + split_off: { kind: "function", metadata: { since: "1.4.0" } }, + swap_remove: { kind: "function", metadata: { since: "1.0.0" } }, + truncate: { kind: "function", metadata: { since: "1.0.0" } }, + try_remove: { kind: "function", metadata: { since: "unstable" } }, + try_reserve: { kind: "function", metadata: { since: "1.57.0" } }, + try_reserve_exact: { kind: "function", metadata: { since: "1.57.0" } }, + try_shrink_to: { kind: "function", metadata: { since: "unstable" } }, + try_shrink_to_fit: { kind: "function", metadata: { since: "unstable" } }, + try_with_capacity: { kind: "function", associated: true, metadata: { since: "unstable" } }, + try_with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + with_capacity: { kind: "function", associated: true, metadata: { since: "1.0.0" } }, + with_capacity_in: { kind: "function", associated: true, metadata: { since: "unstable" } }, + }, + }, +} as const satisfies Record; diff --git a/packages/rust/src/components/associated-type.tsx b/packages/rust/src/components/associated-type.tsx new file mode 100644 index 000000000..2f6f2a183 --- /dev/null +++ b/packages/rust/src/components/associated-type.tsx @@ -0,0 +1,39 @@ +import { + Children, + Declaration as CoreDeclaration, + Namekey, + Refkey, +} from "@alloy-js/core"; +import { createAssociatedTypeSymbol } from "../symbols/factories.js"; + +export interface AssociatedTypeProps { + name: string | Namekey; + refkey?: Refkey; + constraint?: Children; + children?: Children; +} + +export function AssociatedType(props: AssociatedTypeProps) { + const associatedTypeSymbol = createAssociatedTypeSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + + return ( + + {"type "} + {associatedTypeSymbol.name} + {props.children ? + <> + {" = "} + {props.children} + + : props.constraint ? + <> + {": "} + {props.constraint} + + : null} + {";"} + + ); +} diff --git a/packages/rust/src/components/attribute.tsx b/packages/rust/src/components/attribute.tsx new file mode 100644 index 000000000..56d55c3f3 --- /dev/null +++ b/packages/rust/src/components/attribute.tsx @@ -0,0 +1,64 @@ +import { Children, For, Refkey } from "@alloy-js/core"; +import { Reference } from "./reference.js"; + +export interface AttributeProps { + name: string | Refkey; + args?: Children; +} + +export interface InnerAttributeProps { + name: string | Refkey; + args?: Children; +} + +export interface DeriveAttributeProps { + traits: (string | Refkey)[]; +} + +export function Attribute(props: AttributeProps) { + return ; +} + +export function InnerAttribute(props: InnerAttributeProps) { + return ; +} + +interface AttributeBaseProps { + marker: "#[" | "#!["; + name: string | Refkey; + args?: Children; +} + +function AttributeBase(props: AttributeBaseProps) { + return ( + <> + {props.marker} + {typeof props.name === "string" ? + props.name + : } + {props.args !== undefined ? + <> + {"("} + {props.args} + {")"} + + : null} + {"]"} + + ); +} + +export function DeriveAttribute(props: DeriveAttributeProps) { + return ( + + {(item) => + typeof item === "string" ? item : + } + + } + /> + ); +} diff --git a/packages/rust/src/components/await-expression.tsx b/packages/rust/src/components/await-expression.tsx new file mode 100644 index 000000000..1ce2ed91c --- /dev/null +++ b/packages/rust/src/components/await-expression.tsx @@ -0,0 +1,16 @@ +import { Children } from "@alloy-js/core"; + +export interface AwaitExpressionProps { + try?: boolean; + children: Children; +} + +export function AwaitExpression(props: AwaitExpressionProps) { + return ( + <> + {props.children} + {".await"} + {props.try ? "?" : ""} + + ); +} diff --git a/packages/rust/src/components/block-expression.tsx b/packages/rust/src/components/block-expression.tsx new file mode 100644 index 000000000..b8c62ef34 --- /dev/null +++ b/packages/rust/src/components/block-expression.tsx @@ -0,0 +1,63 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface BlockExpressionProps { + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +export function BlockExpression(props: BlockExpressionProps) { + const statements = normalizeChildren(props.children); + const precedingStatements = statements.slice(0, -1); + const lastExpression = statements.at(-1); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + {precedingStatements.length > 0 ? + <> + }> + {(statement) => statement} + + + + : null} + {lastExpression} + + + + : null} + {"}"} + + ); +} diff --git a/packages/rust/src/components/break-expression.tsx b/packages/rust/src/components/break-expression.tsx new file mode 100644 index 000000000..517f7fb93 --- /dev/null +++ b/packages/rust/src/components/break-expression.tsx @@ -0,0 +1,20 @@ +import { Children } from "@alloy-js/core"; + +export interface BreakExpressionProps { + label?: string; + children?: Children; +} + +export function BreakExpression(props: BreakExpressionProps) { + return ( + <> + {"break"} + {props.label ? + <> {props.label} + : null} + {typeof props.children !== "undefined" ? + <> {props.children} + : null} + + ); +} diff --git a/packages/rust/src/components/cargo-toml-file.tsx b/packages/rust/src/components/cargo-toml-file.tsx new file mode 100644 index 000000000..dfc6a57e1 --- /dev/null +++ b/packages/rust/src/components/cargo-toml-file.tsx @@ -0,0 +1,78 @@ +import { SourceFile, memo } from "@alloy-js/core"; +import { useCrateContext } from "../context/crate-context.js"; +import type { CrateDependency } from "../scopes/rust-crate-scope.js"; + +export type { CrateDependency }; + +export interface CargoTomlFileProps { + name: string; + version?: string; + edition?: string; + dependencies?: Record; +} + +function renderDependency(dependency: CrateDependency): string { + if (typeof dependency === "string") { + return `"${dependency}"`; + } + + if (!dependency.features || dependency.features.length === 0) { + return `{ version = "${dependency.version}" }`; + } + + const sortedFeatures = [...dependency.features].sort((left, right) => + left.localeCompare(right), + ); + return `{ version = "${dependency.version}", features = ["${sortedFeatures.join('", "')}"] }`; +} + +export function CargoTomlFile(props: CargoTomlFileProps) { + const crate = useCrateContext(); + const cargoToml = memo(() => { + const mergedDependencies = new Map(); + + if (crate) { + for (const [name, dependency] of crate.scope.dependencies) { + mergedDependencies.set(name, dependency); + } + } + + for (const [name, dependency] of Object.entries(props.dependencies ?? {})) { + mergedDependencies.set(name, dependency); + } + + const sortedDependencies = [...mergedDependencies.entries()].sort( + ([left], [right]) => left.localeCompare(right), + ); + const crateType = crate?.crateType ?? "lib"; + const crateName = crate?.name ?? props.name; + const targetLines = + crateType === "bin" ? + ["[[bin]]", `name = "${crateName}"`, 'path = "main.rs"'] + : ["[lib]", 'path = "lib.rs"']; + + const lines = [ + "[package]", + `name = "${props.name}"`, + `version = "${props.version ?? "0.1.0"}"`, + `edition = "${props.edition ?? "2021"}"`, + "", + ...targetLines, + ]; + + if (sortedDependencies.length > 0) { + lines.push("", "[dependencies]"); + for (const [name, dependency] of sortedDependencies) { + lines.push(`${name} = ${renderDependency(dependency)}`); + } + } + + return lines.join("\n"); + }); + + return ( + + {cargoToml} + + ); +} diff --git a/packages/rust/src/components/closure-expression.tsx b/packages/rust/src/components/closure-expression.tsx new file mode 100644 index 000000000..a823e5d3f --- /dev/null +++ b/packages/rust/src/components/closure-expression.tsx @@ -0,0 +1,103 @@ +import { Children, For, Indent, isComponentCreator } from "@alloy-js/core"; + +export interface ClosureParameter { + name: string; + type?: Children; +} + +export interface ClosureExpressionProps { + parameters?: ClosureParameter[]; + move?: boolean; + returnType?: Children; + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function shouldRenderBlock(statements: Children[], hasReturnType: boolean) { + if (hasReturnType || statements.length !== 1) { + return true; + } + + return isComponentCreator(statements[0]); +} + +function renderBlock(children: Children[]) { + return ( + <> + {"{"} + {children.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function ClosureExpression(props: ClosureExpressionProps) { + const parameters = props.parameters ?? []; + const bodyStatements = normalizeChildren(props.children); + const hasReturnType = typeof props.returnType !== "undefined"; + const renderAsBlock = shouldRenderBlock(bodyStatements, hasReturnType); + + return ( + <> + {props.move ? "move " : ""} + {"|"} + + {(parameter) => ( + <> + {parameter.name} + {parameter.type ? + <>: {parameter.type} + : null} + + )} + + {"|"} + {hasReturnType ? + <> + {" "} + {"->"} {props.returnType} + + : null} + {renderAsBlock ? + <> {renderBlock(bodyStatements)} + : <> {bodyStatements[0]}} + + ); +} diff --git a/packages/rust/src/components/const-declaration.tsx b/packages/rust/src/components/const-declaration.tsx new file mode 100644 index 000000000..7db322eeb --- /dev/null +++ b/packages/rust/src/components/const-declaration.tsx @@ -0,0 +1,52 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Namekey, + Refkey, +} from "@alloy-js/core"; +import { createConstSymbol } from "../symbols/factories.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface ConstDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + attributes?: Children[]; + type: Children; + children?: Children; +} + +export function ConstDeclaration(props: ConstDeclarationProps) { + const constSymbol = createConstSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + + constSymbol.visibility = toRustVisibility(props.pub); + + return ( + <> + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + + {"const "} + {constSymbol.name} + {": "} + {props.type} + {" = "} + {props.children} + {";"} + + + ); +} diff --git a/packages/rust/src/components/continue-expression.tsx b/packages/rust/src/components/continue-expression.tsx new file mode 100644 index 000000000..c967e9359 --- /dev/null +++ b/packages/rust/src/components/continue-expression.tsx @@ -0,0 +1,14 @@ +export interface ContinueExpressionProps { + label?: string; +} + +export function ContinueExpression(props: ContinueExpressionProps) { + return ( + <> + {"continue"} + {props.label ? + <> {props.label} + : null} + + ); +} diff --git a/packages/rust/src/components/crate-directory.tsx b/packages/rust/src/components/crate-directory.tsx new file mode 100644 index 000000000..6a828aa17 --- /dev/null +++ b/packages/rust/src/components/crate-directory.tsx @@ -0,0 +1,51 @@ +import { + Scope, + SourceDirectory, + createScope, + type Children, +} from "@alloy-js/core"; +import { CrateContext, CrateContextValue } from "../context/crate-context.js"; +import { + RustCrateScope, + type CrateDependency, +} from "../scopes/rust-crate-scope.js"; +import { CargoTomlFile } from "./cargo-toml-file.js"; + +export interface CrateDirectoryProps { + name: string; + version?: string; + edition?: string; + crateType?: "lib" | "bin"; + dependencies?: Record; + includeCargoToml?: boolean; + children?: Children; +} + +export function CrateDirectory(props: CrateDirectoryProps) { + const scope = createScope(RustCrateScope, props.name, props.version); + const context: CrateContextValue = { + scope, + name: props.name, + version: props.version, + edition: props.edition ?? "2021", + crateType: props.crateType ?? "lib", + }; + + return ( + + + + {props.children} + {props.includeCargoToml ? + + : null} + + + + ); +} diff --git a/packages/rust/src/components/declaration.tsx b/packages/rust/src/components/declaration.tsx new file mode 100644 index 000000000..55f40fbb7 --- /dev/null +++ b/packages/rust/src/components/declaration.tsx @@ -0,0 +1,106 @@ +import { + Children, + Declaration as CoreDeclaration, + Refkey, + createSymbol, + useBinder, +} from "@alloy-js/core"; +import { RustElements, useRustNamePolicy } from "../name-policy.js"; +import { useRustScope } from "../scopes/contexts.js"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { + RustOutputSymbol, + RustSymbolKind, +} from "../symbols/rust-output-symbol.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface DeclarationProps extends RustVisibilityProps { + name: string; + refkey?: Refkey; + nameKind?: string; + children?: Children; +} + +const typeNameKinds = new Set([ + "struct", + "enum", + "trait", + "type-alias", + "type-parameter", +]); +const rustNameKinds = new Set([ + "function", + "method", + "struct", + "enum", + "enum-variant", + "trait", + "type-alias", + "type-parameter", + "field", + "variable", + "parameter", + "constant", + "module", +]); + +function toRustNameKind(nameKind: string | undefined): RustElements { + if (nameKind && rustNameKinds.has(nameKind as RustElements)) { + return nameKind as RustElements; + } + + return "variable"; +} + +function toRustSymbolKind(nameKind: RustElements): RustSymbolKind { + switch (nameKind) { + case "constant": + return "const"; + case "enum-variant": + return "variant"; + case "variable": + return "symbol"; + default: + return nameKind; + } +} + +export function Declaration(props: DeclarationProps) { + const scope = useRustScope(); + if ( + !(scope instanceof RustCrateScope) && + !(scope instanceof RustModuleScope) + ) { + throw new Error( + "Rust declaration components can only be created in crate or module scopes.", + ); + } + + const binder = scope.binder ?? useBinder(); + const rustNameKind = toRustNameKind(props.nameKind); + const symbol = createSymbol( + RustOutputSymbol, + props.name, + typeNameKinds.has(rustNameKind) ? scope.types : scope.values, + { + binder, + refkeys: props.refkey ? [props.refkey] : [], + namePolicy: useRustNamePolicy().for(rustNameKind), + symbolKind: toRustSymbolKind(rustNameKind), + visibility: toRustVisibility(props.pub), + metadata: props.nameKind ? { nameKind: props.nameKind } : undefined, + }, + ); + + return ( + + + {props.children} + + ); +} diff --git a/packages/rust/src/components/doc-comment.tsx b/packages/rust/src/components/doc-comment.tsx new file mode 100644 index 000000000..683213844 --- /dev/null +++ b/packages/rust/src/components/doc-comment.tsx @@ -0,0 +1,114 @@ +import { type Children } from "@alloy-js/core"; + +function renderCommentLines(children: Children, prefix: string) { + const lines = String(children).split("\n"); + return lines.map((line) => ( + <> + {prefix} + {line} + + + )); +} + +function renderBlockComment(children: Children, openDelim: string, closeDelim: string) { + const lines = String(children).split("\n"); + return ( + <> + {openDelim} + + {lines.map((line) => ( + <> + {" * "} + {line} + + + ))} + {" "} + {closeDelim} + + + ); +} + +function isEmptyChildren(children: Children): boolean { + return children === undefined || children === null || String(children).length === 0; +} + +// --- Line comments --- + +export interface LineCommentProps { + children?: Children; +} + +export function LineComment(props: LineCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderCommentLines(props.children, "// "); +} + +// --- Block comments --- + +export interface BlockCommentProps { + children?: Children; +} + +export function BlockComment(props: BlockCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderBlockComment(props.children, "/*", "*/"); +} + +// --- Outer doc comments (///) --- + +export interface DocCommentProps { + children?: Children; +} + +export function DocComment(props: DocCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderCommentLines(props.children, "/// "); +} + +// --- Inner doc comments (//!) --- + +export interface InnerDocCommentProps { + children?: Children; +} + +export function InnerDocComment(props: InnerDocCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderCommentLines(props.children, "//! "); +} + +// --- Outer block doc comments (/** */) --- + +export interface OuterBlockDocCommentProps { + children?: Children; +} + +export function OuterBlockDocComment(props: OuterBlockDocCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderBlockComment(props.children, "/**", "*/"); +} + +// --- Inner block doc comments (/*! */) --- + +export interface InnerBlockDocCommentProps { + children?: Children; +} + +export function InnerBlockDocComment(props: InnerBlockDocCommentProps) { + if (isEmptyChildren(props.children)) { + return <>; + } + return renderBlockComment(props.children, "/*!", "*/"); +} diff --git a/packages/rust/src/components/enum-declaration.tsx b/packages/rust/src/components/enum-declaration.tsx new file mode 100644 index 000000000..54af5b239 --- /dev/null +++ b/packages/rust/src/components/enum-declaration.tsx @@ -0,0 +1,193 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Indent, + Namekey, + Refkey, + Scope, + createScope, +} from "@alloy-js/core"; +import { RustImplScope, useRustScope } from "../scopes/index.js"; +import { + createEnumSymbol, + createTypeParameterSymbol, + createVariantSymbol, +} from "../symbols/factories.js"; +import { DocComment } from "./doc-comment.js"; +import { TypeParameterProp, TypeParameters } from "./type-parameters.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface EnumDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + derives?: (string | Refkey)[]; + attributes?: Children[]; + doc?: string; + typeParameters?: TypeParameterProp[]; + children?: Children; +} + +export interface EnumVariantProps { + name: string | Namekey; + refkey?: Refkey; + attributes?: Children[]; + doc?: string; + kind?: "unit" | "tuple" | "struct"; + fields?: Children[]; + children?: Children; +} + +function DeclareNamedTypeTypeParameters(props: { + typeParameters?: TypeParameterProp[]; +}) { + const params = props.typeParameters ?? []; + for (const param of params) { + if (param.name) { + createTypeParameterSymbol(param.name); + } + } + + return <>; +} + +export function EnumDeclaration(props: EnumDeclarationProps) { + const parentScope = useRustScope(); + const enumSymbol = createEnumSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + const enumScope = createScope(RustImplScope, enumSymbol, parentScope, { + binder: parentScope.binder, + }); + enumSymbol.visibility = toRustVisibility(props.pub); + const variants = + props.children ? + (Array.isArray(props.children) ? + props.children + : [props.children] + ).filter( + (child) => !(typeof child === "string" && child.trim().length === 0), + ) + : []; + + return ( + <> + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + {props.derives && props.derives.length > 0 ? + <> + {"#[derive("} + + {(derive) => derive} + + {")]"} + + + : null} + + + + + + {"enum "} + {enumSymbol.name} + + {variants.length > 0 ? + <> + {" {"} + + + }> + {(child) => child} + + + + + {"}"} + + : " {}"} + + + ); +} + +export function EnumVariant(props: EnumVariantProps) { + const variantSymbol = createVariantSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + + const tupleFields = (props.fields ?? []).filter( + (field) => !(typeof field === "string" && field.trim().length === 0), + ); + const members = + props.children ? + (Array.isArray(props.children) ? + props.children + : [props.children] + ).filter( + (child) => !(typeof child === "string" && child.trim().length === 0), + ) + : []; + const variantKind = + props.kind ?? + (tupleFields.length > 0 ? "tuple" + : members.length > 0 ? "struct" + : "unit"); + const tupleValues = tupleFields.length > 0 ? tupleFields : members; + + return ( + + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + {variantSymbol.name} + {variantKind === "tuple" && tupleValues.length > 0 ? + <> + {"("} + + {(field) => field} + + {"),"} + + : variantKind === "struct" ? + <> + {" {"} + + }> + {(child) => child} + + + + {"},"} + + : ","} + + ); +} diff --git a/packages/rust/src/components/for-expression.tsx b/packages/rust/src/components/for-expression.tsx new file mode 100644 index 000000000..41854a135 --- /dev/null +++ b/packages/rust/src/components/for-expression.tsx @@ -0,0 +1,75 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface ForExpressionProps { + pattern: Children; + iterator: Children; + label?: string; + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function renderBlock(children: Children | undefined) { + const statements = normalizeChildren(children); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function ForExpression(props: ForExpressionProps) { + return ( + <> + {props.label ? + <> + {props.label} + {": "} + + : null} + {"for "} + {props.pattern} + {" in "} + {props.iterator} {renderBlock(props.children)} + + ); +} diff --git a/packages/rust/src/components/function-call-expression.tsx b/packages/rust/src/components/function-call-expression.tsx new file mode 100644 index 000000000..16242f4f4 --- /dev/null +++ b/packages/rust/src/components/function-call-expression.tsx @@ -0,0 +1,35 @@ +import { Children, For, Indent, Wrap } from "@alloy-js/core"; + +export interface FunctionCallExpressionProps { + target: Children; + args?: Children[]; + typeArgs?: Children[]; +} + +export function FunctionCallExpression(props: FunctionCallExpressionProps) { + return ( + + {props.target} + {props.typeArgs && props.typeArgs.length > 0 ? + <> + {"::<"} + + {(typeArg) => typeArg} + + {">"} + + : null} + {"("} + 1} + with={Indent} + props={{ softline: true, trailingBreak: true }} + > + + {(arg) => arg} + + + {")"} + + ); +} diff --git a/packages/rust/src/components/function-declaration.tsx b/packages/rust/src/components/function-declaration.tsx new file mode 100644 index 000000000..ec644d072 --- /dev/null +++ b/packages/rust/src/components/function-declaration.tsx @@ -0,0 +1,141 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Indent, + Namekey, + Refkey, + Scope, + createScope, +} from "@alloy-js/core"; +import { ParameterDescriptor } from "../parameter-descriptor.js"; +import { + RustFunctionScope, + RustImplScope, + RustTraitScope, + useRustScope, +} from "../scopes/index.js"; +import { + createFunctionSymbol, + createMethodSymbol, +} from "../symbols/factories.js"; +import { DocComment } from "./doc-comment.js"; +import { Parameters } from "./parameters.js"; +import { + TypeParameterProp, + TypeParameters, + WhereClause, +} from "./type-parameters.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface FunctionDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + async?: boolean; + unsafe?: boolean; + const?: boolean; + parameters?: readonly ParameterDescriptor[]; + returnType?: Children; + typeParameters?: TypeParameterProp[]; + whereClause?: Children; + receiver?: "&self" | "&mut self" | "self" | "none"; + attributes?: Children[]; + doc?: string; + children?: Children; +} + +export function FunctionDeclaration(props: FunctionDeclarationProps) { + const parentScope = useRustScope(); + const isMethod = + parentScope instanceof RustImplScope || + parentScope instanceof RustTraitScope; + const effectiveReceiver = isMethod ? (props.receiver ?? "&self") : "none"; + const functionSymbol = + isMethod ? + createMethodSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }) + : createFunctionSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + const functionScope = createScope( + RustFunctionScope, + functionSymbol.name, + parentScope, + { + ownerSymbol: functionSymbol, + binder: parentScope.binder, + }, + ); + + functionSymbol.visibility = toRustVisibility(props.pub); + functionSymbol.isAsync = props.async ?? false; + functionSymbol.isUnsafe = props.unsafe ?? false; + functionSymbol.isConst = props.const ?? false; + functionSymbol.receiverType = + effectiveReceiver === "none" ? undefined : effectiveReceiver; + + return ( + <> + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + + {props.async ? "async " : ""} + {props.unsafe ? "unsafe " : ""} + {props.const ? "const " : ""} + {"fn "} + {functionSymbol.name} + + + {"("} + {effectiveReceiver !== "none" ? + <> + {effectiveReceiver} + {props.parameters && props.parameters.length > 0 ? ", " : ""} + + : null} + + {")"} + {props.returnType ? + <> + {" -> "} + {props.returnType} + + : null} + {props.whereClause ? + <> + {" "} + {props.whereClause} + + : null} + {props.children ? + <> + {" {"} + {props.children} + + {"}"} + + : parentScope instanceof RustTraitScope ? + ";" + : " {}"} + + + + ); +} diff --git a/packages/rust/src/components/if-expression.tsx b/packages/rust/src/components/if-expression.tsx new file mode 100644 index 000000000..d529876c3 --- /dev/null +++ b/packages/rust/src/components/if-expression.tsx @@ -0,0 +1,105 @@ +import { Children, For, Indent, isComponentCreator } from "@alloy-js/core"; + +export interface IfExpressionProps { + condition: Children; + children?: Children; +} + +export interface ElseIfClauseProps { + condition: Children; + children?: Children; +} + +export interface ElseClauseProps { + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function isClause(child: Children) { + if (!isComponentCreator(child)) { + return false; + } + + return child.component === ElseIfClause || child.component === ElseClause; +} + +function renderBlock(children: Children | undefined) { + const statements = normalizeChildren(children); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function IfExpression(props: IfExpressionProps) { + const children = normalizeChildren(props.children); + const bodyChildren = children.filter((child) => !isClause(child)); + const clauses = children.filter((child) => isClause(child)); + + return ( + <> + {"if "} + {props.condition} {renderBlock(bodyChildren)} + {clauses} + + ); +} + +export function ElseIfClause(props: ElseIfClauseProps) { + return ( + <> + {" else if "} + {props.condition} {renderBlock(props.children)} + + ); +} + +export function ElseClause(props: ElseClauseProps) { + return ( + <> + {" else "} + {renderBlock(props.children)} + + ); +} diff --git a/packages/rust/src/components/impl-block.tsx b/packages/rust/src/components/impl-block.tsx new file mode 100644 index 000000000..fb9557bab --- /dev/null +++ b/packages/rust/src/components/impl-block.tsx @@ -0,0 +1,184 @@ +import { + Children, + For, + Indent, + Refkey, + Scope, + code, + createScope, + createSymbol, + isRefkey, + unresolvedRefkey, +} from "@alloy-js/core"; +import { + RustImplScope, + RustModuleScope, + useRustScope, +} from "../scopes/index.js"; +import { NamedTypeSymbol } from "../symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../symbols/rust-output-symbol.js"; +import { Reference } from "./reference.js"; +import { + TypeParameterProp, + TypeParameters, + WhereClause, +} from "./type-parameters.js"; + +export type TypeParameterProps = TypeParameterProp; + +export interface ImplBlockProps { + type: Refkey | Children; + trait?: Refkey | Children; + typeParameters?: TypeParameterProps[]; + whereClause?: Children; + attributes?: Children[]; + children?: Children; +} + +function resolveTypeSymbolFromRefkey( + refkey: Refkey, + scope: ReturnType, +): NamedTypeSymbol | undefined { + const result = scope.binder?.resolveDeclarationByKey(scope, refkey).value; + return result?.symbol instanceof NamedTypeSymbol ? result.symbol : undefined; +} + +function resolveSymbolNameFromRefkey( + refkey: Refkey, + scope: ReturnType, +): string { + const result = scope.binder?.resolveDeclarationByKey(scope, refkey).value; + const symbol = result?.symbol as RustOutputSymbol | undefined; + return symbol?.name ?? unresolvedRefkey(refkey); +} + +function findTypeSymbolFromInline( + value: Children, + scope: ReturnType, +): NamedTypeSymbol | undefined { + if (typeof value !== "string") { + return undefined; + } + + const moduleScope = scope.enclosingModule; + if (!(moduleScope instanceof RustModuleScope)) { + return undefined; + } + + for (const symbol of moduleScope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === value) { + return symbol; + } + } + + return undefined; +} + +function inferredTypeParametersForImpl( + symbol: NamedTypeSymbol | undefined, +): TypeParameterProp[] { + if (!symbol) { + return []; + } + + const params: TypeParameterProp[] = []; + for (const typeParameter of symbol.typeParameters) { + if (typeParameter instanceof RustOutputSymbol) { + params.push({ name: typeParameter.name }); + } + } + + return params; +} + +function renderTypeWithInferredTypeParameters( + renderedType: Children, + inferredTypeParameters: TypeParameterProp[], +): Children { + if (typeof renderedType !== "string" || inferredTypeParameters.length === 0) { + return renderedType; + } + + const names = inferredTypeParameters + .map((param) => param.name) + .filter((name) => Boolean(name)); + if (names.length === 0) { + return renderedType; + } + + return code`${renderedType}<${names.join(", ")}>`; +} + +export function ImplBlock(props: ImplBlockProps) { + const parentScope = useRustScope(); + + const renderedType = + isRefkey(props.type) ? + resolveSymbolNameFromRefkey(props.type, parentScope) + : props.type; + const targetTypeSymbol = + isRefkey(props.type) ? + resolveTypeSymbolFromRefkey(props.type, parentScope) + : findTypeSymbolFromInline(props.type, parentScope); + + const implTargetSymbol = + targetTypeSymbol ?? + createSymbol(NamedTypeSymbol, "__impl_target__", undefined, "struct", { + binder: parentScope.binder, + symbolKind: "struct", + }); + + const renderedTrait = + props.trait && isRefkey(props.trait) ? + + : props.trait; + + const implScope = createScope(RustImplScope, implTargetSymbol, parentScope, { + binder: parentScope.binder, + }); + const inferredTypeParameters = + inferredTypeParametersForImpl(targetTypeSymbol); + const implTypeParameters = props.typeParameters ?? inferredTypeParameters; + const renderedTypeWithTypeParameters = renderTypeWithInferredTypeParameters( + renderedType, + inferredTypeParameters, + ); + + return ( + <> + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + {code`impl`} + {" "} + {renderedTrait ? + <> + {renderedTrait} + {code` for `} + + : null} + {renderedTypeWithTypeParameters} + {props.whereClause ? + <> + {" "} + {props.whereClause} + + : null} + {props.children ? + <> + {code` {`} + + {props.children} + + + {code`}`} + + : code` {}`} + + ); +} diff --git a/packages/rust/src/components/index.ts b/packages/rust/src/components/index.ts new file mode 100644 index 000000000..9c0a20b6d --- /dev/null +++ b/packages/rust/src/components/index.ts @@ -0,0 +1,41 @@ +export * from "./associated-type.js"; +export * from "./attribute.js"; +export * from "./await-expression.js"; +export * from "./block-expression.js"; +export * from "./break-expression.js"; +export * from "./cargo-toml-file.js"; +export * from "./closure-expression.js"; +export * from "./const-declaration.js"; +export * from "./continue-expression.js"; +export * from "./crate-directory.js"; +export * from "./declaration.js"; +export * from "./doc-comment.js"; +export * from "./enum-declaration.js"; +export * from "./for-expression.js"; +export * from "./function-call-expression.js"; +export * from "./function-declaration.js"; +export * from "./if-expression.js"; +export * from "./impl-block.js"; +export * from "./let-binding.js"; +export * from "./loop-expression.js"; +export * from "./macro-call.js"; +export * from "./match-expression.js"; +export * from "./method-chain-expression.js"; +export * from "./mod-declarations.js"; +export * from "./module-directory.js"; +export * from "./parameters.js"; +export * from "./reference.js"; +export * from "./return-expression.js"; +export * from "./source-file.js"; +export * from "./static-declaration.js"; +export * as stc from "./stc/index.js"; +export * from "./struct-declaration.js"; +export * from "./struct-expression.js"; +export * from "./trait-declaration.js"; +export * from "./try-expression.js"; +export * from "./type-alias.js"; +export * from "./type-parameters.js"; +export * from "./unsafe-block.js"; +export * from "./use-statement.js"; +export * from "./value.js"; +export * from "./while-expression.js"; diff --git a/packages/rust/src/components/let-binding.tsx b/packages/rust/src/components/let-binding.tsx new file mode 100644 index 000000000..f24ba80a3 --- /dev/null +++ b/packages/rust/src/components/let-binding.tsx @@ -0,0 +1,34 @@ +import { Children } from "@alloy-js/core"; + +export interface LetBindingProps { + name: string; + mutable?: boolean; + type?: Children; + children?: Children; +} + +export function LetBinding(props: LetBindingProps) { + const hasType = typeof props.type !== "undefined"; + const hasInitializer = typeof props.children !== "undefined"; + + return ( + <> + {"let "} + {props.mutable ? "mut " : ""} + {props.name} + {hasType ? + <> + {": "} + {props.type} + + : null} + {hasInitializer ? + <> + {" = "} + {props.children} + + : null} + {";"} + + ); +} diff --git a/packages/rust/src/components/loop-expression.tsx b/packages/rust/src/components/loop-expression.tsx new file mode 100644 index 000000000..33953d51a --- /dev/null +++ b/packages/rust/src/components/loop-expression.tsx @@ -0,0 +1,71 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface LoopExpressionProps { + label?: string; + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function renderBlock(children: Children | undefined) { + const statements = normalizeChildren(children); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function LoopExpression(props: LoopExpressionProps) { + return ( + <> + {props.label ? + <> + {props.label} + {": "} + + : null} + {"loop "} + {renderBlock(props.children)} + + ); +} diff --git a/packages/rust/src/components/macro-call.tsx b/packages/rust/src/components/macro-call.tsx new file mode 100644 index 000000000..d6b99522a --- /dev/null +++ b/packages/rust/src/components/macro-call.tsx @@ -0,0 +1,39 @@ +import { Children, For, Indent, Wrap } from "@alloy-js/core"; + +export interface MacroCallProps { + name: string; + args?: Children[]; + bracket?: "paren" | "bracket" | "brace"; +} + +const macroCallBrackets: Record< + NonNullable, + [string, string] +> = { + paren: ["(", ")"], + bracket: ["[", "]"], + brace: ["{", "}"], +}; + +export function MacroCall(props: MacroCallProps) { + const [openBracket, closeBracket] = + macroCallBrackets[props.bracket ?? "paren"]; + + return ( + + {props.name} + {"!"} + {openBracket} + 1} + with={Indent} + props={{ softline: true, trailingBreak: true }} + > + + {(arg) => arg} + + + {closeBracket} + + ); +} diff --git a/packages/rust/src/components/match-expression.tsx b/packages/rust/src/components/match-expression.tsx new file mode 100644 index 000000000..6fdd509b4 --- /dev/null +++ b/packages/rust/src/components/match-expression.tsx @@ -0,0 +1,79 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface MatchExpressionProps { + expression: Children; + children?: Children; +} + +export interface MatchArmProps { + pattern: Children; + guard?: Children; + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + return (Array.isArray(children) ? children : [children]).filter( + (child) => !(typeof child === "string" && child.trim().length === 0), + ); +} + +export function MatchExpression(props: MatchExpressionProps) { + const arms = normalizeChildren(props.children); + + return ( + <> + {"match "} + {props.expression} + {" {"} + {arms.length > 0 ? + <> + + }> + {(arm) => arm} + + + + + : null} + {"}"} + + ); +} + +export function MatchArm(props: MatchArmProps) { + const statements = normalizeChildren(props.children); + const renderInline = statements.length === 1; + + return ( + <> + {props.pattern} + {props.guard ? + <> + {" if "} + {props.guard} + + : null} + {" => "} + {renderInline ? + <> + {statements[0]} + {","} + + : <> + {"{"} + + }> + {(statement) => statement} + + + + {"},"} + + } + + ); +} diff --git a/packages/rust/src/components/method-chain-expression.tsx b/packages/rust/src/components/method-chain-expression.tsx new file mode 100644 index 000000000..239240b0d --- /dev/null +++ b/packages/rust/src/components/method-chain-expression.tsx @@ -0,0 +1,161 @@ +import { + BasePartProps, + Children, + createAccessExpression, + For, + Indent, + Wrap, +} from "@alloy-js/core"; + +export interface MethodChainExpressionProps { + receiver: Children; + children: Children; +} + +export interface MethodChainCallProps { + name: string; + args?: Children[]; + typeArgs?: Children[]; + await?: boolean; + try?: boolean; +} + +interface MethodChainPartProps extends BasePartProps { + receiver?: Children; + name?: string; + args?: Children[]; + typeArgs?: Children[]; + await?: boolean; + try?: boolean; +} + +interface MethodChainPartDescriptor { + receiver?: Children; + name?: string; + args: Children[]; + typeArgs: Children[]; + await: boolean; + try: boolean; + call: boolean; + [key: string]: unknown; +} + +const { Expression, Part, registerOuterComponent } = createAccessExpression< + MethodChainPartProps, + MethodChainPartDescriptor +>({ + createDescriptor(partProps, _symbol, first) { + if (first) { + if (partProps.receiver === undefined) { + throw new Error( + "MethodChainExpression requires a receiver before method calls.", + ); + } + + return { + receiver: partProps.receiver, + args: [], + typeArgs: [], + await: false, + try: false, + call: false, + }; + } + + if (!partProps.name) { + throw new Error( + "MethodChainExpression.Call requires a method name for each chain step.", + ); + } + + return { + name: partProps.name, + args: partProps.args ?? [], + typeArgs: partProps.typeArgs ?? [], + await: !!partProps.await, + try: !!partProps.try, + call: true, + }; + }, + + getBase(part) { + return part.receiver ?? ""; + }, + + formatPart(part, _prevPart, inCallChain) { + if (!part.name) { + throw new Error( + "MethodChainExpression call part is missing a method name.", + ); + } + + const content = ( + <> + {"."} + {part.name} + {part.typeArgs.length > 0 ? + <> + {"::<"} + + {(typeArg) => typeArg} + + {">"} + + : null} + {"("} + 1} + with={Indent} + props={{ softline: true, trailingBreak: true }} + > + + {(arg) => arg} + + + {")"} + {part.await ? ".await" : ""} + {part.try ? "?" : ""} + + ); + + if (inCallChain) { + return ( + + + {content} + + ); + } + + return ( + + + + {content} + + + ); + }, + + isCallPart(part) { + return part.call; + }, +}); + +export function MethodChainExpression( + props: MethodChainExpressionProps, +): Children { + return ( + + + {props.children} + + ); +} + +export const MethodChainCall = Part as ( + props: MethodChainCallProps, +) => Children; + +MethodChainExpression.Call = MethodChainCall; +registerOuterComponent(MethodChainExpression); diff --git a/packages/rust/src/components/mod-declarations.tsx b/packages/rust/src/components/mod-declarations.tsx new file mode 100644 index 000000000..222d6580f --- /dev/null +++ b/packages/rust/src/components/mod-declarations.tsx @@ -0,0 +1,61 @@ +import { type Children, code, For, memo } from "@alloy-js/core"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { type RustVisibilityProps, VisibilityPrefix } from "./visibility.js"; + +interface ModDeclaration extends RustVisibilityProps { + name: string; + attributes?: Children[]; +} + +export interface ModDeclarationsProps { + scope: RustCrateScope | RustModuleScope; +} + +function ModDeclarationLine(props: ModDeclaration) { + return ( + <> + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + {code`mod `} + {props.name} + {code`;`} + + ); +} + +export function ModDeclarations(props: ModDeclarationsProps) { + return memo(() => { + const declarations = Array.from(props.scope.childModules.values()).sort( + (left, right) => left.name.localeCompare(right.name), + ); + + if (declarations.length === 0) { + return <>; + } + + return ( + <> + {declarations.map((declaration, index) => ( + <> + + {index < declarations.length - 1 ? + + : null} + + ))} + + ); + }); +} diff --git a/packages/rust/src/components/module-directory.tsx b/packages/rust/src/components/module-directory.tsx new file mode 100644 index 000000000..bde76a90e --- /dev/null +++ b/packages/rust/src/components/module-directory.tsx @@ -0,0 +1,47 @@ +import { + Scope, + SourceDirectory, + createScope, + useScope, + type Children, +} from "@alloy-js/core"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { type RustVisibilityProps } from "./visibility.js"; + +export interface ModuleDirectoryProps extends RustVisibilityProps { + path: string; + attributes?: Children[]; + children?: Children; +} + +function getModuleName(path: string): string { + const segments = path.split("/").filter((segment) => segment.length > 0); + return segments[segments.length - 1] ?? path; +} + +export function ModuleDirectory(props: ModuleDirectoryProps) { + const parentScope = useScope(); + const scopeParent = + ( + parentScope instanceof RustCrateScope || + parentScope instanceof RustModuleScope + ) ? + parentScope + : undefined; + const moduleName = getModuleName(props.path); + + if (scopeParent) { + scopeParent.addChildModule({ name: moduleName, pub: props.pub, attributes: props.attributes }); + } + + const scope = createScope(RustModuleScope, moduleName, scopeParent, { + binder: scopeParent?.binder, + }); + + return ( + + {props.children} + + ); +} diff --git a/packages/rust/src/components/parameters.tsx b/packages/rust/src/components/parameters.tsx new file mode 100644 index 000000000..bbbe1fce2 --- /dev/null +++ b/packages/rust/src/components/parameters.tsx @@ -0,0 +1,44 @@ +import { Declaration as CoreDeclaration, For } from "@alloy-js/core"; +import { ParameterDescriptor } from "../parameter-descriptor.js"; +import { createParameterSymbol } from "../symbols/factories.js"; + +export interface ParametersProps { + parameters?: readonly ParameterDescriptor[]; + wrap?: boolean; +} + +function Parameter(props: { parameter: ParameterDescriptor }) { + const parameterSymbol = createParameterSymbol(props.parameter.name); + const typePrefix = + props.parameter.refType ? `${props.parameter.refType} ` : ""; + + return ( + + {props.parameter.mutable ? "mut " : ""} + {parameterSymbol.name} + {props.parameter.type !== undefined ? + <> + {": "} + {typePrefix} + {props.parameter.type} + + : null} + + ); +} + +export function Parameters(props: ParametersProps) { + const shouldWrap = props.wrap ?? true; + + return ( + <> + {shouldWrap ? "(" : null} + {props.parameters && props.parameters.length > 0 ? + + {(parameter) => } + + : null} + {shouldWrap ? ")" : null} + + ); +} diff --git a/packages/rust/src/components/reference.tsx b/packages/rust/src/components/reference.tsx new file mode 100644 index 000000000..fd7c34b1d --- /dev/null +++ b/packages/rust/src/components/reference.tsx @@ -0,0 +1,25 @@ +import { + type Refkey, + type RefkeyableObject, + REFKEYABLE, + computed, + emitSymbol, + isRefkey, +} from "@alloy-js/core"; +import { ref } from "../symbols/reference.js"; + +export interface ReferenceProps { + refkey: Refkey | RefkeyableObject; +} + +export function Reference(props: ReferenceProps) { + const resolvedRefkey = isRefkey(props.refkey) + ? props.refkey + : (props.refkey as RefkeyableObject)[REFKEYABLE](); + const result = ref(resolvedRefkey); + const symbolRef = computed(() => result()[1]); + + emitSymbol(symbolRef); + + return <>{result()[0]}; +} diff --git a/packages/rust/src/components/return-expression.tsx b/packages/rust/src/components/return-expression.tsx new file mode 100644 index 000000000..c51ae0cd0 --- /dev/null +++ b/packages/rust/src/components/return-expression.tsx @@ -0,0 +1,16 @@ +import { Children } from "@alloy-js/core"; + +export interface ReturnExpressionProps { + children?: Children; +} + +export function ReturnExpression(props: ReturnExpressionProps) { + return ( + <> + {"return"} + {typeof props.children !== "undefined" ? + <> {props.children} + : null} + + ); +} diff --git a/packages/rust/src/components/source-file.tsx b/packages/rust/src/components/source-file.tsx new file mode 100644 index 000000000..b4da43ff5 --- /dev/null +++ b/packages/rust/src/components/source-file.tsx @@ -0,0 +1,119 @@ +import { + SourceFile as CoreSourceFile, + createScope, + Scope, + Show, + useScope, + type Children, +} from "@alloy-js/core"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { ModDeclarations } from "./mod-declarations.js"; +import { Reference } from "./reference.js"; +import { UseStatements } from "./use-statement.js"; +import { type RustVisibilityProps } from "./visibility.js"; + +export interface SourceFileProps extends RustVisibilityProps { + path: string; + attributes?: Children[]; + children?: Children; + header?: Children; + headerComment?: Children; +} + +function isModuleRootPath(path: string): boolean { + const fileName = path.split("/").pop() ?? path; + return ( + fileName === "lib.rs" || fileName === "main.rs" || fileName === "mod.rs" + ); +} + +function getDeclarationScope( + path: string, + parent: RustCrateScope | RustModuleScope | undefined, + scope: RustModuleScope, +) { + if (!isModuleRootPath(path)) { + return undefined; + } + + if (path.endsWith("mod.rs") && parent instanceof RustModuleScope) { + return parent; + } + + if ( + (path.endsWith("lib.rs") || path.endsWith("main.rs")) && + parent instanceof RustCrateScope + ) { + return parent; + } + + return scope; +} + +function getStandaloneModuleName(path: string): string { + const segments = path.split("/").filter((segment) => segment.length > 0); + const fileName = segments[segments.length - 1] ?? path; + return fileName.endsWith(".rs") ? fileName.slice(0, -".rs".length) : fileName; +} + +function isStandaloneModulePath(path: string): boolean { + return path.endsWith(".rs") && !isModuleRootPath(path); +} + +export function SourceFile(props: SourceFileProps) { + const parentScope = useScope(); + const scopeParent = + ( + parentScope instanceof RustCrateScope || + parentScope instanceof RustModuleScope + ) ? + parentScope + : undefined; + if (scopeParent && isStandaloneModulePath(props.path)) { + scopeParent.addChildModule({ + name: getStandaloneModuleName(props.path), + pub: props.pub, + attributes: props.attributes, + }); + } + const scope = createScope(RustModuleScope, props.path, scopeParent, { + binder: scopeParent?.binder, + }); + const declarationScope = getDeclarationScope(props.path, scopeParent, scope); + + const header = + props.headerComment !== undefined || props.header !== undefined ? + <> + {props.headerComment} + {props.header} + + : undefined; + + return ( + + + {declarationScope ? + + : null} + {( + declarationScope && + declarationScope.childModules.size > 0 && + (scope.imports.size > 0 || props.children !== undefined) + ) ? + + : null} + + 0}> + + + {props.children} + + + ); +} diff --git a/packages/rust/src/components/static-declaration.tsx b/packages/rust/src/components/static-declaration.tsx new file mode 100644 index 000000000..92223bf6b --- /dev/null +++ b/packages/rust/src/components/static-declaration.tsx @@ -0,0 +1,56 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Namekey, + Refkey, +} from "@alloy-js/core"; +import { createStaticSymbol } from "../symbols/factories.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface StaticDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + mutable?: boolean; + attributes?: Children[]; + type: Children; + children?: Children; +} + +export function StaticDeclaration(props: StaticDeclarationProps) { + const staticSymbol = createStaticSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + + staticSymbol.visibility = toRustVisibility(props.pub); + + const mutabilityPrefix = props.mutable ? "mut " : ""; + + return ( + <> + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + + {"static "} + {mutabilityPrefix} + {staticSymbol.name} + {": "} + {props.type} + {" = "} + {props.children} + {";"} + + + ); +} diff --git a/packages/rust/src/components/stc/index.ts b/packages/rust/src/components/stc/index.ts new file mode 100644 index 000000000..9c4404d51 --- /dev/null +++ b/packages/rust/src/components/stc/index.ts @@ -0,0 +1,120 @@ +import { stc } from "@alloy-js/core"; +import { AssociatedType as AssociatedTypeComponent } from "../associated-type.js"; +import { + Attribute as AttributeComponent, + DeriveAttribute as DeriveAttributeComponent, + InnerAttribute as InnerAttributeComponent, +} from "../attribute.js"; +import { AwaitExpression as AwaitExpressionComponent } from "../await-expression.js"; +import { BlockExpression as BlockExpressionComponent } from "../block-expression.js"; +import { BreakExpression as BreakExpressionComponent } from "../break-expression.js"; +import { CargoTomlFile as CargoTomlFileComponent } from "../cargo-toml-file.js"; +import { ClosureExpression as ClosureExpressionComponent } from "../closure-expression.js"; +import { ConstDeclaration as ConstDeclarationComponent } from "../const-declaration.js"; +import { ContinueExpression as ContinueExpressionComponent } from "../continue-expression.js"; +import { CrateDirectory as CrateDirectoryComponent } from "../crate-directory.js"; +import { + BlockComment as BlockCommentComponent, + DocComment as DocCommentComponent, + InnerBlockDocComment as InnerBlockDocCommentComponent, + InnerDocComment as InnerDocCommentComponent, + LineComment as LineCommentComponent, + OuterBlockDocComment as OuterBlockDocCommentComponent, +} from "../doc-comment.js"; +import { + EnumDeclaration as EnumDeclarationComponent, + EnumVariant as EnumVariantComponent, +} from "../enum-declaration.js"; +import { ForExpression as ForExpressionComponent } from "../for-expression.js"; +import { FunctionCallExpression as FunctionCallExpressionComponent } from "../function-call-expression.js"; +import { FunctionDeclaration as FunctionDeclarationComponent } from "../function-declaration.js"; +import { + ElseClause as ElseClauseComponent, + ElseIfClause as ElseIfClauseComponent, + IfExpression as IfExpressionComponent, +} from "../if-expression.js"; +import { ImplBlock as ImplBlockComponent } from "../impl-block.js"; +import { LetBinding as LetBindingComponent } from "../let-binding.js"; +import { LoopExpression as LoopExpressionComponent } from "../loop-expression.js"; +import { MacroCall as MacroCallComponent } from "../macro-call.js"; +import { + MatchArm as MatchArmComponent, + MatchExpression as MatchExpressionComponent, +} from "../match-expression.js"; +import { + MethodChainCall as MethodChainCallComponent, + MethodChainExpression as MethodChainExpressionComponent, +} from "../method-chain-expression.js"; +import { ModuleDirectory as ModuleDirectoryComponent } from "../module-directory.js"; +import { Parameters as ParametersComponent } from "../parameters.js"; +import { Reference as ReferenceComponent } from "../reference.js"; +import { ReturnExpression as ReturnExpressionComponent } from "../return-expression.js"; +import { SourceFile as SourceFileComponent } from "../source-file.js"; +import { StaticDeclaration as StaticDeclarationComponent } from "../static-declaration.js"; +import { + Field as FieldComponent, + StructDeclaration as StructDeclarationComponent, +} from "../struct-declaration.js"; +import { + FieldInit as FieldInitComponent, + StructExpression as StructExpressionComponent, +} from "../struct-expression.js"; +import { TraitDeclaration as TraitDeclarationComponent } from "../trait-declaration.js"; +import { TryExpression as TryExpressionComponent } from "../try-expression.js"; +import { TypeAlias as TypeAliasComponent } from "../type-alias.js"; +import { TypeParameters as TypeParametersComponent } from "../type-parameters.js"; +import { UnsafeBlock as UnsafeBlockComponent } from "../unsafe-block.js"; +import { Value as ValueComponent } from "../value.js"; +import { WhileExpression as WhileExpressionComponent } from "../while-expression.js"; + +export const Attribute = stc(AttributeComponent); +export const InnerAttribute = stc(InnerAttributeComponent); +export const AssociatedType = stc(AssociatedTypeComponent); +export const AwaitExpression = stc(AwaitExpressionComponent); +export const BlockExpression = stc(BlockExpressionComponent); +export const CargoTomlFile = stc(CargoTomlFileComponent); +export const ClosureExpression = stc(ClosureExpressionComponent); +export const ConstDeclaration = stc(ConstDeclarationComponent); +export const StaticDeclaration = stc(StaticDeclarationComponent); +export const ContinueExpression = stc(ContinueExpressionComponent); +export const CrateDirectory = stc(CrateDirectoryComponent); +export const BreakExpression = stc(BreakExpressionComponent); +export const DeriveAttribute = stc(DeriveAttributeComponent); +export const BlockComment = stc(BlockCommentComponent); +export const DocComment = stc(DocCommentComponent); +export const EnumDeclaration = stc(EnumDeclarationComponent); +export const EnumVariant = stc(EnumVariantComponent); +export const Field = stc(FieldComponent); +export const FunctionDeclaration = stc(FunctionDeclarationComponent); +export const ForExpression = stc(ForExpressionComponent); +export const LoopExpression = stc(LoopExpressionComponent); +export const MacroCall = stc(MacroCallComponent); +export const MethodChainExpression = stc(MethodChainExpressionComponent); +export const MethodChainCall = stc(MethodChainCallComponent); +export const IfExpression = stc(IfExpressionComponent); +export const ElseIfClause = stc(ElseIfClauseComponent); +export const ElseClause = stc(ElseClauseComponent); +export const FunctionCallExpression = stc(FunctionCallExpressionComponent); +export const ImplBlock = stc(ImplBlockComponent); +export const LetBinding = stc(LetBindingComponent); +export const MatchArm = stc(MatchArmComponent); +export const MatchExpression = stc(MatchExpressionComponent); +export const ModuleDirectory = stc(ModuleDirectoryComponent); +export const InnerBlockDocComment = stc(InnerBlockDocCommentComponent); +export const InnerDocComment = stc(InnerDocCommentComponent); +export const LineComment = stc(LineCommentComponent); +export const OuterBlockDocComment = stc(OuterBlockDocCommentComponent); +export const Parameters = stc(ParametersComponent); +export const Reference = stc(ReferenceComponent); +export const ReturnExpression = stc(ReturnExpressionComponent); +export const SourceFile = stc(SourceFileComponent); +export const StructExpression = stc(StructExpressionComponent); +export const FieldInit = stc(FieldInitComponent); +export const StructDeclaration = stc(StructDeclarationComponent); +export const TraitDeclaration = stc(TraitDeclarationComponent); +export const TryExpression = stc(TryExpressionComponent); +export const TypeAlias = stc(TypeAliasComponent); +export const TypeParameters = stc(TypeParametersComponent); +export const UnsafeBlock = stc(UnsafeBlockComponent); +export const Value = stc(ValueComponent); +export const WhileExpression = stc(WhileExpressionComponent); diff --git a/packages/rust/src/components/struct-declaration.tsx b/packages/rust/src/components/struct-declaration.tsx new file mode 100644 index 000000000..fcf275f76 --- /dev/null +++ b/packages/rust/src/components/struct-declaration.tsx @@ -0,0 +1,190 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Indent, + Namekey, + Refkey, + Scope, + createScope, +} from "@alloy-js/core"; +import { RustImplScope, useRustScope } from "../scopes/index.js"; +import { + createFieldSymbol, + createStructSymbol, + createTypeParameterSymbol, +} from "../symbols/factories.js"; +import { DocComment } from "./doc-comment.js"; +import { + TypeParameterProp, + TypeParameters, + WhereClause, +} from "./type-parameters.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface StructDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + derives?: (string | Refkey)[]; + attributes?: Children[]; + doc?: string; + typeParameters?: TypeParameterProp[]; + whereClause?: Children; + tuple?: boolean; + types?: Children[]; + unit?: boolean; + children?: Children; +} + +export interface FieldProps extends RustVisibilityProps { + name: string | Namekey; + type: Children; + refkey?: Refkey; + attributes?: Children[]; + doc?: string; +} + +function DeclareNamedTypeTypeParameters(props: { + typeParameters?: TypeParameterProp[]; +}) { + const params = props.typeParameters ?? []; + for (const param of params) { + if (param.name) { + createTypeParameterSymbol(param.name); + } + } + + return <>; +} + +export function StructDeclaration(props: StructDeclarationProps) { + const parentScope = useRustScope(); + const structSymbol = createStructSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + const structScope = createScope(RustImplScope, structSymbol, parentScope, { + binder: parentScope.binder, + }); + + structSymbol.visibility = toRustVisibility(props.pub); + const members = + props.children ? + (Array.isArray(props.children) ? + props.children + : [props.children] + ).filter( + (child) => !(typeof child === "string" && child.trim().length === 0), + ) + : []; + const tupleTypes = props.types ?? []; + + return ( + <> + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + {props.derives && props.derives.length > 0 ? + <> + {"#[derive("} + + {(derive) => derive} + + {")]"} + + + : null} + + + + + + {"struct "} + {structSymbol.name} + + {props.whereClause && !props.tuple ? + <> + {" "} + {props.whereClause} + + : null} + {props.unit ? + ";" + : props.tuple ? + <> + {"("} + + {(type) => type} + + {")"} + {props.whereClause ? + <> + {" "} + {props.whereClause} + + : null} + {";"} + + : members.length > 0 ? + <> + {" {"} + + + }> + {(child) => child} + + + + + {"}"} + + : " {}"} + + + ); +} + +export function Field(props: FieldProps) { + const fieldSymbol = createFieldSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + fieldSymbol.visibility = toRustVisibility(props.pub); + + return ( + + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + {fieldSymbol.name} + {": "} + {props.type} + {","} + + ); +} diff --git a/packages/rust/src/components/struct-expression.tsx b/packages/rust/src/components/struct-expression.tsx new file mode 100644 index 000000000..ecbe9fcd2 --- /dev/null +++ b/packages/rust/src/components/struct-expression.tsx @@ -0,0 +1,64 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface StructExpressionProps { + type: Children; + spread?: Children; + children?: Children; +} + +export interface FieldInitProps { + name: string; + children?: Children; +} + +export function StructExpression(props: StructExpressionProps) { + const fields = + props.children ? + (Array.isArray(props.children) ? + props.children + : [props.children] + ).filter( + (child) => !(typeof child === "string" && child.trim().length === 0), + ) + : []; + + return ( + <> + {props.type} + {" {"} + {fields.length > 0 || props.spread ? + <> + + {fields.length > 0 ? + }> + {(field) => field} + + : null} + {props.spread ? + <> + {fields.length > 0 ? + + : null} + {".."} + {props.spread} + + : null} + + + + : null} + {"}"} + + ); +} + +export function FieldInit(props: FieldInitProps) { + return ( + <> + {props.name} + {typeof props.children === "undefined" ? null : ": "} + {props.children} + {","} + + ); +} diff --git a/packages/rust/src/components/trait-declaration.tsx b/packages/rust/src/components/trait-declaration.tsx new file mode 100644 index 000000000..1556e543c --- /dev/null +++ b/packages/rust/src/components/trait-declaration.tsx @@ -0,0 +1,95 @@ +import { + Children, + code, + Declaration as CoreDeclaration, + createScope, + For, + Indent, + Namekey, + Refkey, + Scope, +} from "@alloy-js/core"; +import { RustTraitScope, useRustScope } from "../scopes/index.js"; +import { createTraitSymbol } from "../symbols/factories.js"; +import { DocComment } from "./doc-comment.js"; +import { + TypeParameterProp, + TypeParameters, + WhereClause, +} from "./type-parameters.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface TraitDeclarationProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + typeParameters?: TypeParameterProp[]; + supertraits?: Children[]; + whereClause?: Children; + attributes?: Children[]; + doc?: Children; + children?: Children; +} + +export function TraitDeclaration(props: TraitDeclarationProps) { + const parentScope = useRustScope(); + const traitSymbol = createTraitSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + const traitScope = createScope(RustTraitScope, traitSymbol, parentScope, { + binder: parentScope.binder, + }); + + traitSymbol.visibility = toRustVisibility(props.pub); + + return ( + <> + {props.doc ? + <> + {props.doc} + + : null} + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + + {code`trait `} + {traitSymbol.name} + + {props.supertraits && props.supertraits.length > 0 ? + <> + {code`: `} + + {(supertrait) => supertrait} + + + : null} + {props.whereClause ? + <> + {" "} + {props.whereClause} + + : null} + {props.children ? + <> + {code` {`} + + {props.children} + + + {code`}`} + + : code` {}`} + + + ); +} diff --git a/packages/rust/src/components/try-expression.tsx b/packages/rust/src/components/try-expression.tsx new file mode 100644 index 000000000..178dae75f --- /dev/null +++ b/packages/rust/src/components/try-expression.tsx @@ -0,0 +1,14 @@ +import { Children } from "@alloy-js/core"; + +export interface TryExpressionProps { + children: Children; +} + +export function TryExpression(props: TryExpressionProps) { + return ( + <> + {props.children} + {"?"} + + ); +} diff --git a/packages/rust/src/components/type-alias.tsx b/packages/rust/src/components/type-alias.tsx new file mode 100644 index 000000000..130112bc7 --- /dev/null +++ b/packages/rust/src/components/type-alias.tsx @@ -0,0 +1,52 @@ +import { + Children, + Declaration as CoreDeclaration, + For, + Namekey, + Refkey, +} from "@alloy-js/core"; +import { createTypeAliasSymbol } from "../symbols/factories.js"; +import { TypeParameterProp, TypeParameters } from "./type-parameters.js"; +import { + type RustVisibilityProps, + toRustVisibility, + VisibilityPrefix, +} from "./visibility.js"; + +export interface TypeAliasProps extends RustVisibilityProps { + name: string | Namekey; + refkey?: Refkey; + attributes?: Children[]; + typeParameters?: TypeParameterProp[]; + children?: Children; +} + +export function TypeAlias(props: TypeAliasProps) { + const typeAliasSymbol = createTypeAliasSymbol(props.name, { + refkeys: props.refkey ? [props.refkey] : [], + }); + + typeAliasSymbol.visibility = toRustVisibility(props.pub); + + return ( + <> + {props.attributes && props.attributes.length > 0 ? + <> + + {(attr) => attr} + + + + : null} + + + {"type "} + {typeAliasSymbol.name} + + {" = "} + {props.children} + {";"} + + + ); +} diff --git a/packages/rust/src/components/type-parameters.tsx b/packages/rust/src/components/type-parameters.tsx new file mode 100644 index 000000000..7000d6684 --- /dev/null +++ b/packages/rust/src/components/type-parameters.tsx @@ -0,0 +1,75 @@ +import { Children, For } from "@alloy-js/core"; + +export interface TypeParameterProp { + name?: string; + lifetime?: string; + constraint?: Children; +} + +export interface TypeParametersProps { + params?: TypeParameterProp[]; +} + +export interface WhereClauseProps { + children?: Children; +} + +export function TypeParameters(props: TypeParametersProps) { + if (!props.params || props.params.length === 0) { + return <>; + } + + const lifetimes: TypeParameterProp[] = []; + const typeParameters: TypeParameterProp[] = []; + + for (const param of props.params) { + if (param.lifetime) { + lifetimes.push(param); + continue; + } + + if (param.name) { + typeParameters.push(param); + continue; + } + + throw new Error( + "TypeParameters entries must include either `lifetime` or `name`.", + ); + } + + const orderedParams = [...lifetimes, ...typeParameters]; + + return ( + <> + {"<"} + + {(param) => ( + <> + {param.lifetime ?? param.name} + {param.constraint ? + <> + {": "} + {param.constraint} + + : null} + + )} + + {">"} + + ); +} + +export function WhereClause(props: WhereClauseProps) { + if (!props.children) { + return <>; + } + + return ( + <> + {"where "} + {props.children} + + ); +} diff --git a/packages/rust/src/components/unsafe-block.tsx b/packages/rust/src/components/unsafe-block.tsx new file mode 100644 index 000000000..9f908f389 --- /dev/null +++ b/packages/rust/src/components/unsafe-block.tsx @@ -0,0 +1,64 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface UnsafeBlockProps { + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function renderBlock(children: Children | undefined) { + const statements = normalizeChildren(children); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function UnsafeBlock(props: UnsafeBlockProps) { + return ( + <> + {"unsafe "} + {renderBlock(props.children)} + + ); +} diff --git a/packages/rust/src/components/use-statement.tsx b/packages/rust/src/components/use-statement.tsx new file mode 100644 index 000000000..1ee9d5f85 --- /dev/null +++ b/packages/rust/src/components/use-statement.tsx @@ -0,0 +1,121 @@ +import { code, memo } from "@alloy-js/core"; +import { useRustModuleScope } from "../scopes/contexts.js"; +import { type RustVisibilityProps, VisibilityPrefix } from "./visibility.js"; + +export interface UseStatementProps extends RustVisibilityProps { + path: string; + symbol: string; +} + +interface UseStatementEntry extends RustVisibilityProps { + path: string; + symbols: string[]; +} + +interface UseStatementGroupProps { + entries: UseStatementEntry[]; +} + +export function UseStatement(props: UseStatementProps) { + return ( + <> + + {code`use `} + {props.path} + {code`::`} + {props.symbol} + {code`;`} + + ); +} + +function UseStatementPath(props: UseStatementEntry) { + const sortedSymbols = [...props.symbols].sort((left, right) => + left.localeCompare(right), + ); + + if (sortedSymbols.length === 1) { + return ; + } + + return ( + <> + + {code`use `} + {props.path} + {code`::{`} + {sortedSymbols.join(", ")} + {code`};`} + + ); +} + +function UseStatementGroup(props: UseStatementGroupProps) { + return ( + <> + {props.entries.map((entry, index) => ( + <> + + {index < props.entries.length - 1 ? + + : null} + + ))} + + ); +} + +export function UseStatements() { + const moduleScope = useRustModuleScope(); + return memo(() => { + const stdEntries: UseStatementEntry[] = []; + const externalEntries: UseStatementEntry[] = []; + const crateEntries: UseStatementEntry[] = []; + + for (const [path, symbols] of moduleScope.imports) { + const entry = { + path, + symbols: [...symbols].map((symbol) => symbol.name), + }; + + if (path === "std" || path.startsWith("std::")) { + stdEntries.push(entry); + } else if (path === "crate" || path.startsWith("crate::")) { + crateEntries.push(entry); + } else { + externalEntries.push(entry); + } + } + + const sortEntries = (left: UseStatementEntry, right: UseStatementEntry) => + left.path.localeCompare(right.path); + + stdEntries.sort(sortEntries); + externalEntries.sort(sortEntries); + crateEntries.sort(sortEntries); + + const groups = [stdEntries, externalEntries, crateEntries].filter( + (group) => group.length > 0, + ); + + if (groups.length === 0) { + return <>; + } + + return ( + <> + {groups.map((group, index) => ( + <> + + {index < groups.length - 1 ? + <> + + + + : null} + + ))} + + ); + }); +} diff --git a/packages/rust/src/components/value.tsx b/packages/rust/src/components/value.tsx new file mode 100644 index 000000000..4c9f515ca --- /dev/null +++ b/packages/rust/src/components/value.tsx @@ -0,0 +1,33 @@ +import { Children, For, memo } from "@alloy-js/core"; + +export interface ValueProps { + value?: unknown; +} + +export function Value(props: ValueProps): Children { + return memo((): Children => { + const value = props.value; + + if (typeof value === "undefined" || value === null) { + return "None"; + } else if (typeof value === "number" || typeof value === "boolean") { + return String(value); + } else if (typeof value === "string") { + return JSON.stringify(value); + } else if (Array.isArray(value)) { + return ( + <> + {"vec!["} + + {(item) => } + + {"]"} + + ); + } else if (typeof value === "function") { + return value as () => Children; + } + + return String(value); + }); +} diff --git a/packages/rust/src/components/visibility.ts b/packages/rust/src/components/visibility.ts new file mode 100644 index 000000000..09a1e0d12 --- /dev/null +++ b/packages/rust/src/components/visibility.ts @@ -0,0 +1,50 @@ +import { + type Children, + type Refkey, + isRefkey, + memo, + resolve, +} from "@alloy-js/core"; +import { type RustVisibility } from "../symbols/rust-output-symbol.js"; +import { buildUsePath } from "../symbols/reference.js"; +import { type RustScopeBase } from "../scopes/rust-scope.js"; + +export interface RustVisibilityProps { + pub?: boolean | "crate" | "super" | RustVisibility | Refkey; +} + +export function toRustVisibility( + pub: RustVisibilityProps["pub"], +): RustVisibility { + switch (pub) { + case true: + return "pub"; + case "crate": + return "pub(crate)"; + case "super": + return "pub(super)"; + case "pub": + case "pub(crate)": + case "pub(super)": + return pub; + default: + if (typeof pub === "string" && pub.startsWith("pub(in ")) { + return pub as RustVisibility; + } + return undefined; + } +} + +export function VisibilityPrefix(props: RustVisibilityProps): Children { + if (isRefkey(props.pub)) { + const resolveResult = resolve(props.pub); + return memo(() => { + if (!resolveResult.value) return ""; + const path = buildUsePath("crate", resolveResult.value.pathDown); + return `pub(in ${path}) `; + }); + } + + const visibility = toRustVisibility(props.pub); + return visibility ? `${visibility} ` : ""; +} diff --git a/packages/rust/src/components/while-expression.tsx b/packages/rust/src/components/while-expression.tsx new file mode 100644 index 000000000..84fd47f98 --- /dev/null +++ b/packages/rust/src/components/while-expression.tsx @@ -0,0 +1,72 @@ +import { Children, For, Indent } from "@alloy-js/core"; + +export interface WhileExpressionProps { + condition: Children; + label?: string; + children?: Children; +} + +function normalizeChildren(children: Children | undefined) { + if (!children) { + return []; + } + + const normalized: Children[] = []; + const queue = Array.isArray(children) ? [...children] : [children]; + + while (queue.length > 0) { + const child = queue.shift(); + if (typeof child === "undefined") { + continue; + } + + if (Array.isArray(child)) { + queue.unshift(...child); + continue; + } + + if (typeof child === "string" && child.trim().length === 0) { + continue; + } + + normalized.push(child); + } + + return normalized; +} + +function renderBlock(children: Children | undefined) { + const statements = normalizeChildren(children); + + return ( + <> + {"{"} + {statements.length > 0 ? + <> + + }> + {(statement) => statement} + + + + + : null} + {"}"} + + ); +} + +export function WhileExpression(props: WhileExpressionProps) { + return ( + <> + {props.label ? + <> + {props.label} + {": "} + + : null} + {"while "} + {props.condition} {renderBlock(props.children)} + + ); +} diff --git a/packages/rust/src/context/crate-context.tsx b/packages/rust/src/context/crate-context.tsx new file mode 100644 index 000000000..eb632cb37 --- /dev/null +++ b/packages/rust/src/context/crate-context.tsx @@ -0,0 +1,17 @@ +import { ComponentContext, createContext, useContext } from "@alloy-js/core"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; + +export interface CrateContextValue { + scope: RustCrateScope; + name: string; + version?: string; + edition: string; + crateType: "lib" | "bin"; +} + +export const CrateContext: ComponentContext = + createContext(); + +export function useCrateContext() { + return useContext(CrateContext); +} diff --git a/packages/rust/src/context/index.ts b/packages/rust/src/context/index.ts new file mode 100644 index 000000000..753bc9060 --- /dev/null +++ b/packages/rust/src/context/index.ts @@ -0,0 +1 @@ +export * from "./crate-context.js"; diff --git a/packages/rust/src/create-crate.ts b/packages/rust/src/create-crate.ts new file mode 100644 index 000000000..5c17f183b --- /dev/null +++ b/packages/rust/src/create-crate.ts @@ -0,0 +1,406 @@ +import { + type Binder, + createScope, + createSymbol, + getSymbolCreatorSymbol, + REFKEYABLE, + type Refkey, + type RefkeyableObject, + refkey, + SymbolCreator, +} from "@alloy-js/core"; +import { RustCrateScope, RustModuleScope } from "./scopes/index.js"; +import { + FunctionSymbol, + NamedTypeSymbol, + RustOutputSymbol, + type RustSymbolKind, +} from "./symbols/index.js"; + +export interface MemberDescriptor { + kind: RustSymbolKind; + name?: string; + /** True for associated functions (no self receiver, called with `::`). */ + associated?: boolean; + metadata?: Record; +} + +export interface SymbolDescriptor { + kind: RustSymbolKind; + name?: string; + metadata?: Record; + members?: Record; +} + +export interface CrateDescriptor< + TModules extends Record> = Record< + string, + Record + >, +> { + name: string; + version?: string; + builtin?: boolean; + modules: TModules; +} + +type SymbolRef = + TSymbol extends { members: infer M extends Record } + ? RefkeyableObject & { [K in keyof M]: Refkey } + : Refkey; + +export type CrateRef = { + [P in keyof TDescriptor["modules"]]: { + [S in keyof TDescriptor["modules"][P]]: SymbolRef< + TDescriptor["modules"][P][S] + >; + }; +}; + +const crateFactoryStateSymbol: unique symbol = Symbol( + "RustCreateCrateFactoryState", +); + +interface CrateFactoryState { + name: string; + version?: string; + builtin: boolean; + scopes: WeakMap; +} + +export interface ExternalCrate { + [crateFactoryStateSymbol]: unknown; +} + +export function getCrateInfo(crate: ExternalCrate) { + const state = getFactoryState(crate); + return { + name: state.name, + version: state.version, + }; +} + +export function getCrateScope(crate: ExternalCrate, binder: Binder) { + return getFactoryState(crate).scopes.get(binder); +} + +export function isBuiltinCrate(crate: ExternalCrate | RustCrateScope) { + if (crate instanceof RustCrateScope) { + return crate.builtin; + } + + return getFactoryState(crate).builtin; +} + +interface DescriptorEntry { + modulePath: string; + exportName: string; + descriptor: SymbolDescriptor; + symbolRefkey: Refkey; + modules: object; // reference to descriptor.modules for stable refkey derivation +} + +interface BinderState { + crateScope: RustCrateScope; + moduleScopes: Map; + createdSymbols: Map; +} + +export function createCrate< + const TModules extends Record>, +>( + descriptor: CrateDescriptor, +): CrateRef> & SymbolCreator & ExternalCrate { + const binderStates = new WeakMap(); + const entries: DescriptorEntry[] = []; + const crateFactoryState: CrateFactoryState = { + name: descriptor.name, + version: descriptor.version, + builtin: descriptor.builtin ?? false, + scopes: new WeakMap(), + }; + + const crateRef = { + [getSymbolCreatorSymbol()](binder: Binder) { + const state = mapGet(binderStates, binder, () => + createBinderState(binder, descriptor, crateFactoryState), + ); + + for (const entry of entries) { + if (state.createdSymbols.has(entry.symbolRefkey)) { + continue; + } + + const moduleScope = ensureModuleScope( + state, + entry.modulePath, + descriptor.name, + ); + const symbol = createSymbolFromDescriptor( + binder, + moduleScope, + entry, + ); + state.createdSymbols.set(entry.symbolRefkey, symbol); + } + }, + [crateFactoryStateSymbol]: crateFactoryState, + } as Record; + + for (const [modulePath, symbols] of Object.entries(descriptor.modules)) { + const moduleRefs: Record = {}; + for (const [exportName, symbolDescriptor] of Object.entries(symbols)) { + const symbolRefkey = refkey(descriptor.modules, modulePath, exportName); + + // If the symbol has members, create a RefkeyableObject with member refkeys + if (symbolDescriptor.members) { + const symbolWithMembers: Record = { + [REFKEYABLE]() { + return symbolRefkey; + }, + }; + for (const memberName of Object.keys(symbolDescriptor.members)) { + const memberRefkey = refkey( + descriptor.modules, + modulePath, + exportName, + memberName, + ); + symbolWithMembers[memberName] = memberRefkey; + } + moduleRefs[exportName] = symbolWithMembers; + } else { + moduleRefs[exportName] = symbolRefkey; + } + + entries.push({ + modulePath, + exportName, + descriptor: symbolDescriptor, + symbolRefkey, + modules: descriptor.modules, + }); + } + + crateRef[modulePath] = moduleRefs; + } + + return crateRef as CrateRef> & + SymbolCreator & + ExternalCrate; +} + +function getFactoryState(crate: ExternalCrate): CrateFactoryState { + return crate[crateFactoryStateSymbol] as CrateFactoryState; +} + +function createBinderState( + binder: Binder, + descriptor: CrateDescriptor, + crateFactoryState: CrateFactoryState, +): BinderState { + const crateScope = createScope( + RustCrateScope, + descriptor.name, + descriptor.version, + { + binder, + builtin: crateFactoryState.builtin, + metadata: { + external: true, + builtin: crateFactoryState.builtin, + }, + }, + ); + crateFactoryState.scopes.set(binder, crateScope); + + return { + crateScope, + moduleScopes: new Map(), + createdSymbols: new Map(), + }; +} + +function ensureModuleScope( + state: BinderState, + modulePath: string, + crateName: string, +) { + const normalizedPath = normalizeModulePath(modulePath); + return mapGet(state.moduleScopes, normalizedPath, () => { + const segments = parseModulePath(normalizedPath); + if (segments.length === 0) { + return createScope(RustModuleScope, "", state.crateScope, { + binder: state.crateScope.binder, + }); + } + + let currentParent: RustCrateScope | RustModuleScope = state.crateScope; + let currentPath = ""; + for (const segment of segments) { + currentPath = + currentPath.length === 0 ? segment : `${currentPath}::${segment}`; + currentParent = mapGet(state.moduleScopes, currentPath, () => { + currentParent.addChildModule({ name: segment, pub: true }); + return createScope(RustModuleScope, segment, currentParent, { + binder: state.crateScope.binder, + metadata: { + crate: crateName, + }, + }); + }); + } + + return currentParent as RustModuleScope; + }); +} + +function createSymbolFromDescriptor( + binder: Binder, + moduleScope: RustModuleScope, + entry: DescriptorEntry, +) { + const { descriptor, symbolRefkey, exportName } = entry; + const symbolName = descriptor.name ?? exportName; + const options = { + binder, + refkeys: symbolRefkey, + symbolKind: descriptor.kind, + metadata: descriptor.metadata, + ignoreNamePolicy: true, + ignoreNameConflict: true, + } as const; + + let symbol: RustOutputSymbol; + + switch (descriptor.kind) { + case "struct": + case "enum": + case "trait": + case "type-alias": + symbol = createSymbol( + NamedTypeSymbol, + symbolName, + moduleScope.types, + descriptor.kind, + options, + ); + break; + case "function": + case "method": + symbol = createSymbol( + FunctionSymbol, + symbolName, + moduleScope.values, + options, + ); + break; + default: + symbol = createSymbol( + RustOutputSymbol, + symbolName, + moduleScope.values, + options, + ); + break; + } + + // Create member symbols on the type's member space + if (descriptor.members) { + for (const [memberName, memberDesc] of Object.entries( + descriptor.members, + )) { + const memberRefkey = refkey( + entry.modules, + entry.modulePath, + exportName, + memberName, + ); + const memberSymbolName = memberDesc.name ?? memberName; + const memberOptions = { + binder, + refkeys: memberRefkey, + symbolKind: memberDesc.kind, + metadata: memberDesc.metadata, + ignoreNamePolicy: true, + ignoreNameConflict: true, + } as const; + + switch (memberDesc.kind) { + case "function": + case "method": // legacy compat + createSymbol( + FunctionSymbol, + memberSymbolName, + symbol.members, + memberDesc.associated + ? memberOptions + : { ...memberOptions, receiverType: "&self" }, + ); + break; + case "field": + case "variant": + createSymbol( + RustOutputSymbol, + memberSymbolName, + symbol.members, + memberOptions, + ); + break; + default: + createSymbol( + RustOutputSymbol, + memberSymbolName, + symbol.members, + memberOptions, + ); + break; + } + } + } + + return symbol; +} + +function normalizeModulePath(path: string) { + return path.trim(); +} + +function parseModulePath(path: string): string[] { + if (path === "") { + return []; + } + + return path + .split(/::|\//g) + .map((segment) => segment.trim()) + .filter((segment) => segment.length > 0); +} + +function mapGet( + map: WeakMap, + key: K, + init: () => V, +): V; +function mapGet(map: Map, key: K, init: () => V): V; +function mapGet( + map: WeakMap | Map, + key: K, + init: () => V, +): V { + if (map instanceof WeakMap) { + let value = map.get(key); + if (value === undefined) { + value = init(); + map.set(key, value); + } + return value; + } + + let value = map.get(key); + if (value === undefined) { + value = init(); + map.set(key, value); + } + return value; +} diff --git a/packages/rust/src/index.ts b/packages/rust/src/index.ts new file mode 100644 index 000000000..da10803f3 --- /dev/null +++ b/packages/rust/src/index.ts @@ -0,0 +1,9 @@ +export * from "./builtins/index.js"; +export * from "./components/index.js"; +export * from "./context/index.js"; +export * from "./create-crate.js"; +export * from "./name-conflict-resolver.js"; +export * from "./name-policy.js"; +export * from "./parameter-descriptor.js"; +export * from "./scopes/index.js"; +export * from "./symbols/index.js"; diff --git a/packages/rust/src/name-conflict-resolver.ts b/packages/rust/src/name-conflict-resolver.ts new file mode 100644 index 000000000..a913deab2 --- /dev/null +++ b/packages/rust/src/name-conflict-resolver.ts @@ -0,0 +1,42 @@ +import { RustOutputSymbol } from "./symbols/rust-output-symbol.js"; + +function isUseImportedSymbol(symbol: RustOutputSymbol): boolean { + return ( + symbol.isAlias || + symbol.metadata.rustImportedSymbol === true || + symbol.metadata.isRustUseImport === true + ); +} + +export function rustNameConflictResolver( + name: string, + symbols: RustOutputSymbol[], +) { + if (symbols.length <= 1) { + return; + } + + const localSymbols: RustOutputSymbol[] = []; + const importedSymbols: RustOutputSymbol[] = []; + + for (const symbol of symbols) { + if (isUseImportedSymbol(symbol)) { + importedSymbols.push(symbol); + } else { + localSymbols.push(symbol); + } + } + + let nameCount = 2; + + if (localSymbols.length === 0 && importedSymbols.length > 0) { + for (const symbol of importedSymbols.slice(1)) { + symbol.name = `${name}_${nameCount++}`; + } + return; + } + + for (const symbol of importedSymbols) { + symbol.name = `${name}_${nameCount++}`; + } +} diff --git a/packages/rust/src/name-policy.ts b/packages/rust/src/name-policy.ts new file mode 100644 index 000000000..3b024f3cc --- /dev/null +++ b/packages/rust/src/name-policy.ts @@ -0,0 +1,96 @@ +import { createNamePolicy, NamePolicy, useNamePolicy } from "@alloy-js/core"; +import { constantCase, pascalCase, snakeCase } from "change-case"; + +export type RustElements = + | "function" + | "method" + | "struct" + | "enum" + | "enum-variant" + | "trait" + | "type-alias" + | "type-parameter" + | "field" + | "variable" + | "parameter" + | "constant" + | "module"; + +const RESERVED_WORDS = new Set([ + "as", + "async", + "await", + "break", + "const", + "continue", + "crate", + "dyn", + "else", + "enum", + "extern", + "false", + "fn", + "for", + "if", + "impl", + "in", + "let", + "loop", + "match", + "mod", + "move", + "mut", + "pub", + "ref", + "return", + "self", + "Self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "while", + "yield", +]); + +function ensureNonReservedName(name: string): string { + if (RESERVED_WORDS.has(name)) { + return `r#${name}`; + } + + return name; +} + +export function createRustNamePolicy(): NamePolicy { + return createNamePolicy((name, element) => { + let transformedName: string; + + switch (element) { + case "struct": + case "enum": + case "enum-variant": + case "trait": + case "type-alias": + case "type-parameter": + transformedName = pascalCase(name); + break; + case "constant": + transformedName = constantCase(name); + break; + default: + transformedName = snakeCase(name); + break; + } + + return ensureNonReservedName(transformedName); + }); +} + +export function useRustNamePolicy(): NamePolicy { + return useNamePolicy(); +} diff --git a/packages/rust/src/parameter-descriptor.ts b/packages/rust/src/parameter-descriptor.ts new file mode 100644 index 000000000..8e704e119 --- /dev/null +++ b/packages/rust/src/parameter-descriptor.ts @@ -0,0 +1,24 @@ +import type { Children, Namekey } from "@alloy-js/core"; +import { isNamekey } from "@alloy-js/core"; + +export interface ParameterDescriptor { + readonly name: string | Namekey; + readonly type?: Children; + readonly mutable?: boolean; + readonly refType?: "&" | "&mut"; +} + +export function isParameterDescriptor( + value: unknown, +): value is ParameterDescriptor { + if ( + typeof value !== "object" || + value === null || + !Object.hasOwn(value, "name") + ) { + return false; + } + + const name = (value as Record).name; + return typeof name === "string" || isNamekey(name); +} diff --git a/packages/rust/src/scopes/contexts.ts b/packages/rust/src/scopes/contexts.ts new file mode 100644 index 000000000..3f9a18f4a --- /dev/null +++ b/packages/rust/src/scopes/contexts.ts @@ -0,0 +1,35 @@ +import { useScope } from "@alloy-js/core"; +import { RustCrateScope } from "./rust-crate-scope.js"; +import { RustModuleScope } from "./rust-module-scope.js"; +import { RustScopeBase } from "./rust-scope.js"; + +export function useRustScope() { + const scope = useScope(); + if (!(scope instanceof RustScopeBase)) { + throw new Error("Expected a Rust scope, got a different kind of scope."); + } + + return scope; +} + +export function useRustModuleScope() { + const scope = useRustScope(); + if (!(scope instanceof RustModuleScope)) { + throw new Error( + `Expected a Rust module scope, but got ${scope.constructor.name}.`, + ); + } + + return scope; +} + +export function useRustCrateScope() { + const scope = useRustScope(); + if (!(scope instanceof RustCrateScope)) { + throw new Error( + `Expected a Rust crate scope, but got ${scope.constructor.name}.`, + ); + } + + return scope; +} diff --git a/packages/rust/src/scopes/index.ts b/packages/rust/src/scopes/index.ts new file mode 100644 index 000000000..0b6cf33cc --- /dev/null +++ b/packages/rust/src/scopes/index.ts @@ -0,0 +1,23 @@ +import type { RustCrateScope } from "./rust-crate-scope.js"; +import type { RustFunctionScope } from "./rust-function-scope.js"; +import type { RustImplScope } from "./rust-impl-scope.js"; +import type { RustLexicalScope } from "./rust-lexical-scope.js"; +import type { RustModuleScope } from "./rust-module-scope.js"; +import type { RustTraitScope } from "./rust-trait-scope.js"; + +export type RustScope = + | RustCrateScope + | RustModuleScope + | RustFunctionScope + | RustLexicalScope + | RustImplScope + | RustTraitScope; + +export * from "./contexts.js"; +export * from "./rust-crate-scope.js"; +export * from "./rust-function-scope.js"; +export * from "./rust-impl-scope.js"; +export * from "./rust-lexical-scope.js"; +export * from "./rust-module-scope.js"; +export * from "./rust-scope.js"; +export * from "./rust-trait-scope.js"; diff --git a/packages/rust/src/scopes/rust-crate-scope.ts b/packages/rust/src/scopes/rust-crate-scope.ts new file mode 100644 index 000000000..60bc8b2ee --- /dev/null +++ b/packages/rust/src/scopes/rust-crate-scope.ts @@ -0,0 +1,92 @@ +import { + type Children, + type OutputScopeOptions, + type OutputSpace, + shallowReactive, +} from "@alloy-js/core"; +import { type RustVisibilityProps } from "../components/visibility.js"; +import { RustScopeBase } from "./rust-scope.js"; + +export interface CrateDependencyDetails { + version: string; + features?: string[]; +} + +export type CrateDependency = string | CrateDependencyDetails; + +export interface RustChildModuleDeclaration extends RustVisibilityProps { + name: string; + attributes?: Children[]; +} + +export interface RustCrateScopeOptions extends OutputScopeOptions { + builtin?: boolean; +} + +export class RustCrateScope extends RustScopeBase { + public static readonly declarationSpaces = ["types", "values"]; + + #version?: string; + get version() { + return this.#version; + } + + #childModules = shallowReactive>( + new Map(), + ); + #dependencies = shallowReactive>(new Map()); + #builtin = false; + get builtin() { + return this.#builtin; + } + + constructor( + name: string, + version?: string, + options: RustCrateScopeOptions = {}, + ) { + super(name, undefined, options); + this.#version = version; + this.#builtin = options.builtin ?? false; + } + + override get enclosingCrate() { + return this; + } + + get childModules() { + return this.#childModules; + } + + addChildModule(declaration: RustChildModuleDeclaration) { + const existing = this.#childModules.get(declaration.name); + if (existing) { + return existing; + } + + this.#childModules.set(declaration.name, declaration); + return declaration; + } + + get dependencies() { + return this.#dependencies; + } + + addDependency(name: string, dependency: CrateDependency) { + const existing = this.#dependencies.get(name); + if (existing) { + return existing; + } + + this.#dependencies.set(name, dependency); + return dependency; + } + + get types(): OutputSpace { + return this.spaceFor("types")!; + } + + get values(): OutputSpace { + return this.spaceFor("values")!; + } +} diff --git a/packages/rust/src/scopes/rust-function-scope.ts b/packages/rust/src/scopes/rust-function-scope.ts new file mode 100644 index 000000000..4cd92b7f1 --- /dev/null +++ b/packages/rust/src/scopes/rust-function-scope.ts @@ -0,0 +1,22 @@ +import type { OutputSpace } from "@alloy-js/core"; +import { RustLexicalScope } from "./rust-lexical-scope.js"; + +export class RustFunctionScope extends RustLexicalScope { + public static readonly declarationSpaces = [ + "local-variables", + "parameters", + "type-parameters", + ]; + + get localVariables(): OutputSpace { + return this.spaceFor("local-variables")!; + } + + get parameters(): OutputSpace { + return this.spaceFor("parameters")!; + } + + get typeParameters(): OutputSpace { + return this.spaceFor("type-parameters")!; + } +} diff --git a/packages/rust/src/scopes/rust-impl-scope.ts b/packages/rust/src/scopes/rust-impl-scope.ts new file mode 100644 index 000000000..e0120f192 --- /dev/null +++ b/packages/rust/src/scopes/rust-impl-scope.ts @@ -0,0 +1,26 @@ +import { type OutputScopeOptions } from "@alloy-js/core"; +import type { RustOutputSymbol } from "../symbols/rust-output-symbol.js"; +import { RustScopeBase } from "./rust-scope.js"; + +export class RustImplScope extends RustScopeBase { + public static readonly declarationSpaces: readonly string[] = []; + + constructor( + ownerSymbol: RustOutputSymbol, + parent: RustScopeBase | undefined, + options: OutputScopeOptions = {}, + ) { + super(`${ownerSymbol.name} impl scope`, parent, { + ownerSymbol, + ...options, + }); + } + + get ownerSymbol(): RustOutputSymbol { + return super.ownerSymbol as RustOutputSymbol; + } + + get members() { + return this.ownerSymbol.members; + } +} diff --git a/packages/rust/src/scopes/rust-lexical-scope.ts b/packages/rust/src/scopes/rust-lexical-scope.ts new file mode 100644 index 000000000..164ce8823 --- /dev/null +++ b/packages/rust/src/scopes/rust-lexical-scope.ts @@ -0,0 +1,10 @@ +import type { OutputSpace } from "@alloy-js/core"; +import { RustScopeBase } from "./rust-scope.js"; + +export class RustLexicalScope extends RustScopeBase { + public static readonly declarationSpaces = ["local-variables"]; + + get localVariables(): OutputSpace { + return this.spaceFor("local-variables")!; + } +} diff --git a/packages/rust/src/scopes/rust-module-scope.ts b/packages/rust/src/scopes/rust-module-scope.ts new file mode 100644 index 000000000..ffb31bb71 --- /dev/null +++ b/packages/rust/src/scopes/rust-module-scope.ts @@ -0,0 +1,80 @@ +import { + type Children, + type OutputScopeOptions, + type OutputSpace, + shallowReactive, +} from "@alloy-js/core"; +import { type RustOutputSymbol } from "../symbols/rust-output-symbol.js"; +import { type RustVisibilityProps } from "../components/visibility.js"; +import { RustCrateScope } from "./rust-crate-scope.js"; +import { RustScopeBase } from "./rust-scope.js"; + +export interface RustModuleDeclaration extends RustVisibilityProps { + name: string; + attributes?: Children[]; +} + +export class RustModuleScope extends RustScopeBase { + public static readonly declarationSpaces = ["types", "values"]; + + #imports = shallowReactive>>(new Map()); + #childModules = shallowReactive>( + new Map(), + ); + + constructor( + name: string, + parent: RustCrateScope | RustModuleScope | undefined, + options?: OutputScopeOptions, + ) { + super(name, parent, options); + } + + get parent() { + return super.parent as RustCrateScope | RustModuleScope | undefined; + } + + set parent(value: RustCrateScope | RustModuleScope | undefined) { + super.parent = value; + } + + override get enclosingModule() { + return this; + } + + get imports() { + return this.#imports; + } + + addUse(path: string, symbol: RustOutputSymbol) { + let symbolsForPath = this.#imports.get(path); + if (!symbolsForPath) { + symbolsForPath = shallowReactive(new Set()); + this.#imports.set(path, symbolsForPath); + } + + symbolsForPath.add(symbol); + } + + get childModules() { + return this.#childModules; + } + + addChildModule(declaration: RustModuleDeclaration) { + const existing = this.#childModules.get(declaration.name); + if (existing) { + return existing; + } + + this.#childModules.set(declaration.name, declaration); + return declaration; + } + + get types(): OutputSpace { + return this.spaceFor("types")!; + } + + get values(): OutputSpace { + return this.spaceFor("values")!; + } +} diff --git a/packages/rust/src/scopes/rust-scope.ts b/packages/rust/src/scopes/rust-scope.ts new file mode 100644 index 000000000..bb1b9b53f --- /dev/null +++ b/packages/rust/src/scopes/rust-scope.ts @@ -0,0 +1,28 @@ +import { OutputScope, OutputScopeOptions } from "@alloy-js/core"; +import type { RustOutputSymbol } from "../symbols/rust-output-symbol.js"; + +export class RustScopeBase extends OutputScope { + constructor( + name: string, + parent: RustScopeBase | undefined, + options?: OutputScopeOptions, + ) { + super(name, parent, options); + this.#crateScope = parent?.enclosingCrate; + this.#moduleScope = parent?.enclosingModule; + } + + #crateScope: RustScopeBase | undefined; + get enclosingCrate() { + return this.#crateScope; + } + + #moduleScope: RustScopeBase | undefined; + get enclosingModule() { + return this.#moduleScope; + } + + get ownerSymbol(): RustOutputSymbol | undefined { + return super.ownerSymbol as RustOutputSymbol | undefined; + } +} diff --git a/packages/rust/src/scopes/rust-trait-scope.ts b/packages/rust/src/scopes/rust-trait-scope.ts new file mode 100644 index 000000000..c2dac9a62 --- /dev/null +++ b/packages/rust/src/scopes/rust-trait-scope.ts @@ -0,0 +1,30 @@ +import { type OutputScopeOptions } from "@alloy-js/core"; +import type { NamedTypeSymbol } from "../symbols/named-type-symbol.js"; +import { RustScopeBase } from "./rust-scope.js"; + +export class RustTraitScope extends RustScopeBase { + public static readonly declarationSpaces: readonly string[] = []; + + constructor( + ownerSymbol: NamedTypeSymbol, + parent: RustScopeBase | undefined, + options: OutputScopeOptions = {}, + ) { + super(`${ownerSymbol.name} trait scope`, parent, { + ownerSymbol, + ...options, + }); + } + + get ownerSymbol(): NamedTypeSymbol { + return super.ownerSymbol as NamedTypeSymbol; + } + + get members() { + return this.ownerSymbol.members; + } + + get typeParameters() { + return this.ownerSymbol.typeParameters; + } +} diff --git a/packages/rust/src/symbols/factories.ts b/packages/rust/src/symbols/factories.ts new file mode 100644 index 000000000..7c3e80050 --- /dev/null +++ b/packages/rust/src/symbols/factories.ts @@ -0,0 +1,280 @@ +import { + createSymbol, + Namekey, + NamePolicyGetter, + useBinder, +} from "@alloy-js/core"; +import { RustElements, useRustNamePolicy } from "../name-policy.js"; +import { useRustScope } from "../scopes/contexts.js"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustFunctionScope } from "../scopes/rust-function-scope.js"; +import { RustImplScope } from "../scopes/rust-impl-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { RustTraitScope } from "../scopes/rust-trait-scope.js"; +import { FunctionSymbol, FunctionSymbolOptions } from "./function-symbol.js"; +import { + NamedTypeSymbol, + NamedTypeSymbolOptions, +} from "./named-type-symbol.js"; +import { + RustOutputSymbol, + RustOutputSymbolOptions, +} from "./rust-output-symbol.js"; + +export function createStructSymbol( + originalName: string | Namekey, + options: NamedTypeSymbolOptions = {}, +) { + const scope = useTypeValueScope("struct"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(NamedTypeSymbol, originalName, scope.types, "struct", { + ...withNamePolicy(options, "struct"), + binder, + symbolKind: "struct", + }); +} + +export function createEnumSymbol( + originalName: string | Namekey, + options: NamedTypeSymbolOptions = {}, +) { + const scope = useTypeValueScope("enum"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(NamedTypeSymbol, originalName, scope.types, "enum", { + ...withNamePolicy(options, "enum"), + binder, + symbolKind: "enum", + }); +} + +export function createTraitSymbol( + originalName: string | Namekey, + options: NamedTypeSymbolOptions = {}, +) { + const scope = useTypeValueScope("trait"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(NamedTypeSymbol, originalName, scope.types, "trait", { + ...withNamePolicy(options, "trait"), + binder, + symbolKind: "trait", + }); +} + +export function createFunctionSymbol( + originalName: string | Namekey, + options: FunctionSymbolOptions = {}, +) { + const scope = useTypeValueScope("function"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(FunctionSymbol, originalName, scope.values, { + ...withNamePolicy(options, "function"), + binder, + symbolKind: "function", + }); +} + +export function createMethodSymbol( + originalName: string | Namekey, + options: FunctionSymbolOptions = {}, +) { + const scope = useRustScope(); + if (!(scope instanceof RustImplScope) && !(scope instanceof RustTraitScope)) { + throw new Error( + "Can't create method symbol outside of an impl or trait scope.", + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(FunctionSymbol, originalName, scope.members, { + ...withNamePolicy(options, "method"), + binder, + symbolKind: "method", + }); +} + +export function createAssociatedTypeSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useRustScope(); + if (!(scope instanceof RustImplScope) && !(scope instanceof RustTraitScope)) { + throw new Error( + "Can't create associated type symbol outside of an impl or trait scope.", + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(RustOutputSymbol, originalName, scope.members, { + ...withNamePolicy(options, "type-alias"), + binder, + symbolKind: "associated-type", + }); +} + +export function createTypeAliasSymbol( + originalName: string | Namekey, + options: NamedTypeSymbolOptions = {}, +) { + const scope = useTypeValueScope("type alias"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol( + NamedTypeSymbol, + originalName, + scope.types, + "type-alias", + { + ...withNamePolicy(options, "type-alias"), + binder, + symbolKind: "type-alias", + }, + ); +} + +export function createConstSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useTypeValueScope("const"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(RustOutputSymbol, originalName, scope.values, { + ...withNamePolicy(options, "constant"), + binder, + symbolKind: "const", + }); +} + +export function createStaticSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useTypeValueScope("static"); + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(RustOutputSymbol, originalName, scope.values, { + ...withNamePolicy(options, "constant"), + binder, + symbolKind: "static", + }); +} + +export function createFieldSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useRustScope(); + if (!(scope.ownerSymbol instanceof NamedTypeSymbol)) { + throw new Error( + "Can't create field symbol outside of a named type member scope.", + ); + } + if (scope.ownerSymbol.typeKind !== "struct") { + throw new Error( + `Can't create field symbol for non-struct type ${scope.ownerSymbol.typeKind}.`, + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol( + RustOutputSymbol, + originalName, + scope.ownerSymbol.members, + { + ...withNamePolicy(options, "field"), + binder, + symbolKind: "field", + }, + ); +} + +export function createVariantSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useRustScope(); + if (!(scope.ownerSymbol instanceof NamedTypeSymbol)) { + throw new Error( + "Can't create variant symbol outside of a named type member scope.", + ); + } + if (scope.ownerSymbol.typeKind !== "enum") { + throw new Error( + `Can't create variant symbol for non-enum type ${scope.ownerSymbol.typeKind}.`, + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol( + RustOutputSymbol, + originalName, + scope.ownerSymbol.members, + { + ...withNamePolicy(options, "enum-variant"), + binder, + symbolKind: "variant", + }, + ); +} + +export function createParameterSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useRustScope(); + if (!(scope instanceof RustFunctionScope)) { + throw new Error( + "Can't create parameter symbol outside of a function scope.", + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(RustOutputSymbol, originalName, scope.parameters, { + ...withNamePolicy(options, "parameter"), + binder, + symbolKind: "parameter", + }); +} + +export function createTypeParameterSymbol( + originalName: string | Namekey, + options: RustOutputSymbolOptions = {}, +) { + const scope = useRustScope(); + const typeParameterSpace = + scope instanceof RustFunctionScope ? scope.typeParameters + : scope.ownerSymbol instanceof NamedTypeSymbol ? + scope.ownerSymbol.typeParameters + : undefined; + + if (!typeParameterSpace) { + throw new Error( + "Can't create type parameter symbol outside of a function or named type scope.", + ); + } + + const binder = options.binder ?? scope.binder ?? useBinder(); + return createSymbol(RustOutputSymbol, originalName, typeParameterSpace, { + ...withNamePolicy(options, "type-parameter"), + binder, + symbolKind: "type-parameter", + }); +} + +function useTypeValueScope(kind: string): RustCrateScope | RustModuleScope { + const scope = useRustScope(); + if (scope instanceof RustCrateScope || scope instanceof RustModuleScope) { + return scope; + } + + throw new Error( + `Can't create ${kind} symbol outside of a crate or module scope.`, + ); +} + +function withNamePolicy( + options: T, + elementType: RustElements, +) { + return { + ...options, + namePolicy: options.namePolicy ?? useRustNamePolicy().for(elementType), + }; +} diff --git a/packages/rust/src/symbols/function-symbol.ts b/packages/rust/src/symbols/function-symbol.ts new file mode 100644 index 000000000..47e33397c --- /dev/null +++ b/packages/rust/src/symbols/function-symbol.ts @@ -0,0 +1,110 @@ +import { + Children, + createSymbol, + Namekey, + OutputSpace, + track, + TrackOpTypes, + trigger, + TriggerOpTypes, + watch, +} from "@alloy-js/core"; +import { + RustOutputSymbol, + RustOutputSymbolOptions, +} from "./rust-output-symbol.js"; + +export interface FunctionSymbolOptions extends RustOutputSymbolOptions { + receiverType?: Children; +} + +export class FunctionSymbol extends RustOutputSymbol { + #receiverType?: Children; + get receiverType() { + track(this, TrackOpTypes.GET, "receiverType"); + return this.#receiverType; + } + + set receiverType(value: Children | undefined) { + const old = this.#receiverType; + if (old === value) { + return; + } + + this.#receiverType = value; + trigger(this, TriggerOpTypes.SET, "receiverType", value, old); + } + + get isInstanceMemberSymbol(): boolean { + return this.receiverType !== undefined; + } + + constructor( + name: string | Namekey, + spaces: OutputSpace[] | OutputSpace | undefined, + options: FunctionSymbolOptions = {}, + ) { + super(name, spaces, options); + this.#receiverType = options.receiverType; + } + + copy() { + const options = this.getCopyOptions(); + const binder = this.binder; + const copy = createSymbol(FunctionSymbol, this.name, undefined, { + ...options, + binder, + visibility: this.visibility, + symbolKind: this.symbolKind, + isAsync: this.isAsync, + isUnsafe: this.isUnsafe, + isConst: this.isConst, + receiverType: this.#receiverType, + }); + this.initializeCopy(copy); + + watch( + () => this.receiverType, + (newValue) => { + copy.receiverType = newValue; + }, + ); + + watch( + () => this.visibility, + (newValue) => { + copy.visibility = newValue; + }, + ); + + watch( + () => this.symbolKind, + (newValue) => { + copy.symbolKind = newValue; + }, + ); + + watch( + () => this.isAsync, + (newValue) => { + copy.isAsync = newValue; + }, + ); + + watch( + () => this.isUnsafe, + (newValue) => { + copy.isUnsafe = newValue; + }, + ); + + watch( + () => this.isConst, + (newValue) => { + copy.isConst = newValue; + }, + ); + + return copy; + } +} diff --git a/packages/rust/src/symbols/index.ts b/packages/rust/src/symbols/index.ts new file mode 100644 index 000000000..682eb129c --- /dev/null +++ b/packages/rust/src/symbols/index.ts @@ -0,0 +1,5 @@ +export * from "./factories.js"; +export * from "./function-symbol.js"; +export * from "./named-type-symbol.js"; +export * from "./reference.js"; +export * from "./rust-output-symbol.js"; diff --git a/packages/rust/src/symbols/named-type-symbol.ts b/packages/rust/src/symbols/named-type-symbol.ts new file mode 100644 index 000000000..29bd33303 --- /dev/null +++ b/packages/rust/src/symbols/named-type-symbol.ts @@ -0,0 +1,123 @@ +import { + createSymbol, + Namekey, + OutputSpace, + track, + TrackOpTypes, + trigger, + TriggerOpTypes, + watch, +} from "@alloy-js/core"; +import { + RustOutputSymbol, + RustOutputSymbolOptions, +} from "./rust-output-symbol.js"; + +export type NamedTypeTypeKind = "struct" | "enum" | "trait" | "type-alias"; +export interface NamedTypeSymbolOptions extends RustOutputSymbolOptions {} + +export class NamedTypeSymbol extends RustOutputSymbol { + static readonly memberSpaces: readonly string[] = [ + "members", + "type-parameters", + ]; + + #typeKind: NamedTypeTypeKind; + get typeKind() { + track(this, TrackOpTypes.GET, "typeKind"); + return this.#typeKind; + } + + set typeKind(value: NamedTypeTypeKind) { + const old = this.#typeKind; + if (old === value) { + return; + } + + this.#typeKind = value; + trigger(this, TriggerOpTypes.SET, "typeKind", value, old); + } + + constructor( + name: string | Namekey, + spaces: OutputSpace[] | OutputSpace | undefined, + typeKind: NamedTypeTypeKind, + options: NamedTypeSymbolOptions = {}, + ) { + super(name, spaces, options); + this.#typeKind = typeKind; + } + + copy() { + const options = this.getCopyOptions(); + const binder = this.binder; + const copy = createSymbol( + NamedTypeSymbol, + this.name, + undefined, + this.#typeKind, + { + ...options, + binder, + visibility: this.visibility, + symbolKind: this.symbolKind, + isAsync: this.isAsync, + isUnsafe: this.isUnsafe, + isConst: this.isConst, + }, + ); + this.initializeCopy(copy); + + watch( + () => this.typeKind, + (newValue) => { + copy.typeKind = newValue; + }, + ); + + watch( + () => this.visibility, + (newValue) => { + copy.visibility = newValue; + }, + ); + + watch( + () => this.symbolKind, + (newValue) => { + copy.symbolKind = newValue; + }, + ); + + watch( + () => this.isAsync, + (newValue) => { + copy.isAsync = newValue; + }, + ); + + watch( + () => this.isUnsafe, + (newValue) => { + copy.isUnsafe = newValue; + }, + ); + + watch( + () => this.isConst, + (newValue) => { + copy.isConst = newValue; + }, + ); + + return copy; + } + + get members() { + return this.memberSpaceFor("members")!; + } + + get typeParameters() { + return this.memberSpaceFor("type-parameters")!; + } +} diff --git a/packages/rust/src/symbols/reference.tsx b/packages/rust/src/symbols/reference.tsx new file mode 100644 index 000000000..32cb771cc --- /dev/null +++ b/packages/rust/src/symbols/reference.tsx @@ -0,0 +1,181 @@ +import { Children, Refkey, memo, resolve, unresolvedRefkey } from "@alloy-js/core"; +import { + PRELUDE_TYPES, + PRELUDE_TYPES_2015, + PRELUDE_TYPES_2018, + PRELUDE_TYPES_2021, + PRELUDE_TYPES_2024, +} from "../builtins/prelude.js"; +import { useCrateContext } from "../context/crate-context.js"; +import { isBuiltinCrate } from "../create-crate.js"; +import { useRustScope } from "../scopes/contexts.js"; +import { RustCrateScope } from "../scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../scopes/rust-module-scope.js"; +import { RustScopeBase } from "../scopes/rust-scope.js"; +import { RustOutputSymbol, type RustVisibility } from "./rust-output-symbol.js"; + +const PRELUDE_BY_EDITION: Record> = { + "2015": PRELUDE_TYPES_2015, + "2018": PRELUDE_TYPES_2018, + "2021": PRELUDE_TYPES_2021, + "2024": PRELUDE_TYPES_2024, +}; + +export function ref( + refkey: Refkey, +): () => [Children, RustOutputSymbol | undefined] { + const currentScope = useRustScope(); + const currentModuleScope = currentScope.enclosingModule; + if (!(currentModuleScope instanceof RustModuleScope)) { + throw new Error( + `Expected an enclosing Rust module scope, but got ${currentScope.constructor.name}.`, + ); + } + const resolveResult = resolve( + refkey as Refkey, + ); + + // Pick the prelude set for the current crate's edition + const crateContext = useCrateContext(); + const prelude = crateContext + ? (PRELUDE_BY_EDITION[crateContext.edition] ?? PRELUDE_TYPES) + : PRELUDE_TYPES; + + return memo(() => { + if (resolveResult.value === undefined) { + return [unresolvedRefkey(refkey), undefined]; + } + + const result = resolveResult.value; + const { symbol, lexicalDeclaration, commonScope, memberPath } = result; + const declarationName = lexicalDeclaration.name; + + const sourceCrate = currentModuleScope.enclosingCrate; + const declarationScope = lexicalDeclaration.scope as + | RustScopeBase + | undefined; + const targetModule = declarationScope?.enclosingModule; + const targetCrate = declarationScope?.enclosingCrate; + + // Only skip import for prelude types when the symbol is NOT declared + // in the current crate. User-defined types that shadow prelude names + // (e.g., `type Result = ...`) still need `use` imports. + const isLocalSymbol = + targetCrate instanceof RustCrateScope && + sourceCrate instanceof RustCrateScope && + targetCrate === sourceCrate; + + if (!(prelude.has(declarationName) && !isLocalSymbol)) { + if ( + targetModule instanceof RustModuleScope && + targetCrate instanceof RustCrateScope && + sourceCrate instanceof RustCrateScope + ) { + if (targetModule !== currentModuleScope) { + if (targetCrate === sourceCrate) { + if (!isVisibleFrom(lexicalDeclaration.visibility, result.fullReferencePath)) { + throw new Error( + `Cannot reference private symbol '${declarationName}' from module '${currentModuleScope.name}'.`, + ); + } + + const sameCratePath = buildUsePath("crate", result.pathDown); + currentModuleScope.addUse(sameCratePath, lexicalDeclaration); + } else { + const externalCratePath = buildUsePath( + targetCrate.name, + result.pathDown, + ); + currentModuleScope.addUse(externalCratePath, lexicalDeclaration); + if (!isBuiltinCrate(targetCrate)) { + sourceCrate.addDependency( + targetCrate.name, + targetCrate.version ?? "*", + ); + } + } + } + } + } + + return [ + buildReferenceChildren(commonScope, lexicalDeclaration, memberPath), + symbol, + ]; + }); +} + +function buildReferenceChildren( + commonScope: RustScopeBase | undefined, + lexicalDeclaration: RustOutputSymbol, + memberPath: RustOutputSymbol[], +): Children { + const parts: Children[] = []; + + if (commonScope && commonScope.isMemberScope) { + // Referencing a member of a type we are inside + if (lexicalDeclaration.isInstanceMemberSymbol) { + // Instance member: self.member + parts.push("self.", lexicalDeclaration.name); + } else { + // Associated item: Type::member + parts.push(commonScope.ownerSymbol!.name, "::", lexicalDeclaration.name); + } + } else { + parts.push(lexicalDeclaration.name); + } + + for (const member of memberPath) { + if (member.isInstanceMemberSymbol) { + parts.push(".", member.name); + } else { + parts.push("::", member.name); + } + } + + return <>{parts}; +} + +function isVisibleFrom( + visibility: RustVisibility, + referencePath: RustScopeBase[], +): boolean { + if (visibility === undefined) { + return false; + } + + if (typeof visibility === "string" && visibility.startsWith("pub(in ")) { + const allowedPath = visibility.slice("pub(in ".length, -")".length); + const refPath = buildUsePath("crate", referencePath); + return refPath === allowedPath || refPath.startsWith(allowedPath + "::"); + } + + return true; +} + +export function buildUsePath(prefix: string, pathDown: RustScopeBase[]): string { + const moduleSegments: string[] = []; + + for (const scope of pathDown) { + if (scope instanceof RustModuleScope) { + moduleSegments.push(...moduleNameSegments(scope.name)); + } + } + + return [prefix, ...moduleSegments].join("::"); +} + +export function moduleNameSegments(moduleName: string): string[] { + const normalized = moduleName + .split("/") + .map((segment) => segment.trim()) + .filter((segment) => segment.length > 0) + .map((segment) => + segment.endsWith(".rs") ? segment.slice(0, -3) : segment, + ) + .filter( + (segment) => segment !== "mod" && segment !== "lib" && segment !== "main", + ); + + return normalized; +} diff --git a/packages/rust/src/symbols/rust-output-symbol.ts b/packages/rust/src/symbols/rust-output-symbol.ts new file mode 100644 index 000000000..d9c36ba45 --- /dev/null +++ b/packages/rust/src/symbols/rust-output-symbol.ts @@ -0,0 +1,197 @@ +import { + createSymbol, + Namekey, + OutputSpace, + OutputSymbol, + OutputSymbolOptions, + track, + TrackOpTypes, + trigger, + TriggerOpTypes, + watch, +} from "@alloy-js/core"; + +export type RustVisibility = "pub" | "pub(crate)" | "pub(super)" | `pub(in ${string})` | undefined; + +export type RustSymbolKind = + | "symbol" + | "function" + | "struct" + | "enum" + | "trait" + | "type-alias" + | "const" + | "static" + | "module" + | "field" + | "variant" + | "method" + | "associated-type" + | "parameter" + | "lifetime" + | "type-parameter"; + +export interface RustOutputSymbolOptions extends OutputSymbolOptions { + visibility?: RustVisibility; + symbolKind?: RustSymbolKind; + isAsync?: boolean; + isUnsafe?: boolean; + isConst?: boolean; +} + +export class RustOutputSymbol extends OutputSymbol { + static readonly memberSpaces: readonly string[] = ["members"]; + + #visibility: RustVisibility; + get visibility() { + track(this, TrackOpTypes.GET, "visibility"); + return this.#visibility; + } + + set visibility(value: RustVisibility) { + const old = this.#visibility; + if (old === value) { + return; + } + + this.#visibility = value; + trigger(this, TriggerOpTypes.SET, "visibility", value, old); + } + + #symbolKind: RustSymbolKind; + get symbolKind() { + track(this, TrackOpTypes.GET, "symbolKind"); + return this.#symbolKind; + } + + set symbolKind(value: RustSymbolKind) { + const old = this.#symbolKind; + if (old === value) { + return; + } + + this.#symbolKind = value; + trigger(this, TriggerOpTypes.SET, "symbolKind", value, old); + } + + #isAsync: boolean; + get isAsync() { + track(this, TrackOpTypes.GET, "isAsync"); + return this.#isAsync; + } + + set isAsync(value: boolean) { + const old = this.#isAsync; + if (old === value) { + return; + } + + this.#isAsync = value; + trigger(this, TriggerOpTypes.SET, "isAsync", value, old); + } + + #isUnsafe: boolean; + get isUnsafe() { + track(this, TrackOpTypes.GET, "isUnsafe"); + return this.#isUnsafe; + } + + set isUnsafe(value: boolean) { + const old = this.#isUnsafe; + if (old === value) { + return; + } + + this.#isUnsafe = value; + trigger(this, TriggerOpTypes.SET, "isUnsafe", value, old); + } + + #isConst: boolean; + get isConst() { + track(this, TrackOpTypes.GET, "isConst"); + return this.#isConst; + } + + set isConst(value: boolean) { + const old = this.#isConst; + if (old === value) { + return; + } + + this.#isConst = value; + trigger(this, TriggerOpTypes.SET, "isConst", value, old); + } + + constructor( + name: string | Namekey, + spaces: OutputSpace[] | OutputSpace | undefined, + options: RustOutputSymbolOptions = {}, + ) { + super(name, spaces, options); + this.#visibility = options.visibility; + this.#symbolKind = options.symbolKind ?? "symbol"; + this.#isAsync = options.isAsync ?? false; + this.#isUnsafe = options.isUnsafe ?? false; + this.#isConst = options.isConst ?? false; + } + + copy() { + const options = this.getCopyOptions(); + const binder = this.binder; + const copy = createSymbol(RustOutputSymbol, this.name, undefined, { + ...options, + binder, + visibility: this.#visibility, + symbolKind: this.#symbolKind, + isAsync: this.#isAsync, + isUnsafe: this.#isUnsafe, + isConst: this.#isConst, + }); + this.initializeCopy(copy); + + watch( + () => this.visibility, + (newValue) => { + copy.visibility = newValue; + }, + ); + + watch( + () => this.symbolKind, + (newValue) => { + copy.symbolKind = newValue; + }, + ); + + watch( + () => this.isAsync, + (newValue) => { + copy.isAsync = newValue; + }, + ); + + watch( + () => this.isUnsafe, + (newValue) => { + copy.isUnsafe = newValue; + }, + ); + + watch( + () => this.isConst, + (newValue) => { + copy.isConst = newValue; + }, + ); + + return copy; + } + + get members() { + return this.memberSpaceFor("members")!; + } + + get isInstanceMemberSymbol(): boolean { + return this.symbolKind === "field"; + } +} diff --git a/packages/rust/test/T073-inline-reference-line-breaks.test.tsx b/packages/rust/test/T073-inline-reference-line-breaks.test.tsx new file mode 100644 index 000000000..ddf44fd3d --- /dev/null +++ b/packages/rust/test/T073-inline-reference-line-breaks.test.tsx @@ -0,0 +1,53 @@ +import { Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + DocComment, + EnumDeclaration, + EnumVariant, + InnerDocComment, + SourceFile, + StructDeclaration, +} from "../src/index.js"; + +describe("T073: doc comment declaration line breaks", () => { + it("should insert line break between DocComment and EnumDeclaration without explicit hbr", () => { + expect( + + + + {"First doc\nSecond line"} + + + + + + , + ).toRenderTo(d` + /// First doc + /// Second line + pub enum MyEnum { + Variant1, + } + `); + }); + + it("should insert line break between InnerDocComment and StructDeclaration without explicit hbr", () => { + expect( + + + + {"Module docs\nSecond line"} + + + + , + ).toRenderTo(d` + //! Module docs + //! Second line + struct Response {} + `); + }); +}); diff --git a/packages/rust/test/associated-types.test.tsx b/packages/rust/test/associated-types.test.tsx new file mode 100644 index 000000000..c6475f773 --- /dev/null +++ b/packages/rust/test/associated-types.test.tsx @@ -0,0 +1,135 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + AssociatedType, + CrateDirectory, + ImplBlock, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +function MemberKindsProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return [...symbol.members] + .map((member) => + member instanceof RustOutputSymbol ? + `${member.name}:${member.symbolKind}` + : member.name, + ) + .join(","); + } + } + + return "missing"; +} + +describe("AssociatedType", () => { + it("renders trait abstract associated type", () => { + expect( + + + + + + + + + + + , + ).toRenderTo(d` + trait Iterable { + type Item; + } + Item:associated-type + `); + }); + + it("renders trait constrained associated type", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + trait Iterable { + type Item: Clone; + } + `); + }); + + it("renders impl concrete associated type", () => { + const iterableRef = refkey("iterable-trait"); + const numbersRef = refkey("numbers-struct"); + + expect( + + + + + + + + + u32 + + + + , + ).toRenderTo(d` + trait Iterable {} + struct Numbers {} + impl Iterable for Numbers { + type Item = u32; + } + `); + }); + + it("exports working STC wrapper", () => { + expect( + + + + {Stc.TraitDeclaration({ name: "Iterable" }).children([ + Stc.AssociatedType({ name: "Item", constraint: "Clone" }), + ])} + + + , + ).toRenderTo(d` + trait Iterable { + type Item: Clone; + } + `); + }); + + it("throws when used outside trait or impl scope", () => { + expect(() => + render( + + + + + + + , + ), + ).toThrow( + "Can't create associated type symbol outside of an impl or trait scope.", + ); + }); +}); diff --git a/packages/rust/test/attributes.test.tsx b/packages/rust/test/attributes.test.tsx new file mode 100644 index 000000000..9a72c90dd --- /dev/null +++ b/packages/rust/test/attributes.test.tsx @@ -0,0 +1,124 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + Declaration, + DeriveAttribute, + InnerAttribute, + SourceFile, + StructDeclaration, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +describe("Attribute", () => { + it("renders simple attribute", () => { + expect().toRenderTo(d`#[test]`); + }); + + it("renders attribute with args", () => { + expect().toRenderTo(d`#[cfg(test)]`); + }); + + it("renders refkey attribute names", () => { + const attributeName = refkey("custom-attribute"); + expect( + + + + + fn my_custom_attribute() {`{}`} + + + + + + , + ).toRenderTo(d` + fn my_custom_attribute() {} + #[my_custom_attribute] + `); + }); +}); + +describe("DeriveAttribute", () => { + it("renders single derive trait", () => { + expect().toRenderTo( + d`#[derive(Debug)]`, + ); + }); + + it("renders multiple derive traits", () => { + expect( + , + ).toRenderTo(d`#[derive(Debug, Clone, Serialize)]`); + }); + + it("resolves refkey trait names", () => { + const serializeTrait = refkey("serialize-trait"); + expect( + + + + + trait Serialize {`{}`} + + + + + + , + ).toRenderTo(d` + trait Serialize {} + #[derive(Serialize)] + `); + }); + + it("renders before declarations", () => { + expect( + + + + ]} + derives={["Debug", "Clone"]} + /> + + + , + ).toRenderTo(d` + #[repr(C)] + #[derive(Debug, Clone)] + struct Foo {} + `); + }); +}); + +describe("InnerAttribute", () => { + it("renders simple inner attribute", () => { + expect().toRenderTo(d`#![allow]`); + }); + + it("renders inner attribute with args", () => { + expect().toRenderTo( + d`#![cfg(test)]`, + ); + }); + + it("renders stc inner attribute wrapper", () => { + expect( + Stc.InnerAttribute({ name: "cfg", args: 'feature = "cli"' }), + ).toRenderTo(d`#![cfg(feature = "cli")]`); + }); +}); diff --git a/packages/rust/test/await-expression.test.tsx b/packages/rust/test/await-expression.test.tsx new file mode 100644 index 000000000..4ee172f20 --- /dev/null +++ b/packages/rust/test/await-expression.test.tsx @@ -0,0 +1,75 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + AwaitExpression, + CrateDirectory, + FunctionCallExpression, + FunctionDeclaration, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("AwaitExpression", () => { + it("renders postfix await", () => { + expect( + inFile({code`client.get(url)`}), + ).toRenderTo(d`client.get(url).await`); + }); + + it("renders postfix await with try operator", () => { + expect( + inFile({code`client.get(url)`}), + ).toRenderTo(d`client.get(url).await?`); + }); + + it("composes with other expression components", () => { + expect( + inFile( + + + , + ), + ).toRenderTo(d`client.get(url).await`); + }); + + it("renders inside async function bodies", () => { + expect( + inFile( + + {code`client.get(url)`} + , + ), + ).toRenderTo(d` + async fn fetch_user() { + client.get(url).await? + } + `); + }); + + it("stc wrapper renders await and await? forms", () => { + expect( + inFile( + <> + {Stc.AwaitExpression().children([code`client.get(url)`])} + + {Stc.AwaitExpression({ try: true }).children([code`client.get(url)`])} + , + ), + ).toRenderTo(d` + client.get(url).await + client.get(url).await? + `); + }); +}); diff --git a/packages/rust/test/barrel-exports.test.ts b/packages/rust/test/barrel-exports.test.ts new file mode 100644 index 000000000..0a1edef8d --- /dev/null +++ b/packages/rust/test/barrel-exports.test.ts @@ -0,0 +1,61 @@ +import { + Attribute, + CrateContext, + CrateDirectory, + FunctionSymbol, + NamedTypeSymbol, + PRELUDE_TYPES, + RustCrateScope, + RustImplScope, + RustModuleScope, + RustOutputSymbol, + RustTraitScope, + SourceFile, + StructDeclaration, + createCrate, + createRustNamePolicy, + getCrateInfo, + isParameterDescriptor, + ref, + rustNameConflictResolver, + stc, + useCrateContext, +} from "@alloy-js/rust"; +import * as rustStc from "@alloy-js/rust/stc"; +import { describe, expect, it } from "vitest"; + +describe("@alloy-js/rust barrel exports", () => { + it("resolves key public API exports from package root", () => { + expect(StructDeclaration).toBeTypeOf("function"); + expect(Attribute).toBeTypeOf("function"); + expect(CrateDirectory).toBeTypeOf("function"); + expect(SourceFile).toBeTypeOf("function"); + + expect(RustOutputSymbol).toBeTypeOf("function"); + expect(NamedTypeSymbol).toBeTypeOf("function"); + expect(FunctionSymbol).toBeTypeOf("function"); + expect(RustCrateScope).toBeTypeOf("function"); + expect(RustModuleScope).toBeTypeOf("function"); + expect(RustImplScope).toBeTypeOf("function"); + expect(RustTraitScope).toBeTypeOf("function"); + + expect(createCrate).toBeTypeOf("function"); + expect(getCrateInfo).toBeTypeOf("function"); + expect(createRustNamePolicy).toBeTypeOf("function"); + expect(rustNameConflictResolver).toBeTypeOf("function"); + expect(isParameterDescriptor).toBeTypeOf("function"); + expect(ref).toBeTypeOf("function"); + expect(PRELUDE_TYPES).toBeInstanceOf(Set); + + expect(CrateContext).toBeDefined(); + expect(useCrateContext).toBeTypeOf("function"); + expect(stc).toBeDefined(); + }); + + it("exposes STC wrappers from subpath export", () => { + expect(rustStc.StructDeclaration).toBeTypeOf("function"); + expect(rustStc.FunctionDeclaration).toBeTypeOf("function"); + expect(rustStc.TypeAlias).toBeTypeOf("function"); + expect(rustStc.AwaitExpression).toBeTypeOf("function"); + }); +}); diff --git a/packages/rust/test/block-expression.test.tsx b/packages/rust/test/block-expression.test.tsx new file mode 100644 index 000000000..e15a39b57 --- /dev/null +++ b/packages/rust/test/block-expression.test.tsx @@ -0,0 +1,92 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + BlockExpression, + CrateDirectory, + LetBinding, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("BlockExpression", () => { + it("renders a block with a final expression", () => { + expect(inFile({code`x + y`})) + .toRenderTo(d` + { + x + y + } + `); + }); + + it("renders let bindings and a trailing return expression", () => { + expect( + inFile( + + {code`compute()`} + {code`transform(a)`} + {code`a + b`} + , + ), + ).toRenderTo(d` + { + let a = compute(); + let b = transform(a); + a + b + } + `); + }); + + it("renders empty blocks", () => { + expect(inFile()).toRenderTo(d` + {} + `); + }); + + it("composes inside let bindings", () => { + expect( + inFile( + + + {code`compute()`} + {code`transform(a)`} + {code`a + b`} + + , + ), + ).toRenderTo(d` + let x = { + let a = compute(); + let b = transform(a); + a + b + }; + `); + }); + + it("stc wrapper renders the same output", () => { + expect( + inFile( + Stc.BlockExpression().children([ + {code`5`}, + code`x + 1`, + ]), + ), + ).toRenderTo(d` + { + let x = 5; + x + 1 + } + `); + }); +}); diff --git a/packages/rust/test/break-continue-expression.test.tsx b/packages/rust/test/break-continue-expression.test.tsx new file mode 100644 index 000000000..055164681 --- /dev/null +++ b/packages/rust/test/break-continue-expression.test.tsx @@ -0,0 +1,58 @@ +import { Children, Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + BreakExpression, + ContinueExpression, + CrateDirectory, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("BreakExpression + ContinueExpression", () => { + it("break simple", () => { + expect(inFile()).toRenderTo(d`break`); + }); + + it("break with label and value", () => { + expect( + inFile(result), + ).toRenderTo(d`break 'outer result`); + }); + + it("continue simple", () => { + expect(inFile()).toRenderTo(d`continue`); + }); + + it("continue with label", () => { + expect(inFile()).toRenderTo( + d`continue 'outer`, + ); + }); + + it("stc wrappers exported and render correctly", () => { + expect( + inFile( + <> + {Stc.BreakExpression({ label: "'outer" }).children(["value"])} + + {Stc.ContinueExpression({ label: "'outer" })} + , + ), + ).toRenderTo(d` + break 'outer value + continue 'outer + `); + }); +}); diff --git a/packages/rust/test/builtins.test.tsx b/packages/rust/test/builtins.test.tsx new file mode 100644 index 000000000..71b6f8c3f --- /dev/null +++ b/packages/rust/test/builtins.test.tsx @@ -0,0 +1,199 @@ +import { isRefkeyable, Output, render, type Children } from "@alloy-js/core"; +import { describe, expect, it } from "vitest"; +import { core, std } from "../src/builtins/index.js"; +import { + PRELUDE_TYPES, + PRELUDE_TYPES_2021, + PRELUDE_TYPES_2024, +} from "../src/builtins/prelude.js"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { useCrateContext } from "../src/context/crate-context.js"; +import { RustCrateScope } from "../src/scopes/index.js"; +import { findFile } from "./utils.js"; + +interface ScopeCaptureProps { + onCapture: (crateScope: RustCrateScope) => void; + children?: Children; +} + +function ScopeCapture(props: ScopeCaptureProps) { + const crateScope = useCrateContext()!.scope; + props.onCapture(crateScope); + return <>{props.children}; +} + +describe("std builtins", () => { + it("provides refkeys for described symbols", () => { + // Types in their canonical modules (may be Refkey or RefkeyableObject if they have members) + expect(isRefkeyable(std.option.Option)).toBe(true); + expect(isRefkeyable(std.result.Result)).toBe(true); + expect(isRefkeyable(std.vec.Vec)).toBe(true); + expect(isRefkeyable(std.string.String)).toBe(true); + expect(isRefkeyable(std.boxed.Box)).toBe(true); + + // Nested module symbols + expect(isRefkeyable(std.rc.Rc)).toBe(true); + expect(isRefkeyable(std.sync.Arc)).toBe(true); + expect(isRefkeyable(std.collections.HashMap)).toBe(true); + expect(isRefkeyable(std.collections.BTreeMap)).toBe(true); + expect(isRefkeyable(std.fmt.Display)).toBe(true); + expect(isRefkeyable(std.fmt.Debug)).toBe(true); + expect(isRefkeyable(std.io.Read)).toBe(true); + expect(isRefkeyable(std.io.Write)).toBe(true); + expect(isRefkeyable(std.clone.Clone)).toBe(true); + expect(isRefkeyable(std.default.Default)).toBe(true); + expect(isRefkeyable(std.convert.From)).toBe(true); + expect(isRefkeyable(std.convert.Into)).toBe(true); + }); + + it("references non-prelude std types with use statements and no Cargo.toml dependency", () => { + let consumerCrateScope: RustCrateScope | undefined; + + const output = render( + + + + { + consumerCrateScope = capturedCrateScope; + }} + > + type Map = ; + + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + ["use std::collections::HashMap;", "type Map = HashMap;"].join("\n"), + ); + + // std should NOT appear in Cargo.toml dependencies + expect(consumerCrateScope).toBeDefined(); + expect(consumerCrateScope!.dependencies.has("std")).toBe(false); + }); + + it("references fmt::Display with correct use statement", () => { + const output = render( + + + + type Fmt = ; + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + ["use std::fmt::Display;", "type Fmt = Display;"].join("\n"), + ); + }); +}); + +describe("core builtins", () => { + it("provides refkeys for core symbols", () => { + expect(isRefkeyable(core.option.Option)).toBe(true); + expect(isRefkeyable(core.result.Result)).toBe(true); + expect(isRefkeyable(core.fmt.Display)).toBe(true); + expect(isRefkeyable(core.clone.Clone)).toBe(true); + expect(isRefkeyable(core.marker.Send)).toBe(true); + }); + + it("generates core:: use paths", () => { + const output = render( + + + + type Fmt = ; + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + ["use core::fmt::Display;", "type Fmt = Display;"].join("\n"), + ); + }); +}); + +describe("PRELUDE_TYPES", () => { + it("contains core prelude types, traits, and primitives", () => { + // Core types and variants + for (const type of ["Option", "Some", "None", "Result", "Ok", "Err"]) { + expect(PRELUDE_TYPES.has(type)).toBe(true); + } + // Common structs + for (const type of ["Vec", "String", "Box"]) { + expect(PRELUDE_TYPES.has(type)).toBe(true); + } + // Core traits + for (const type of [ + "Clone", + "Copy", + "Default", + "Drop", + "Eq", + "PartialEq", + "Ord", + "PartialOrd", + "Iterator", + "IntoIterator", + "From", + "Into", + "AsRef", + "AsMut", + "Send", + "Sync", + "Sized", + "Unpin", + "ToOwned", + "ToString", + "Fn", + "FnMut", + "FnOnce", + ]) { + expect(PRELUDE_TYPES.has(type)).toBe(true); + } + // Primitives + for (const type of [ + "bool", + "char", + "f32", + "f64", + "i8", + "i16", + "i32", + "i64", + "i128", + "isize", + "u8", + "u16", + "u32", + "u64", + "u128", + "usize", + "str", + ]) { + expect(PRELUDE_TYPES.has(type)).toBe(true); + } + }); + + it("has edition-specific prelude sets", () => { + // 2021 adds TryFrom, TryInto, FromIterator + expect(PRELUDE_TYPES_2021.has("TryFrom")).toBe(true); + expect(PRELUDE_TYPES_2021.has("TryInto")).toBe(true); + expect(PRELUDE_TYPES_2021.has("FromIterator")).toBe(true); + + // 2024 adds Future, IntoFuture + expect(PRELUDE_TYPES_2024.has("Future")).toBe(true); + expect(PRELUDE_TYPES_2024.has("IntoFuture")).toBe(true); + + // 2024 is a superset of 2021 + for (const type of PRELUDE_TYPES_2021) { + expect(PRELUDE_TYPES_2024.has(type)).toBe(true); + } + }); +}); diff --git a/packages/rust/test/cargo-toml.test.tsx b/packages/rust/test/cargo-toml.test.tsx new file mode 100644 index 000000000..dfb5a0d28 --- /dev/null +++ b/packages/rust/test/cargo-toml.test.tsx @@ -0,0 +1,216 @@ +import { Output, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CargoTomlFile } from "../src/components/cargo-toml-file.js"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { createCrate } from "../src/create-crate.js"; +import { findFile } from "./utils.js"; + +describe("CargoTomlFile", () => { + it("renders package metadata and explicit dependencies with defaults", () => { + expect( + + + , + ).toRenderTo(d` + [package] + name = "my_crate" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + + [dependencies] + serde = "1.0" + tokio = { version = "1.0", features = ["full"] } + `); + }); + + it("merges auto-tracked and explicit dependencies with explicit precedence", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + }, + }, + }); + + const output = render( + + + + type Alias = ; + + + , + ); + + expect(findFile(output, "Cargo.toml").contents.trim()).toBe( + d` + [package] + name = "consumer" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + + [dependencies] + serde = { version = "1.0.200", features = ["derive"] } + tokio = "1.42.0" + `.trim(), + ); + }); + + it("renders deterministic dependency output for tracked crate dependencies", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + }, + }, + }); + + const output = render( + + + + type Alias = ; + + + , + ); + + const cargoToml = findFile(output, "Cargo.toml").contents.trim(); + expect(cargoToml).toBe( + d` + [package] + name = "consumer" + version = "2.0.0" + edition = "2024" + + [lib] + path = "lib.rs" + + [dependencies] + serde = "1.0.219" + `.trim(), + ); + + const secondOutput = render( + + + + type Alias = ; + + + , + ); + + expect(findFile(secondOutput, "Cargo.toml").contents.trim()).toBe( + cargoToml, + ); + }); + + it("renders bin target section with crate name and path", () => { + const output = render( + + + fn main() {} + + , + ); + + expect(findFile(output, "Cargo.toml").contents.trim()).toBe( + d` + [package] + name = "consumer_bin" + version = "0.1.0" + edition = "2021" + + [[bin]] + name = "consumer_bin" + path = "main.rs" + `.trim(), + ); + }); + + it("omits dependencies section when no dependencies are present", () => { + expect( + + + , + ).toRenderTo(d` + [package] + name = "empty_dependencies" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + `); + }); + + it("omits dependencies section when dependencies is an empty object", () => { + expect( + + + , + ).toRenderTo(d` + [package] + name = "empty_map" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + `); + }); + + it("renders crate target section before dependencies", () => { + const output = render( + + + , + ); + + const cargoToml = findFile(output, "Cargo.toml").contents; + expect(cargoToml.indexOf("[lib]")).toBeGreaterThan( + cargoToml.indexOf(`edition = "2021"`), + ); + expect(cargoToml.indexOf("[lib]")).toBeLessThan( + cargoToml.indexOf("[dependencies]"), + ); + }); +}); diff --git a/packages/rust/test/closure-expression.test.tsx b/packages/rust/test/closure-expression.test.tsx new file mode 100644 index 000000000..8b8f97c2b --- /dev/null +++ b/packages/rust/test/closure-expression.test.tsx @@ -0,0 +1,108 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + ClosureExpression, + CrateDirectory, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("ClosureExpression", () => { + it("renders a simple closure", () => { + expect( + inFile( + + {code`value + 1`} + , + ), + ).toRenderTo(d` + |value| value + 1 + `); + }); + + it("renders typed parameters", () => { + expect( + inFile( + + {code`left + right`} + , + ), + ).toRenderTo(d` + |left: i32, right: i32| left + right + `); + }); + + it("renders move closures", () => { + expect( + inFile( + + {code`value > limit`} + , + ), + ).toRenderTo(d` + move |value| value > limit + `); + }); + + it("renders block bodies for multi-statement closures", () => { + expect( + inFile( + + {code`let ttl = entry.ttl;`} + {code`ttl.is_some()`} + , + ), + ).toRenderTo(d` + |entry| { + let ttl = entry.ttl; + ttl.is_some() + } + `); + }); + + it("renders return type closures with a block body", () => { + expect( + inFile( + + {code`value > 0`} + , + ), + ).toRenderTo(d` + |value: i32| -> bool { + value > 0 + } + `); + }); + + it("stc wrappers render the same output", () => { + expect( + inFile( + Stc.ClosureExpression({ parameters: [{ name: "value" }] }).children([ + code`value * 2`, + ]), + ), + ).toRenderTo(d` + |value| value * 2 + `); + }); +}); diff --git a/packages/rust/test/create-crate.test.tsx b/packages/rust/test/create-crate.test.tsx new file mode 100644 index 000000000..9aabb9394 --- /dev/null +++ b/packages/rust/test/create-crate.test.tsx @@ -0,0 +1,244 @@ +import { + createOutputBinder, + getSymbolCreator, + isRefkey, + isRefkeyable, + Output, + render, + type Children, +} from "@alloy-js/core"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { useCrateContext } from "../src/context/crate-context.js"; +import { + createCrate, + getCrateInfo, + getCrateScope, +} from "../src/create-crate.js"; +import { useRustModuleScope } from "../src/scopes/contexts.js"; +import { RustCrateScope, RustModuleScope } from "../src/scopes/index.js"; +import { findFile } from "./utils.js"; + +interface ScopeCaptureProps { + onCapture: (moduleScope: RustModuleScope, crateScope: RustCrateScope) => void; + children?: Children; +} + +function ScopeCapture(props: ScopeCaptureProps) { + const moduleScope = useRustModuleScope(); + const crateScope = useCrateContext()!.scope; + props.onCapture(moduleScope, crateScope); + return <>{props.children}; +} + +function findChildModule( + scope: RustCrateScope | RustModuleScope, + name: string, +): RustModuleScope | undefined { + for (const child of scope.children) { + if (child instanceof RustModuleScope && child.name === name) { + return child; + } + } + return undefined; +} + +describe("createCrate", () => { + it("returns root and module-path refkeys and exposes crate info", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + Deserialize: { kind: "trait" }, + }, + json: { + to_string: { kind: "function" }, + }, + }, + }); + + expect(isRefkey(serde[""].Serialize)).toBe(true); + expect(isRefkey(serde[""].Deserialize)).toBe(true); + expect(isRefkey(serde.json.to_string)).toBe(true); + expect(getCrateInfo(serde)).toEqual({ name: "serde", version: "1.0.219" }); + }); + + it("creates crate symbols once per binder and maps module hierarchy", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + }, + json: { + to_string: { kind: "function" }, + }, + }, + }); + + const binder = createOutputBinder(); + getSymbolCreator(serde)(binder); + + const crateScope = getCrateScope(serde, binder)!; + expect(crateScope).toBeDefined(); + expect(findChildModule(crateScope, "json")).toBeDefined(); + + const rootResolution = binder.resolveDeclarationByKey( + undefined, + serde[""].Serialize, + ).value; + expect(rootResolution?.symbol.name).toBe("Serialize"); + + const jsonResolution = binder.resolveDeclarationByKey( + undefined, + serde.json.to_string, + ).value; + expect(jsonResolution?.symbol.name).toBe("to_string"); + expect(jsonResolution?.pathDown.map((scope) => scope.name)).toEqual([ + "serde", + "json", + ]); + + const jsonModule = findChildModule(crateScope, "json")!; + expect(jsonModule.values.symbolNames.has("to_string")).toBe(true); + + getSymbolCreator(serde)(binder); + expect(jsonModule.values.symbolNames.size).toBe(1); + }); + + it("maps root and nested module paths for external references and tracks dependency version", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + }, + de: { + Deserializer: { kind: "trait" }, + }, + }, + }); + + let consumerModuleScope: RustModuleScope | undefined; + let consumerCrateScope: RustCrateScope | undefined; + + const output = render( + + + + { + consumerModuleScope = capturedModuleScope; + consumerCrateScope = capturedCrateScope; + }} + > + type RootAlias = ; + + type NestedAlias = ; + + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + [ + "use serde::Serialize;", + "use serde::de::Deserializer;", + "type RootAlias = Serialize;", + "type NestedAlias = Deserializer;", + ].join("\n"), + ); + + expect(consumerModuleScope).toBeDefined(); + expect(consumerCrateScope).toBeDefined(); + expect(consumerModuleScope!.imports.get("serde")?.size).toBe(1); + expect(consumerModuleScope!.imports.get("serde::de")?.size).toBe(1); + expect(consumerCrateScope!.dependencies.get("serde")).toBe("1.0.219"); + }); + + it("references builtin crate symbols without tracking Cargo.toml dependencies", () => { + const std = createCrate({ + name: "std", + builtin: true, + modules: { + collections: { + HashMap: { kind: "struct" }, + }, + }, + }); + + let consumerCrateScope: RustCrateScope | undefined; + + const output = render( + + + + { + consumerCrateScope = capturedCrateScope; + }} + > + type DataMap = ; + + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + ["use std::collections::HashMap;", "type DataMap = HashMap;"].join("\n"), + ); + expect(consumerCrateScope).toBeDefined(); + expect(consumerCrateScope!.dependencies.has("std")).toBe(false); + }); + + it("creates member symbols and exposes member refkeys on types with members", () => { + const std = createCrate({ + name: "std", + builtin: true, + modules: { + collections: { + HashMap: { + kind: "struct", + members: { + new: { kind: "function", associated: true }, + insert: { kind: "function" }, + get: { kind: "function" }, + len: { kind: "function" }, + }, + }, + }, + }, + }); + + // The type itself is refkeyable (can be passed to ) + expect(isRefkeyable(std.collections.HashMap)).toBe(true); + // Member refkeys are proper refkeys + expect(isRefkey(std.collections.HashMap.new)).toBe(true); + expect(isRefkey(std.collections.HashMap.insert)).toBe(true); + expect(isRefkey(std.collections.HashMap.get)).toBe(true); + expect(isRefkey(std.collections.HashMap.len)).toBe(true); + + // The type refkey itself should still work for references + const output = render( + + + + type Map = ; + + + , + ); + + expect(findFile(output, "lib").contents.trim()).toBe( + ["use std::collections::HashMap;", "type Map = HashMap;"].join("\n"), + ); + }); +}); diff --git a/packages/rust/test/declaration-reference.test.tsx b/packages/rust/test/declaration-reference.test.tsx new file mode 100644 index 000000000..fc5d3d5c4 --- /dev/null +++ b/packages/rust/test/declaration-reference.test.tsx @@ -0,0 +1,112 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Declaration } from "../src/components/declaration.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; + +describe("Declaration", () => { + it("renders no visibility prefix by default", () => { + expect( + + + + + struct Thing; + + + + , + ).toRenderTo(d`struct Thing;`); + }); + + it("renders pub visibility prefix", () => { + expect( + + + + + struct Thing; + + + + , + ).toRenderTo(d`pub struct Thing;`); + }); + + it("renders pub(crate) visibility prefix", () => { + expect( + + + + + struct Thing; + + + + , + ).toRenderTo(d`pub(crate) struct Thing;`); + }); + + it("renders pub(super) visibility prefix", () => { + expect( + + + + + struct Thing; + + + + , + ).toRenderTo(d`pub(super) struct Thing;`); + }); +}); + +describe("Reference", () => { + it("resolves explicit Reference component to symbol name", () => { + const userType = refkey("user-type"); + expect( + + + + + struct UserType; + + + type Alias = ; + + + , + ).toRenderTo(d` + struct UserType; + type Alias = UserType; + `); + }); + + it("resolves inline refkeys through SourceFile reference component", () => { + const responseType = refkey("response-type"); + expect( + + + + + struct ResponseType; + + + type Alias = {responseType}; + + + , + ).toRenderTo(d` + struct ResponseType; + type Alias = ResponseType; + `); + }); +}); diff --git a/packages/rust/test/doc-comment.test.tsx b/packages/rust/test/doc-comment.test.tsx new file mode 100644 index 000000000..d91199dc0 --- /dev/null +++ b/packages/rust/test/doc-comment.test.tsx @@ -0,0 +1,301 @@ +import { Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + BlockComment, + CrateDirectory, + DocComment, + InnerBlockDocComment, + InnerDocComment, + LineComment, + OuterBlockDocComment, + SourceFile, +} from "../src/components/index.js"; + +describe("LineComment", () => { + it("renders a single line", () => { + expect( + + + + Hello + + + , + ).toRenderTo("// Hello\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("// Line 1\n// Line 2\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); + +describe("BlockComment", () => { + it("renders a single line", () => { + expect( + + + + Hello + + + , + ).toRenderTo("/*\n * Hello\n */\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("/*\n * Line 1\n * Line 2\n */\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); + +describe("DocComment", () => { + it("renders a single line", () => { + expect( + + + + Hello + + + , + ).toRenderTo("/// Hello\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("/// Line 1\n/// Line 2\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); + +describe("InnerDocComment", () => { + it("renders a single line", () => { + expect( + + + + Module docs + + + , + ).toRenderTo("//! Module docs\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("//! Line 1\n//! Line 2\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); + +describe("OuterBlockDocComment", () => { + it("renders a single line", () => { + expect( + + + + Hello + + + , + ).toRenderTo("/**\n * Hello\n */\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("/**\n * Line 1\n * Line 2\n */\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); + +describe("InnerBlockDocComment", () => { + it("renders a single line", () => { + expect( + + + + Hello + + + , + ).toRenderTo("/*!\n * Hello\n */\n\n"); + }); + + it("renders multiple lines", () => { + expect( + + + + {"Line 1\nLine 2"} + + + , + ).toRenderTo("/*!\n * Line 1\n * Line 2\n */\n\n"); + }); + + it("renders nothing for empty or undefined children", () => { + expect( + + + + + + + , + ).toRenderTo(""); + + expect( + + + + {""} + + + , + ).toRenderTo(""); + }); +}); diff --git a/packages/rust/test/edge-cases.test.tsx b/packages/rust/test/edge-cases.test.tsx new file mode 100644 index 000000000..37dee9416 --- /dev/null +++ b/packages/rust/test/edge-cases.test.tsx @@ -0,0 +1,278 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + Declaration, + EnumDeclaration, + EnumVariant, + Field, + FunctionDeclaration, + ImplBlock, + Reference, + SourceFile, + StructDeclaration, +} from "../src/components/index.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { createRustNamePolicy } from "../src/name-policy.js"; +import { findFile } from "./utils.js"; + +describe("Rust edge cases", () => { + describe("empty and minimal constructs", () => { + it("renders empty struct, empty enum, and empty function", () => { + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Empty {} + enum Never {} + fn noop() {} + `); + }); + + it("renders minimal single-field and single-variant constructs", () => { + expect( + + + + + + + + + + + + + , + ).toRenderTo(d` + struct Wrapper { + value: i32, + } + enum Unit { + Only, + } + `); + }); + }); + + describe("reserved word escaping", () => { + it("escapes type and related reserved identifiers with r# prefix", () => { + const namePolicy = createRustNamePolicy(); + const reservedWords = [ + "type", + "self", + "super", + "crate", + "fn", + "struct", + "enum", + "trait", + "impl", + ] as const; + + for (const reservedWord of reservedWords) { + expect(namePolicy.getName(reservedWord, "variable")).toBe( + `r#${reservedWord}`, + ); + } + }); + }); + + describe("imports and references", () => { + it("deduplicates duplicate references to the same symbol", () => { + const userRef = refkey("edge-user"); + + const output = render( + + + + + + pub struct User; + + + + + + type FirstAlias = ; + + type SecondAlias = ; + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + type FirstAlias = User; + type SecondAlias = User; + `.trim(), + ); + }); + + it("groups imports from the same module path", () => { + const userRef = refkey("edge-user"); + const accountRef = refkey("edge-account"); + + const output = render( + + + + + + pub struct User; + + + + pub struct Account; + + + + + + type UserAlias = ; + + type AccountAlias = ; + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::{Account, User}; + type UserAlias = User; + type AccountAlias = Account; + `.trim(), + ); + }); + + it("generates use statements for same-crate types shadowing prelude names", () => { + const optionRef = refkey("edge-option"); + const vecRef = refkey("edge-vec"); + const stringRef = refkey("edge-string"); + + const output = render( + + + + + pub struct Option; + + + + pub struct Vec; + + + + pub struct String; + + + + type Maybe = ; + + type Items = ; + + type Name = ; + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + use crate::types::{Option, String, Vec}; + type Maybe = Option; + type Items = Vec; + type Name = String; + `.trim(), + ); + }); + + it("throws when referencing a private symbol across modules", () => { + const privateType = refkey("edge-private-model"); + + expect(() => + render( + + + + + struct PrivateModel; + + + + type Alias = ; + + + , + ), + ).toThrowError("Cannot reference private symbol 'PrivateModel'"); + }); + }); + + describe("multiple impl blocks", () => { + it("renders multiple impl blocks for the same type", () => { + const pointRef = refkey("edge-point"); + + expect( + + + + + + + + + + + + + + + , + ).toRenderTo(d` + struct Point {} + impl Point { + fn new() {} + } + impl Point { + fn distance(&self) {} + } + `); + }); + }); +}); diff --git a/packages/rust/test/enum.test.tsx b/packages/rust/test/enum.test.tsx new file mode 100644 index 000000000..cef1832e7 --- /dev/null +++ b/packages/rust/test/enum.test.tsx @@ -0,0 +1,383 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + EnumDeclaration, + EnumVariant, + SourceFile, +} from "../src/components/index.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; + +function EnumKindProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return symbol.typeKind; + } + } + + return "missing"; +} + +function EnumVisibilityProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return symbol.visibility ?? "none"; + } + } + + return "missing"; +} + +describe("EnumDeclaration", () => { + it("renders empty enum", () => { + expect( + + + + + + + , + ).toRenderTo(d`enum Foo {}`); + }); + + it("renders pub enum", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub enum Foo {}`); + }); + + it("renders pub(super) enum", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub(super) enum Foo {}`); + }); + + it("renders derives and attributes", () => { + expect( + + + + + + + , + ).toRenderTo(d` + #[repr(u8)] + #[derive(Debug, Clone)] + enum Foo {} + `); + }); + + it("renders enum doc comment", () => { + expect( + + + + + + + , + ).toRenderTo(d` + /// Represents status. + enum Foo {} + `); + }); + + it("renders type parameters", () => { + expect( + + + + + + + , + ).toRenderTo(d`enum Result {}`); + }); + + it("creates a NamedTypeSymbol with enum typeKind", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + enum Status {} + enum + `); + }); + + it("applies pub visibility on enum symbols", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + pub enum Status {} + pub + `); + }); +}); + +describe("EnumVariant", () => { + it("renders unit variant", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + enum Status { + Pending, + } + `); + }); + + it("renders tuple variant", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + enum Message { + Text(String, i32), + } + `); + }); + + it("renders tuple variant from children when kind is tuple", () => { + expect( + + + + + + {"String"} + + + + + , + ).toRenderTo(d` + enum Message { + Text(String), + } + `); + }); + + it("renders struct variant with fields", () => { + expect( + + + + + + {"id: u64,"} + {"payload: String,"} + + + + + , + ).toRenderTo(d` + enum Message { + Data { + id: u64, + payload: String, + }, + } + `); + }); + + it("renders doc comment above variant", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + enum Status { + /// Waiting for processing. + Pending, + } + `); + }); + + it("renders mixed variant kinds", () => { + expect( + + + + + + + + {"code: u32,"} + {"message: String,"} + + + + + , + ).toRenderTo(d` + pub enum Event { + Ready, + Data(String), + Error { + code: u32, + message: String, + }, + } + `); + }); + + it("resolves variant references by refkey", () => { + const pending = refkey("variant-pending"); + + expect( + + + + + + + + {pending} + + + , + ).toRenderTo(d` + enum Status { + Pending, + } + Status::Pending + `); + }); + + it("renders attributes on enum variant", () => { + expect( + + + + + ]} + /> + + + + + , + ).toRenderTo(d` + enum Status { + #[deprecated] + Active, + Inactive, + } + `); + }); + + it("renders attributes with doc on enum variant", () => { + expect( + + + + + ]} + /> + + + + , + ).toRenderTo(d` + enum Color { + /// The red color. + #[serde(rename = "red")] + Red, + } + `); + }); + + it("renders attributes on tuple variant", () => { + expect( + + + + + "]} + attributes={[]} + /> + + + + , + ).toRenderTo(d` + enum Message { + #[allow(dead_code)] + Data(Vec), + } + `); + }); +}); diff --git a/packages/rust/test/for-expression.test.tsx b/packages/rust/test/for-expression.test.tsx new file mode 100644 index 000000000..9880ce79c --- /dev/null +++ b/packages/rust/test/for-expression.test.tsx @@ -0,0 +1,94 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + ForExpression, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("ForExpression", () => { + it("renders a simple for loop", () => { + expect( + inFile( + + {code`process(item);`} + , + ), + ).toRenderTo(d` + for item in collection { + process(item); + } + `); + }); + + it("renders for loop with destructuring pattern", () => { + expect( + inFile( + + {code`println!("{}: {}", i, val);`} + , + ), + ).toRenderTo(d` + for (i, val) in list.iter().enumerate() { + println!("{}: {}", i, val); + } + `); + }); + + it("renders for loop with label", () => { + expect( + inFile( + + {code`do_work(x);`} + , + ), + ).toRenderTo(d` + 'outer: for x in 0..10 { + do_work(x); + } + `); + }); + + it("renders multi-statement loop body", () => { + expect( + inFile( + + {code`let processed = transform(item);`} + {code`results.push(processed);`} + , + ), + ).toRenderTo(d` + for item in items { + let processed = transform(item); + results.push(processed); + } + `); + }); + + it("stc wrapper renders the same output", () => { + expect( + inFile( + Stc.ForExpression({ pattern: "x", iterator: "vec" }).children([ + 'println!("{}", x);', + ]), + ), + ).toRenderTo(d` + for x in vec { + println!("{}", x); + } + `); + }); +}); diff --git a/packages/rust/test/function-call-expression.test.tsx b/packages/rust/test/function-call-expression.test.tsx new file mode 100644 index 000000000..6c43866d1 --- /dev/null +++ b/packages/rust/test/function-call-expression.test.tsx @@ -0,0 +1,56 @@ +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { FunctionCallExpression } from "../src/components/function-call-expression.js"; + +describe("FunctionCallExpression", () => { + it("renders no-arg calls", () => { + expect().toRenderTo( + d`self.data.len()`, + ); + }); + + it("renders calls with arguments", () => { + expect( + , + ).toRenderTo(d`self.data.insert(key, entry)`); + }); + + it("renders turbofish type arguments without call arguments", () => { + expect( + "]} />, + ).toRenderTo(d`collect::>()`); + }); + + it("renders turbofish type arguments with call arguments", () => { + expect( + , + ).toRenderTo(d`f::(raw, 10)`); + }); + + it("wraps multiple arguments across lines", () => { + expect( + , + ).toRenderTo(d` + self.data.insert( + key, + entry, + Context::new(session_id, metadata, now, source, trace_id, actor) + ) + `); + }); +}); diff --git a/packages/rust/test/function.test.tsx b/packages/rust/test/function.test.tsx new file mode 100644 index 000000000..d84276b2b --- /dev/null +++ b/packages/rust/test/function.test.tsx @@ -0,0 +1,445 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + FunctionDeclaration, + ImplBlock, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; +import { + RustFunctionScope, + useRustModuleScope, + useRustScope, +} from "../src/scopes/index.js"; +import { FunctionSymbol } from "../src/symbols/function-symbol.js"; + +function FunctionFlagsProbe(props: { name: string }) { + const scope = useRustModuleScope(); + + for (const symbol of scope.values) { + if (symbol instanceof FunctionSymbol && symbol.name === props.name) { + return `${symbol.visibility ?? "none"}|${symbol.isAsync}|${symbol.isUnsafe}|${symbol.isConst}`; + } + } + + return "missing"; +} + +function ParameterNamesProbe() { + const scope = useRustScope(); + if (!(scope instanceof RustFunctionScope)) { + return "not-function-scope"; + } + + return [...scope.parameters].map((symbol) => symbol.name).join(","); +} + +describe("FunctionDeclaration", () => { + it("renders basic function with empty body", () => { + expect( + + + + + + + , + ).toRenderTo(d`fn foo() {}`); + }); + + it("renders qualifiers in rust order", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub(crate) async unsafe const fn work() {}`); + }); + + it("renders pub(super) visibility with qualifiers in rust order", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub(super) async unsafe const fn work() {}`); + }); + + it("renders parameters from descriptors", () => { + expect( + + + + + + + , + ).toRenderTo(d`fn process(mut input-value: &mut String, count: usize) {}`); + }); + + it("renders return type, type parameters, and where clause", () => { + expect( + + + + + + + , + ).toRenderTo(d`fn map(item: T) -> U where U: Clone {}`); + }); + + it("renders multiline doc comments and indented body", () => { + expect( + + + + + {"let value = 1;"} + + {"value"} + + + + , + ).toRenderTo(d` + /// Line one. + /// Line two. + fn run() { + let value = 1; + value + } + `); + }); + + it("creates function and parameter symbols in function scope", () => { + expect( + + + + + + + + + + + , + ).toRenderTo(d` + pub(crate) async unsafe const fn run-work(input-value: i32) { + input-value + } + pub(crate)|true|true|true + `); + }); + + it("applies pub visibility on function symbols", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + pub fn run-work() {} + pub|false|false|false + `); + }); + + it("defaults to &self receiver for methods in impl blocks", () => { + const itemRef = refkey("item"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Item {} + impl Item { + fn run(&self) {} + } + `); + }); + + it("supports explicit &mut self receiver in methods", () => { + const itemRef = refkey("item"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Item {} + impl Item { + fn run(&mut self) {} + } + `); + }); + + it("supports explicit self receiver by value in methods", () => { + const itemRef = refkey("item"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Item {} + impl Item { + fn consume(self) {} + } + `); + }); + + it("supports associated functions with receiver none in methods", () => { + const itemRef = refkey("item"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Item {} + impl Item { + fn new() {} + } + `); + }); + + it("renders receiver before additional parameters", () => { + const itemRef = refkey("item"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Item {} + impl Item { + fn set(&self, x: i32, y: i32) {} + } + `); + }); + + it("defaults to &self receiver for methods in traits", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + trait Runner { + fn run(&self); + } + `); + }); + + it("renders default implementations for trait methods with bodies", () => { + expect( + + + + + + {'println!("default");'} + + + + + , + ).toRenderTo(d` + trait Runner { + fn run(&self) { + println!("default"); + } + } + `); + }); + + it("ignores receiver prop outside impl and trait scopes", () => { + expect( + + + + + + + , + ).toRenderTo(d`fn utility() {}`); + }); + + it("renders attributes before function declaration", () => { + expect( + + + + ]} + /> + + + , + ).toRenderTo(d` + #[test] + fn test_it() {} + `); + }); + + it("renders attributes with doc comment", () => { + expect( + + + + ]} + > + {"todo!()"} + + + + , + ).toRenderTo(d` + /// Runs the process. + #[inline] + fn run() { + todo!() + } + `); + }); + + it("renders multiple attributes", () => { + expect( + + + + , + , + ]} + /> + + + , + ).toRenderTo(d` + #[cfg(feature = "server")] + #[allow(unused_variables)] + pub async fn handler() {} + `); + }); +}); diff --git a/packages/rust/test/golden-scenarios.test.tsx b/packages/rust/test/golden-scenarios.test.tsx new file mode 100644 index 000000000..041da9db7 --- /dev/null +++ b/packages/rust/test/golden-scenarios.test.tsx @@ -0,0 +1,249 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + EnumDeclaration, + EnumVariant, + Field, + FunctionDeclaration, + ImplBlock, + ModuleDirectory, + Reference, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; +import { createCrate } from "../src/create-crate.js"; +import { findFile } from "./utils.js"; + +describe("Golden scenarios", () => { + it("7.1 renders struct with inherent impl", () => { + const pointRef = refkey("point"); + + expect( + + + + + + + + + + + {"Self { x, y }"} + + + + {"let dx = self.x - other.x;"} + + {"let dy = self.y - other.y;"} + + {"(dx * dx + dy * dy).sqrt()"} + + + + + , + ).toRenderTo(d` + pub struct Point { + pub x: f64, + pub y: f64, + } + impl Point { + pub fn new(x: f64, y: f64) -> Self { + Self { x, y } + } + fn distance(&self, other: &Point) -> f64 { + let dx = self.x - other.x; + let dy = self.y - other.y; + (dx * dx + dy * dy).sqrt() + } + } + `); + }); + + it("7.2 renders multi-module crate with imports", () => { + const output = render( + + + + + + + + + + + + + {"use crate::models::User;"} + + + {'format!("Hello, {}!", user.name)'} + + + + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + mod models; + mod services; + `.trim(), + ); + expect(findFile(output, "models/mod.rs").contents.trim()).toBe( + d` + pub struct User { + pub name: String, + pub age: u32, + } + `.trim(), + ); + expect(findFile(output, "services/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + pub fn greet(user: &User) -> String { + format!("Hello, {}!", user.name) + } + `.trim(), + ); + }); + + it("7.3 renders trait declaration and trait impl", () => { + const greetableRef = refkey("greetable"); + const userRef = refkey("user"); + + expect( + + + + + {"fn greet(&self) -> String;"} + + + + + + + + + {'format!("Hello, {}!", self.name)'} + + + + + , + ).toRenderTo(d` + trait Greetable { + fn greet(&self) -> String; + } + pub struct User { + pub name: String, + } + impl Greetable for User { + fn greet(&self) -> String { + format!("Hello, {}!", self.name) + } + } + `); + }); + + it("7.4 renders enum with struct, tuple, and unit variants", () => { + expect( + + + + + {"radius: f64,"} + + {"width: f64,"} + {"height: f64,"} + + + + + + + , + ).toRenderTo(d` + pub enum Shape { + Circle { + radius: f64, + }, + Rectangle { + width: f64, + height: f64, + }, + Point(f64, f64), + Nothing, + } + `); + }); + + it("7.5 renders Cargo.toml with external dependency usage", () => { + const serde = createCrate({ + name: "serde", + version: "1.0.219", + modules: { + "": { + Serialize: { kind: "trait" }, + }, + }, + }); + + const output = render( + + + + type Serializable = ; + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + use serde::Serialize; + type Serializable = Serialize; + `.trim(), + ); + expect(findFile(output, "Cargo.toml").contents.trim()).toBe( + d` + [package] + name = "consumer" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + + [dependencies] + serde = "1.0.219" + `.trim(), + ); + }); +}); diff --git a/packages/rust/test/if-expression.test.tsx b/packages/rust/test/if-expression.test.tsx new file mode 100644 index 000000000..bdb58f28f --- /dev/null +++ b/packages/rust/test/if-expression.test.tsx @@ -0,0 +1,96 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + ElseClause, + ElseIfClause, + IfExpression, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("IfExpression", () => { + it("renders a simple if expression", () => { + expect( + inFile( + + {code`return Err(StoreError::StorageFull);`} + , + ), + ).toRenderTo(d` + if self.data.len() >= self.max_capacity { + return Err(StoreError::StorageFull); + } + `); + }); + + it("renders chained else-if and else clauses from children", () => { + expect( + inFile( + + {code`Err(StoreError::NotFound)`} + {code`Err(StoreError::NotFound)`} + {code`Ok(&entry.value)`} + , + ), + ).toRenderTo(d` + if entry.status == EntryStatus::Expired { + Err(StoreError::NotFound) + } else if entry.is_stale() { + Err(StoreError::NotFound) + } else { + Ok(&entry.value) + } + `); + }); + + it("supports if-let and nested if expressions", () => { + expect( + inFile( + + + {code`return Err(StoreError::NotFound);`} + + , + ), + ).toRenderTo(d` + if let Some(ttl) = entry.ttl { + if entry.created_at.elapsed() > ttl { + return Err(StoreError::NotFound); + } + } + `); + }); + + it("stc wrappers render the same output", () => { + expect( + inFile( + Stc.IfExpression({ condition: "value > 10" }).children([ + "value", + Stc.ElseIfClause({ condition: "value > 0" }).children(["0"]), + Stc.ElseClause().children(["-1"]), + ]), + ), + ).toRenderTo(d` + if value > 10 { + value + } else if value > 0 { + 0 + } else { + -1 + } + `); + }); +}); diff --git a/packages/rust/test/impl.test.tsx b/packages/rust/test/impl.test.tsx new file mode 100644 index 000000000..ff2407160 --- /dev/null +++ b/packages/rust/test/impl.test.tsx @@ -0,0 +1,331 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + EnumDeclaration, + FunctionDeclaration, + ImplBlock, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; + +function TypeMemberNamesProbe(props: { name: string }) { + const scope = useRustModuleScope(); + + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return [...symbol.members].map((member) => member.name).join(","); + } + } + + return "missing"; +} + +describe("ImplBlock", () => { + it("renders inherent impl", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + impl Foo { + fn new(&self) {} + } + `); + }); + + it("renders trait impl", () => { + const typeRef = refkey("foo-type"); + const traitRef = refkey("printable-trait"); + + expect( + + + + + + + + + + + + + , + ).toRenderTo(d` + trait Printable {} + struct Foo {} + impl Printable for Foo { + fn print(&self) {} + } + `); + }); + + it("renders type parameters", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + impl Foo {} + `); + }); + + it("renders where clause", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + impl Foo where T: Clone {} + `); + }); + + it("adds methods in impl blocks to the target type members", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + impl Foo { + fn run(&self) {} + } + run + `); + }); + + it("supports type as both refkey and inline children", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + impl Foo {} + impl Vec {} + `); + }); + + it("supports trait as both refkey and inline children", () => { + const typeRef = refkey("foo-type"); + const traitRef = refkey("displayable-trait"); + + expect( + + + + + + + + + + + + + , + ).toRenderTo(d` + trait Displayable {} + struct Foo {} + impl Displayable for Foo {} + impl Display for Vec {} + `); + }); + + it("forwards type parameters from generic structs to impl blocks", () => { + const typeRef = refkey("store-type"); + + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Store {} + impl Store { + fn new(&self) {} + } + `); + }); + + it("forwards type parameters for trait impls", () => { + const typeRef = refkey("store-type"); + const traitRef = refkey("displayable-trait"); + + expect( + + + + + + + + + + + + + , + ).toRenderTo(d` + trait Displayable {} + struct Store {} + impl Displayable for Store { + fn fmt(&self) {} + } + `); + }); + + it("forwards type parameters from generic enums to impl blocks and keeps where clauses", () => { + const enumRef = refkey("result-type"); + + expect( + + + + + + + + + , + ).toRenderTo(d` + enum Result {} + impl Result where T: Clone {} + `); + }); + + it("renders attributes before impl block", () => { + const typeRef = refkey("foo-type"); + + expect( + + + + + + ]} + /> + + + , + ).toRenderTo(d` + struct Foo {} + #[automatically_derived] + impl Foo {} + `); + }); + + it("renders attributes on trait impl", () => { + const typeRef = refkey("foo-type"); + const traitRef = refkey("display-trait"); + + expect( + + + + + + + + ]} + > + + + + + , + ).toRenderTo(d` + trait Display {} + struct Foo {} + #[automatically_derived] + impl Display for Foo { + fn fmt(&self) {} + } + `); + }); +}); diff --git a/packages/rust/test/imports.test.tsx b/packages/rust/test/imports.test.tsx new file mode 100644 index 000000000..1a04feb94 --- /dev/null +++ b/packages/rust/test/imports.test.tsx @@ -0,0 +1,217 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Declaration } from "../src/components/declaration.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { findFile } from "./utils.js"; + +describe("Rust imports integration", () => { + it("does not generate use statements for same-module references", () => { + const userRef = refkey("user"); + + const output = render( + + + + + struct User; + + + type UserAlias = ; + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + pub struct User; + type UserAlias = User; + `.trim(), + ); + }); + + it("generates crate use for same-crate references across modules", () => { + const userRef = refkey("user"); + + const output = render( + + + + + + pub struct User; + + + + + + type UserAlias = ; + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + type UserAlias = User; + `.trim(), + ); + }); + + it("groups multiple imports from same module path with braces", () => { + const userRef = refkey("user"); + const accountRef = refkey("account"); + + const output = render( + + + + + + pub struct User; + + + + pub struct Account; + + + + + + type UserAlias = ; + + type AccountAlias = ; + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::{Account, User}; + type UserAlias = User; + type AccountAlias = Account; + `.trim(), + ); + }); + + it("sorts imports by std, external, then crate groups with blank lines", () => { + const mapRef = refkey("hash-map"); + const deserializeRef = refkey("deserialize"); + const userRef = refkey("user"); + + const output = render( + + + + + + pub struct HashMap; + + + + + + + + + pub trait DeserializeOwned {} + + + + + + + + + pub struct User; + + + + + + type MapAlias = ; + + type DeserializeAlias = ; + + type UserAlias = ; + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use std::collections::HashMap; + + use serde::de::DeserializeOwned; + + use crate::models::User; + type MapAlias = HashMap; + type DeserializeAlias = DeserializeOwned; + type UserAlias = User; + `.trim(), + ); + }); + + it("generates use statements for same-crate types that shadow prelude names", () => { + const optionRef = refkey("option"); + const resultRef = refkey("result"); + const vecRef = refkey("vec"); + + const output = render( + + + + + pub struct Option; + + + + pub enum Result {} + + + + pub struct Vec; + + + + type OptionAlias = ; + + type ResultAlias = ; + + type VecAlias = ; + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + use crate::types::{Option, Result, Vec}; + type OptionAlias = Option; + type ResultAlias = Result; + type VecAlias = Vec; + `.trim(), + ); + }); +}); diff --git a/packages/rust/test/let-binding.test.tsx b/packages/rust/test/let-binding.test.tsx new file mode 100644 index 000000000..fc6083895 --- /dev/null +++ b/packages/rust/test/let-binding.test.tsx @@ -0,0 +1,78 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + LetBinding, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("LetBinding", () => { + it("renders a simple let binding", () => { + expect( + inFile({code`self.data.len()`}), + ).toRenderTo(d` + let before = self.data.len(); + `); + }); + + it("renders mutable bindings", () => { + expect( + inFile( + {code`Entry::default()`}, + ), + ).toRenderTo(d` + let mut entry = Entry::default(); + `); + }); + + it("renders type annotations", () => { + expect( + inFile( + {code`Entry::default()`}, + ), + ).toRenderTo(d` + let entry: Entry = Entry::default(); + `); + }); + + it("renders let binding without initializer", () => { + expect(inFile()).toRenderTo(d` + let slot; + `); + }); + + it("renders destructuring patterns", () => { + expect(inFile({code`pair`})) + .toRenderTo(d` + let (key, value) = pair; + `); + }); + + it("stc wrapper renders the same output", () => { + expect( + inFile( + Stc.LetBinding({ name: "count", mutable: true }).children([ + "items.len()", + ]), + ), + ).toRenderTo(d` + let mut count = items.len(); + `); + }); +}); diff --git a/packages/rust/test/lifetime-parameters.test.tsx b/packages/rust/test/lifetime-parameters.test.tsx new file mode 100644 index 000000000..f11f41b49 --- /dev/null +++ b/packages/rust/test/lifetime-parameters.test.tsx @@ -0,0 +1,68 @@ +import { Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + EnumDeclaration, + FunctionDeclaration, + ImplBlock, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; + +describe("lifetime parameter integration", () => { + it("renders lifetime parameters for function, struct, enum, trait, and impl block", () => { + expect( + + + + + + + + + + + + + + + , + ).toRenderTo(d` + fn longest<'a>(a: &'a str, b: &'a str) -> &'a str {} + struct Ref<'a, T: 'a + Clone> {} + enum EitherRef<'a, T> {} + trait Reader<'a, T> {} + impl<'a, 'b: 'a, T: 'a + Clone> Ref<'a, T> {} + `); + }); +}); diff --git a/packages/rust/test/macro-call.test.tsx b/packages/rust/test/macro-call.test.tsx new file mode 100644 index 000000000..d8adeed23 --- /dev/null +++ b/packages/rust/test/macro-call.test.tsx @@ -0,0 +1,77 @@ +import { Children, Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + MacroCall, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("MacroCall", () => { + it("renders macro call with paren brackets by default", () => { + expect( + inFile( + , + ), + ).toRenderTo(d`format!("store::{}", self.data.len())`); + }); + + it("renders macro call with bracket delimiters", () => { + expect( + inFile(), + ).toRenderTo(d`vec![1, 2, 3]`); + }); + + it("renders macro call with brace delimiters", () => { + expect( + inFile(), + ).toRenderTo(d`cfg!{test}`); + }); + + it("renders macro call with no arguments", () => { + expect(inFile()).toRenderTo(d`todo!()`); + }); + + it("wraps multiple arguments across lines", () => { + expect( + inFile( + , + ), + ).toRenderTo(d` + println!( + "Long message: {:?}", + var1, + var2, + var3, + Context::new(session_id, metadata, now, source, trace_id, actor) + ) + `); + }); + + it("stc wrapper renders correctly", () => { + expect( + inFile(Stc.MacroCall({ name: "println", args: ['"hello"', "name"] })), + ).toRenderTo(d`println!("hello", name)`); + }); +}); diff --git a/packages/rust/test/match-expression.test.tsx b/packages/rust/test/match-expression.test.tsx new file mode 100644 index 000000000..6fd73776f --- /dev/null +++ b/packages/rust/test/match-expression.test.tsx @@ -0,0 +1,93 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + MatchArm, + MatchExpression, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("MatchExpression", () => { + it("renders inline match arms", () => { + expect( + inFile( + + {code`write!(f, "key not found")`} + {code`write!(f, "storage is full")`} + + {code`write!(f, "serialization error: {}", msg)`} + + , + ), + ).toRenderTo(d` + match self { + Self::NotFound => write!(f, "key not found"), + Self::StorageFull => write!(f, "storage is full"), + Self::SerializationError(msg) => write!(f, "serialization error: {}", msg), + } + `); + }); + + it("renders guard clauses", () => { + expect( + inFile( + + + {code`Err(StoreError::NotFound)`} + + , + ), + ).toRenderTo(d` + match entry { + Some(e) if e.is_expired() => Err(StoreError::NotFound), + } + `); + }); + + it("renders block arms for multi-statement bodies", () => { + expect( + inFile( + + + {code`println!("got value");`} + {code`x + 1`} + + , + ), + ).toRenderTo(d` + match option { + Some(x) => { + println!("got value"); + x + 1 + }, + } + `); + }); + + it("stc wrappers render the same output", () => { + expect( + inFile( + Stc.MatchExpression({ expression: "result" }).children([ + Stc.MatchArm({ pattern: "Ok(value)" }).children(["value"]), + ]), + ), + ).toRenderTo(d` + match result { + Ok(value) => value, + } + `); + }); +}); diff --git a/packages/rust/test/method-chain-expression.test.tsx b/packages/rust/test/method-chain-expression.test.tsx new file mode 100644 index 000000000..9552e73cb --- /dev/null +++ b/packages/rust/test/method-chain-expression.test.tsx @@ -0,0 +1,77 @@ +import { Children, Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + MethodChainExpression, + SourceFile, +} from "../src/components/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("MethodChainExpression", () => { + it("renders a basic method call chain", () => { + expect( + inFile( + + + + + , + ), + ).toRenderTo(d`items.iter().filter(is_valid).collect()`); + }); + + it("renders turbofish on individual calls", () => { + expect( + inFile( + + + "]} /> + , + ), + ).toRenderTo(d`items.iter().collect::>()`); + }); + + it("renders await and try per chain step", () => { + expect( + inFile( + + + + , + ), + ).toRenderTo(d`client.send().await?.json::()?`); + }); + + it("wraps long chains using access-expression call-chain formatting", () => { + expect( + inFile( + + + + + "]} /> + , + ), + ).toRenderTo( + d` + items + .iter() + .filter(predicate) + .map(mapper) + .collect::>() + `, + { printWidth: 20 }, + ); + }); +}); diff --git a/packages/rust/test/mod-declarations.test.tsx b/packages/rust/test/mod-declarations.test.tsx new file mode 100644 index 000000000..dfa8a8b31 --- /dev/null +++ b/packages/rust/test/mod-declarations.test.tsx @@ -0,0 +1,143 @@ +import { Output, Scope, code, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { Attribute } from "../src/components/attribute.js"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { ModDeclarations } from "../src/components/mod-declarations.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { RustCrateScope } from "../src/scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../src/scopes/rust-module-scope.js"; +import { findFile } from "./utils.js"; + +describe("ModDeclarations", () => { + it("renders crate root declarations with sorting and visibility", () => { + expect( + + + + + {code`fn main() {}`} + + , + ).toRenderTo(d` + pub mod alpha; + mod zebra; + fn main() {} + `); + }); + + it("renders nested module-root declarations from parent module scope", () => { + const output = render( + + + + + + {code`fn net() {}`} + + + , + ); + + expect(findFile(output, "net/mod.rs").contents).toContain(d` + pub mod alpha; + mod zebra; + fn net() {} + `); + }); + + it("does not render declarations in non-root source files", () => { + const crateScope = new RustCrateScope("my_crate"); + const parentModuleScope = new RustModuleScope("net", crateScope); + parentModuleScope.addChildModule({ name: "alpha", pub: true }); + + expect( + + + {code`fn handle() {}`} + + , + ).toRenderTo(d`fn handle() {}`); + }); + + it("renders declarations before module content", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("mod.rs", crateScope); + moduleScope.addChildModule({ name: "zebra" }); + moduleScope.addChildModule({ name: "alpha", pub: true }); + + expect( + + + + + {code`fn module_root() {}`} + + , + ).toRenderTo(d` + pub mod alpha; + mod zebra; + fn module_root() {} + `); + }); + + it("renders attributes on mod declarations from SourceFile", () => { + const testAttrs = []; + const output = render( + + + + {code`fn test_it() {}`} + + {code`fn main() {}`} + + , + ); + + expect(findFile(output, "lib.rs").contents).toContain(d` + #[cfg(test)] + mod tests; + fn main() {} + `); + }); + + it("renders attributes on mod declarations from ModuleDirectory", () => { + const testAttrs = []; + expect( + + + + {code`fn main() {}`} + + , + ).toRenderTo(d` + #[cfg(test)] + pub mod tests; + fn main() {} + `); + }); + + it("renders attributes via addChildModule on scope", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("mod.rs", crateScope); + moduleScope.addChildModule({ + name: "tests", + attributes: [], + }); + moduleScope.addChildModule({ name: "utils", pub: true }); + + expect( + + + + + , + ).toRenderTo(d` + #[cfg(test)] + mod tests; + pub mod utils; + `); + }); +}); diff --git a/packages/rust/test/module-directory.test.tsx b/packages/rust/test/module-directory.test.tsx new file mode 100644 index 000000000..f61fc0a14 --- /dev/null +++ b/packages/rust/test/module-directory.test.tsx @@ -0,0 +1,154 @@ +import { Output, code, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { useCrateContext } from "../src/context/crate-context.js"; +import { RustModuleScope, useRustModuleScope } from "../src/scopes/index.js"; +import { RustCrateScope } from "../src/scopes/rust-crate-scope.js"; +import { findFile } from "./utils.js"; + +function ScopeCapture() { + const sourceFileScope = useRustModuleScope(); + const crateScope = useCrateContext()!.scope; + const directoryScope = sourceFileScope.parent; + const parentDirectoryScope = + directoryScope instanceof RustModuleScope ? + directoryScope.parent + : undefined; + + return ( + <> + {`${sourceFileScope.name}|${directoryScope?.name ?? "none"}|${ + parentDirectoryScope?.name ?? "none" + }|${crateScope instanceof RustCrateScope}`} + + ); +} + +describe("ModuleDirectory", () => { + it("creates a source directory and registers a public child module on the crate scope", () => { + let crateScope: RustCrateScope | undefined; + + function CrateScopeCapture() { + crateScope = useCrateContext()!.scope; + return ; + } + + const output = render( + + + + + + + + + , + ); + + expect(findFile(output, "net/client.rs").contents.trim()).toBe( + d`client.rs|net|my_crate|true`, + ); + expect(crateScope).toBeDefined(); + expect(crateScope!.childModules.get("net")).toEqual({ + name: "net", + pub: true, + }); + }); + + it("registers nested module directories as child modules of their parent module scope", () => { + let crateScope: RustCrateScope | undefined; + let sourceFileScope: RustModuleScope | undefined; + + function NestedScopeCapture() { + sourceFileScope = useRustModuleScope(); + crateScope = useCrateContext()!.scope; + return <>; + } + + const output = render( + + + + + + + {code`fn client() {}`} + + + + + , + ); + + expect(findFile(output, "net/http/client.rs").contents.trim()).toBe( + d`fn client() {}`, + ); + expect(crateScope).toBeDefined(); + expect(sourceFileScope).toBeDefined(); + expect(crateScope!.childModules.get("net")).toEqual({ + name: "net", + pub: undefined, + }); + + const innerDirectoryScope = sourceFileScope!.parent; + expect(innerDirectoryScope).toBeInstanceOf(RustModuleScope); + expect(innerDirectoryScope!.name).toBe("http"); + expect(innerDirectoryScope!.childModules.get("client")).toEqual({ + name: "client", + pub: undefined, + }); + + const outerDirectoryScope = innerDirectoryScope!.parent; + expect(outerDirectoryScope).toBeInstanceOf(RustModuleScope); + if (!(outerDirectoryScope instanceof RustModuleScope)) { + throw new Error( + "Expected outer directory scope to be a RustModuleScope.", + ); + } + expect(outerDirectoryScope.name).toBe("net"); + expect(outerDirectoryScope.childModules.get("http")).toEqual({ + name: "http", + pub: true, + }); + }); + + it("supports pub(super) and precedence for module directory visibility", () => { + let crateScope: RustCrateScope | undefined; + + function CrateScopeCapture() { + crateScope = useCrateContext()!.scope; + return <>; + } + + render( + + + + + + + + + + + + + + , + ); + + expect(crateScope).toBeDefined(); + expect(crateScope!.childModules.get("net")).toEqual({ + name: "net", + pub: "super", + }); + expect(crateScope!.childModules.get("api")).toEqual({ + name: "api", + pub: "crate", + }); + }); +}); diff --git a/packages/rust/test/module-structure.test.tsx b/packages/rust/test/module-structure.test.tsx new file mode 100644 index 000000000..9e8a70bdf --- /dev/null +++ b/packages/rust/test/module-structure.test.tsx @@ -0,0 +1,278 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + Declaration, + Field, + FunctionDeclaration, + ModuleDirectory, + Reference, + SourceFile, + StructDeclaration, +} from "../src/components/index.js"; +import { findFile } from "./utils.js"; + +describe("Module structure integration", () => { + it("renders a multi-file crate tree with root module declarations", () => { + const output = render( + + + + {`pub struct User;`} + + + {`pub fn list() {}`} + + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + mod models; + mod routes; + `.trim(), + ); + expect(findFile(output, "models/mod.rs").contents.trim()).toBe( + d`pub struct User;`, + ); + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d`pub fn list() {}`, + ); + }); + + it("renders nested module directories with expected file tree", () => { + const output = render( + + + + + {`pub struct User;`} + + + + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe(d`mod models;`); + expect(findFile(output, "models/mod.rs").contents.trim()).toBe( + d`mod user;`, + ); + expect(findFile(output, "models/user/mod.rs").contents.trim()).toBe( + d`pub struct User;`, + ); + }); + + it("auto-generates lib.rs mod declarations for top-level modules", () => { + const output = render( + + + + + + + + + {`fn root() {}`} + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + pub mod alpha; + mod zebra; + fn root() {} + `.trim(), + ); + }); + + it("auto-generates nested mod.rs declarations for child module directories", () => { + const output = render( + + + + + + + + + + {`fn api_root() {}`} + + + + , + ); + + expect(findFile(output, "api/mod.rs").contents.trim()).toBe( + d` + mod internal; + pub mod v1; + fn api_root() {} + `.trim(), + ); + }); + + it("auto-generates lib.rs mod declarations for standalone root source files", () => { + const output = render( + + + {`pub struct User;`} + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe(d`mod user;`); + expect(findFile(output, "user.rs").contents.trim()).toBe( + d`pub struct User;`, + ); + }); + + it("supports pub mod declarations for standalone root source files", () => { + const output = render( + + + {`pub fn ping() {}`} + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe(d`pub mod api;`); + expect(findFile(output, "api.rs").contents.trim()).toBe( + d`pub fn ping() {}`, + ); + }); + + it("does not self-register module-root files", () => { + const output = render( + + + {`fn root() {}`} + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe(d`fn root() {}`); + }); + + it("keeps deterministic ordering across module directories and standalone source files", () => { + const output = render( + + + + + + + + + + + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + mod alpha; + pub mod beta; + pub mod gamma; + mod zebra; + `.trim(), + ); + }); + + it("generates use statements for cross-module references", () => { + const userRef = refkey("user"); + + const output = render( + + + + + + pub struct User; + + + + + + type UserAlias = ; + + + + + , + ); + + expect(findFile(output, "services/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + type UserAlias = User; + `.trim(), + ); + }); + + it("renders the PRD 7.2 golden module scenario", () => { + const output = render( + + + + + + + + + + + + + {`use crate::models::User;`} + + + {`format!("Hello, {}!", user.name)`} + + + + + + , + ); + + expect(findFile(output, "lib.rs").contents.trim()).toBe( + d` + mod models; + mod services; + `.trim(), + ); + expect(findFile(output, "models/mod.rs").contents.trim()).toBe( + d` + pub struct User { + pub name: String, + pub age: u32, + } + `.trim(), + ); + expect(findFile(output, "services/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + pub fn greet(user: &User) -> String { + format!("Hello, {}!", user.name) + } + `.trim(), + ); + }); +}); diff --git a/packages/rust/test/name-conflict-resolver.test.ts b/packages/rust/test/name-conflict-resolver.test.ts new file mode 100644 index 000000000..7283f6df6 --- /dev/null +++ b/packages/rust/test/name-conflict-resolver.test.ts @@ -0,0 +1,53 @@ +import { createSymbol } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { rustNameConflictResolver } from "../src/name-conflict-resolver.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +describe("rustNameConflictResolver", () => { + it("keeps local declarations unchanged and renames imported symbols", () => { + const local = createSymbol(RustOutputSymbol, "Widget", undefined); + const importTargetA = createSymbol(RustOutputSymbol, "Widget", undefined); + const importTargetB = createSymbol(RustOutputSymbol, "Widget", undefined); + const importedA = createSymbol(RustOutputSymbol, "Widget", undefined, { + aliasTarget: importTargetA, + }); + const importedB = createSymbol(RustOutputSymbol, "Widget", undefined, { + aliasTarget: importTargetB, + }); + + rustNameConflictResolver("Widget", [importedA, local, importedB]); + + expect(local.name).toBe("Widget"); + expect(importedA.name).toBe("Widget_2"); + expect(importedB.name).toBe("Widget_3"); + }); + + it("keeps the first imported symbol when all conflicts are imports", () => { + const importTargetA = createSymbol(RustOutputSymbol, "Result", undefined); + const importTargetB = createSymbol(RustOutputSymbol, "Result", undefined); + const importedA = createSymbol(RustOutputSymbol, "Result", undefined, { + aliasTarget: importTargetA, + }); + const importedB = createSymbol(RustOutputSymbol, "Result", undefined, { + aliasTarget: importTargetB, + }); + + rustNameConflictResolver("Result", [importedA, importedB]); + + expect(importedA.name).toBe("Result"); + expect(importedB.name).toBe("Result_2"); + }); + + it("treats metadata-marked symbols as imported", () => { + const local = createSymbol(RustOutputSymbol, "Config", undefined); + const imported = createSymbol(RustOutputSymbol, "Config", undefined, { + metadata: { rustImportedSymbol: true }, + }); + + rustNameConflictResolver("Config", [local, imported]); + + expect(local.name).toBe("Config"); + expect(imported.name).toBe("Config_2"); + }); +}); diff --git a/packages/rust/test/name-policy.test.tsx b/packages/rust/test/name-policy.test.tsx new file mode 100644 index 000000000..ab5e9fb0b --- /dev/null +++ b/packages/rust/test/name-policy.test.tsx @@ -0,0 +1,111 @@ +import { Output } from "@alloy-js/core"; +import { describe, expect, it } from "vitest"; +import { + createRustNamePolicy, + RustElements, + useRustNamePolicy, +} from "../src/name-policy.js"; + +const RESERVED_KEYWORDS = [ + "as", + "async", + "await", + "break", + "const", + "continue", + "crate", + "dyn", + "else", + "enum", + "extern", + "false", + "fn", + "for", + "if", + "impl", + "in", + "let", + "loop", + "match", + "mod", + "move", + "mut", + "pub", + "ref", + "return", + "self", + "Self", + "static", + "struct", + "super", + "trait", + "true", + "type", + "unsafe", + "use", + "where", + "while", + "yield", +] as const; + +describe("createRustNamePolicy", () => { + it.each<[RustElements, string, string]>([ + ["struct", "my-type", "MyType"], + ["enum", "status-kind", "StatusKind"], + ["enum-variant", "active-state", "ActiveState"], + ["trait", "display-item", "DisplayItem"], + ["type-alias", "result-value", "ResultValue"], + ["type-parameter", "item-type", "ItemType"], + ["function", "do-work", "do_work"], + ["method", "run-task", "run_task"], + ["field", "user-name", "user_name"], + ["variable", "temp-value", "temp_value"], + ["parameter", "input-value", "input_value"], + ["module", "my-module", "my_module"], + ["constant", "max-retries", "MAX_RETRIES"], + ])("applies case mapping for %s", (element, input, expected) => { + const namePolicy = createRustNamePolicy(); + expect(namePolicy.getName(input, element)).toBe(expected); + }); + + it("prefixes all reserved keywords using raw identifiers", () => { + const namePolicy = createRustNamePolicy(); + + for (const keyword of RESERVED_KEYWORDS) { + const element: RustElements = keyword === "Self" ? "struct" : "variable"; + const expected = keyword === "Self" ? "r#Self" : `r#${keyword}`; + expect(namePolicy.getName(keyword, element)).toBe(expected); + } + }); + + it("leaves non-reserved names unchanged after case mapping", () => { + const namePolicy = createRustNamePolicy(); + + expect(namePolicy.getName("already_snake_case", "function")).toBe( + "already_snake_case", + ); + expect(namePolicy.getName("alreadyCamel", "variable")).toBe( + "already_camel", + ); + expect(namePolicy.getName("already_pascal", "struct")).toBe( + "AlreadyPascal", + ); + }); +}); + +describe("useRustNamePolicy", () => { + it("returns the current output name policy from context", () => { + const namePolicy = createRustNamePolicy(); + + function Probe() { + const rustNamePolicy = useRustNamePolicy(); + return rustNamePolicy.getName("sample-item", "struct"); + } + + expect( + + + , + ).toRenderTo("SampleItem"); + }); +}); diff --git a/packages/rust/test/namekey.test.tsx b/packages/rust/test/namekey.test.tsx new file mode 100644 index 000000000..252283091 --- /dev/null +++ b/packages/rust/test/namekey.test.tsx @@ -0,0 +1,242 @@ +import { namekey, Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + ConstDeclaration, + CrateDirectory, + EnumDeclaration, + EnumVariant, + Field, + FunctionDeclaration, + ImplBlock, + Reference, + SourceFile, + StructDeclaration, + TraitDeclaration, + TypeAlias, +} from "../src/components/index.js"; +import { findFile, toSourceTextMultiple } from "./utils.js"; + +describe("namekey support", () => { + it("struct declaration with namekey as name", () => { + const personKey = namekey("Person"); + expect( + + + + + + + + + , + ).toRenderTo(d` + pub struct Person { + pub name: String, + } + `); + }); + + it("namekey enables cross-reference without explicit refkey", () => { + const personKey = namekey("Person"); + expect( + + + + + + + + type Alias = ; + + + , + ).toRenderTo(d` + pub struct Person { + pub name: String, + } + type Alias = Person; + `); + }); + + it("namekey works for inline refkey resolution", () => { + const personKey = namekey("Person"); + expect( + + + + + + + + type Alias = {personKey}; + + + , + ).toRenderTo(d` + pub struct Person { + pub name: String, + } + type Alias = Person; + `); + }); + + it("namekey generates use statement across files", () => { + const personKey = namekey("Person"); + const output = toSourceTextMultiple([ + + + + + , + + type Alias = {personKey}; + , + ]); + const libFile = findFile(output, "lib.rs"); + expect(libFile.contents).toContain("use crate::models::Person;"); + expect(libFile.contents).toContain("type Alias = Person;"); + }); + + it("enum declaration with namekey", () => { + const statusKey = namekey("Status"); + expect( + + + + + + + + + + , + ).toRenderTo(d` + pub enum Status { + Active, + Inactive, + } + `); + }); + + it("trait declaration with namekey and cross-reference", () => { + const greetableKey = namekey("Greetable"); + const personKey = namekey("Person"); + expect( + + + + + + + + + + + + String::from("hello") + + + + + , + ).toRenderTo(d` + pub struct Person {} + pub trait Greetable { + fn greet(&self) -> String; + } + impl Greetable for Person { + fn greet(&self) -> String { + String::from("hello") + } + } + `); + }); + + it("function declaration with namekey", () => { + const fnKey = namekey("do_stuff"); + expect( + + + + + true + + + + , + ).toRenderTo(d` + pub fn do_stuff() -> bool { + true + } + `); + }); + + it("const declaration with namekey", () => { + const maxKey = namekey("MAX_RETRIES"); + expect( + + + + 5 + + + , + ).toRenderTo(d`pub const MAX_RETRIES: u32 = 5;`); + }); + + it("type alias with namekey", () => { + const resultKey = namekey("AppResult"); + expect( + + + + Result<String, String> + + + , + ).toRenderTo(d`pub type AppResult = Result;`); + }); + + it("field with namekey", () => { + const nameField = namekey("user_name"); + expect( + + + + + + + + + , + ).toRenderTo(d` + pub struct Config { + pub user_name: String, + } + `); + }); + + it("namekey with explicit refkey — both resolve to symbol", () => { + const explicitKey = refkey(); + const nk = namekey("Foo"); + expect( + + + + + + type A = ; + + type B = {nk}; + + + , + ).toRenderTo(d` + pub struct Foo {} + type A = Foo; + type B = Foo; + `); + }); +}); diff --git a/packages/rust/test/parameter-descriptor.test.ts b/packages/rust/test/parameter-descriptor.test.ts new file mode 100644 index 000000000..cc0034f54 --- /dev/null +++ b/packages/rust/test/parameter-descriptor.test.ts @@ -0,0 +1,58 @@ +import { namekey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + isParameterDescriptor, + type ParameterDescriptor, +} from "../src/parameter-descriptor.js"; + +function formatParameter(parameter: ParameterDescriptor): string { + const prefix = + parameter.refType === "&mut" ? "&mut " + : parameter.refType === "&" ? "&" + : ""; + const mutability = + parameter.refType ? "" + : parameter.mutable ? "mut " + : ""; + return `${prefix}${mutability}${String(parameter.name)}`; +} + +describe("isParameterDescriptor", () => { + it("returns true for a parameter with a string name", () => { + expect( + isParameterDescriptor({ + name: "value", + type: "u32", + mutable: true, + refType: "&mut", + }), + ).toBe(true); + }); + + it("returns true for a parameter with a namekey name", () => { + expect( + isParameterDescriptor({ name: namekey("value"), refType: "&" }), + ).toBe(true); + }); + + it.each([null, undefined, "value", 42, {}, { name: 123 }, { name: true }])( + "returns false for %o", + (value) => { + expect(isParameterDescriptor(value)).toBe(false); + }, + ); +}); + +describe("ParameterDescriptor usage", () => { + it("supports expected parameter shape with mutability and reference type", () => { + const parameter: ParameterDescriptor = { + name: "item", + type: "Vec", + mutable: true, + refType: "&mut", + }; + + expect(formatParameter(parameter)).toBe("&mut item"); + }); +}); diff --git a/packages/rust/test/ref-qualified-path.test.tsx b/packages/rust/test/ref-qualified-path.test.tsx new file mode 100644 index 000000000..f75e5558f --- /dev/null +++ b/packages/rust/test/ref-qualified-path.test.tsx @@ -0,0 +1,111 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + EnumDeclaration, + EnumVariant, + FunctionDeclaration, + ImplBlock, + SourceFile, + StructDeclaration, +} from "../src/components/index.js"; + +describe("ref() qualified path rendering", () => { + it("renders enum variant with qualified path (same module)", () => { + const pendingVariant = refkey(); + + // In Rust, enum variants must always be qualified: Status::Pending + // Currently ref() returns only the leaf name "Pending" + expect( + + + + + + + + + let s = {pendingVariant}; + + + , + ).toRenderTo(d` + pub enum Status { + Active, + Pending, + } + let s = Status::Pending; + `); + }); + + it("renders enum variant with qualified path (cross module)", () => { + const pendingVariant = refkey(); + + // Cross-module: should generate use for the enum + qualified variant access + expect( + + + + + + + + + + let s = {pendingVariant}; + + + , + ).toRenderTo({ + "models.rs": d` + pub enum Status { + Active, + Pending, + } + `, + "lib.rs": d` + mod models; + use crate::models::Status; + let s = Status::Pending; + `, + }); + }); + + it("renders associated function with qualified path", () => { + const structKey = refkey(); + const newFn = refkey(); + + // Associated functions use :: syntax: Config::new() + expect( + + + + + {"timeout: u64,"} + + + + + {"Self { timeout: 30 }"} + + + + let c = {newFn}(); + + + , + ).toRenderTo(d` + pub struct Config { + timeout: u64, + } + impl Config { + pub fn new() -> Self { + Self { timeout: 30 } + } + } + let c = Config::new(); + `); + }); +}); diff --git a/packages/rust/test/reference.test.tsx b/packages/rust/test/reference.test.tsx new file mode 100644 index 000000000..3a16872f2 --- /dev/null +++ b/packages/rust/test/reference.test.tsx @@ -0,0 +1,462 @@ +import { + Children, + Output, + Scope, + createScope, + refkey, + render, +} from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { Declaration } from "../src/components/declaration.js"; +import { FunctionDeclaration } from "../src/components/function-declaration.js"; +import { ImplBlock } from "../src/components/impl-block.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { Reference } from "../src/components/reference.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { + Field, + StructDeclaration, +} from "../src/components/struct-declaration.js"; +import { TraitDeclaration } from "../src/components/trait-declaration.js"; +import { useCrateContext } from "../src/context/crate-context.js"; +import { RustModuleScope, useRustModuleScope } from "../src/scopes/index.js"; +import { RustCrateScope } from "../src/scopes/rust-crate-scope.js"; +import { findFile } from "./utils.js"; + +interface ScopeCaptureProps { + onCapture: (moduleScope: RustModuleScope, crateScope: RustCrateScope) => void; + children?: Children; +} + +function ScopeCapture(props: ScopeCaptureProps) { + const moduleScope = useRustModuleScope(); + const crateScope = useCrateContext()!.scope; + props.onCapture(moduleScope, crateScope); + return <>{props.children}; +} + +interface NestedModuleProps { + name: string; + children?: Children; +} + +function NestedModule(props: NestedModuleProps) { + const parent = useRustModuleScope(); + const scope = createScope(RustModuleScope, props.name, parent, { + binder: parent.binder, + }); + + return {props.children}; +} + +describe("Rust reference resolution", () => { + it("resolves refkey to same-module symbol", () => { + const userType = refkey("user-type"); + let moduleScope: RustModuleScope | undefined; + + render( + + + + { + moduleScope = capturedModuleScope; + }} + > + + pub struct UserType; + + + type Alias = ; + + + + , + ); + + expect(moduleScope).toBeDefined(); + expect(moduleScope!.imports.size).toBe(0); + }); + + it("resolves refkey to different-module symbol in same crate", () => { + const nestedType = refkey("nested-type"); + let consumerModuleScope: RustModuleScope | undefined; + + render( + + + + + + pub struct NestedType; + + + + + { + consumerModuleScope = capturedModuleScope; + }} + > + type Alias = ; + + + + , + ); + + expect(consumerModuleScope).toBeDefined(); + expect(consumerModuleScope!.imports.get("crate::types::nested")?.size).toBe( + 1, + ); + }); + + it("resolves refkey to external crate symbol and tracks dependency", () => { + const externalType = refkey("external-type"); + let consumerModuleScope: RustModuleScope | undefined; + let consumerCrateScope: RustCrateScope | undefined; + + render( + + + + + pub trait Serialize {} + + + + + + { + consumerModuleScope = capturedModuleScope; + consumerCrateScope = capturedCrateScope; + }} + > + type Alias = ; + + + + , + ); + + expect(consumerModuleScope).toBeDefined(); + expect(consumerCrateScope).toBeDefined(); + expect(consumerModuleScope!.imports.get("serde::types")?.size).toBe(1); + expect(consumerCrateScope!.dependencies.get("serde")).toBe("*"); + }); + + it("generates use for same-crate symbols that shadow prelude names", () => { + const preludeLikeType = refkey("prelude-like-type"); + let consumerModuleScope: RustModuleScope | undefined; + + render( + + + + + pub struct Option; + + + + { + consumerModuleScope = capturedModuleScope; + }} + > + type Alias = ; + + + + , + ); + + expect(consumerModuleScope).toBeDefined(); + expect(consumerModuleScope!.imports.size).toBe(1); + expect(consumerModuleScope!.imports.get("crate::types")?.size).toBe(1); + }); + + it("throws on private symbol reference from another module", () => { + const privateType = refkey("private-type"); + + expect(() => + render( + + + + + struct PrivateModel; + + + + type Alias = ; + + + , + ), + ).toThrowError("Cannot reference private symbol 'PrivateModel'"); + }); + + it("renders pub(in path) visibility on a declaration", () => { + expect( + + + + + struct InternalThing; + + + + , + ).toRenderTo(d`pub(in crate::models) struct InternalThing;`); + }); + + it("allows pub(in path) reference from within the specified path", () => { + const internalType = refkey("internal-type"); + + const output = render( + + + + + + + + struct InternalModel; + + + + type Alias = ; + + + + , + ); + + expect(findFile(output, "models/mod.rs").contents).toContain( + "use crate::inner::InternalModel;", + ); + }); + + it("throws on pub(in path) reference from outside the specified path", () => { + const internalType = refkey("internal-type"); + + expect(() => + render( + + + + + + pub(in crate::models) struct InternalModel; + + + + + type Alias = ; + + + , + ), + ).toThrowError(/Cannot reference.*InternalModel/); + }); +}); + +describe("Rust reference nested scope traversal", () => { + it("resolves Reference inside Field type and emits use in enclosing module", () => { + const userType = refkey("user-type"); + + const output = render( + + + + + + pub struct User; + + + + + + + } /> + + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + struct Response { + user: User, + } + `.trim(), + ); + }); + + it("resolves Reference inside function parameters and returnType", () => { + const userType = refkey("user-type"); + + const output = render( + + + + + + pub struct User; + + + + + + }, + ]} + returnType={} + /> + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + fn lookup(user: User) -> User {} + `.trim(), + ); + }); + + it("resolves Reference inside ImplBlock method signatures", () => { + const userType = refkey("user-type"); + const routerType = refkey("router-type"); + + const output = render( + + + + + + pub struct User; + + + + + + + + + }, + ]} + returnType={} + /> + + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + struct Router {} + impl Router { + fn from_user(user: User) -> User {} + } + `.trim(), + ); + }); + + it("resolves Reference inside TraitDeclaration method signatures", () => { + const userType = refkey("user-type"); + + const output = render( + + + + + + pub struct User; + + + + + + + }, + ]} + returnType={} + /> + + + + + , + ); + + expect(findFile(output, "routes/mod.rs").contents.trim()).toBe( + d` + use crate::models::User; + trait UserMapper { + fn map_user(user: User) -> User; + } + `.trim(), + ); + }); +}); diff --git a/packages/rust/test/return-expression.test.tsx b/packages/rust/test/return-expression.test.tsx new file mode 100644 index 000000000..e9315dc5c --- /dev/null +++ b/packages/rust/test/return-expression.test.tsx @@ -0,0 +1,38 @@ +import { Children, Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + ReturnExpression, + SourceFile, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("ReturnExpression", () => { + it("renders bare return", () => { + expect(inFile()).toRenderTo(d`return`); + }); + + it("renders return with value", () => { + expect( + inFile(Err(StoreError::NotFound)), + ).toRenderTo(d`return Err(StoreError::NotFound)`); + }); + + it("stc wrapper renders correctly", () => { + expect(inFile(Stc.ReturnExpression({}).children(["Ok(())"]))).toRenderTo( + d`return Ok(())`, + ); + }); +}); diff --git a/packages/rust/test/rust-output-symbol.test.ts b/packages/rust/test/rust-output-symbol.test.ts new file mode 100644 index 000000000..bfe1ffc9b --- /dev/null +++ b/packages/rust/test/rust-output-symbol.test.ts @@ -0,0 +1,92 @@ +import { createSymbol, watch } from "@alloy-js/core"; +import { describe, expect, it, vi } from "vitest"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +describe("RustOutputSymbol", () => { + it("uses expected default values", () => { + const symbol = createSymbol(RustOutputSymbol, "item", undefined); + + expect(symbol.visibility).toBeUndefined(); + expect(symbol.symbolKind).toBe("symbol"); + expect(symbol.isAsync).toBe(false); + expect(symbol.isUnsafe).toBe(false); + expect(symbol.isConst).toBe(false); + }); + + it("tracks and triggers reactive property updates", () => { + const symbol = createSymbol(RustOutputSymbol, "item", undefined); + const visibilitySpy = vi.fn(); + const symbolKindSpy = vi.fn(); + const isAsyncSpy = vi.fn(); + const isUnsafeSpy = vi.fn(); + const isConstSpy = vi.fn(); + + watch(() => symbol.visibility, visibilitySpy); + watch(() => symbol.symbolKind, symbolKindSpy); + watch(() => symbol.isAsync, isAsyncSpy); + watch(() => symbol.isUnsafe, isUnsafeSpy); + watch(() => symbol.isConst, isConstSpy); + + symbol.visibility = "pub(crate)"; + symbol.symbolKind = "function"; + symbol.isAsync = true; + symbol.isUnsafe = true; + symbol.isConst = true; + + expect(symbol.visibility).toBe("pub(crate)"); + expect(symbol.symbolKind).toBe("function"); + expect(symbol.isAsync).toBe(true); + expect(symbol.isUnsafe).toBe(true); + expect(symbol.isConst).toBe(true); + expect(visibilitySpy).toHaveBeenCalled(); + expect(symbolKindSpy).toHaveBeenCalled(); + expect(isAsyncSpy).toHaveBeenCalled(); + expect(isUnsafeSpy).toHaveBeenCalled(); + expect(isConstSpy).toHaveBeenCalled(); + }); + + it("copy preserves configured values", () => { + const symbol = createSymbol(RustOutputSymbol, "item", undefined, { + visibility: "pub", + symbolKind: "trait", + isAsync: true, + isUnsafe: true, + isConst: true, + }); + + const copy = symbol.copy(); + + expect(copy).toBeInstanceOf(RustOutputSymbol); + expect(copy).not.toBe(symbol); + expect(copy.visibility).toBe("pub"); + expect(copy.symbolKind).toBe("trait"); + expect(copy.isAsync).toBe(true); + expect(copy.isUnsafe).toBe(true); + expect(copy.isConst).toBe(true); + }); + + it("copy stays reactively in sync with source properties", () => { + const symbol = createSymbol(RustOutputSymbol, "item", undefined); + const copy = symbol.copy(); + + symbol.visibility = "pub(super)"; + symbol.symbolKind = "module"; + symbol.isAsync = true; + symbol.isUnsafe = true; + symbol.isConst = true; + + expect(copy.visibility).toBe("pub(super)"); + expect(copy.symbolKind).toBe("module"); + expect(copy.isAsync).toBe(true); + expect(copy.isUnsafe).toBe(true); + expect(copy.isConst).toBe(true); + }); + + it("exposes members accessor for the members space", () => { + const owner = createSymbol(RustOutputSymbol, "owner", undefined); + const member = createSymbol(RustOutputSymbol, "member", owner.members); + + expect(owner.members).toBe(owner.memberSpaceFor("members")); + expect(owner.members.has(member)).toBe(true); + }); +}); diff --git a/packages/rust/test/scope-hierarchy.test.tsx b/packages/rust/test/scope-hierarchy.test.tsx new file mode 100644 index 000000000..be22ccb12 --- /dev/null +++ b/packages/rust/test/scope-hierarchy.test.tsx @@ -0,0 +1,146 @@ +import { Output, Scope, createSymbol } from "@alloy-js/core"; +import { describe, expect, it } from "vitest"; +import { + RustCrateScope, + RustFunctionScope, + RustImplScope, + RustLexicalScope, + RustModuleScope, + RustTraitScope, + useRustCrateScope, + useRustModuleScope, + useRustScope, +} from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +describe("Rust scope hierarchy", () => { + it("tracks crate modules and dependencies", () => { + const crateScope = new RustCrateScope("my_crate"); + + crateScope.addChildModule({ name: "net", pub: true }); + crateScope.addChildModule({ name: "net", pub: "super" }); + crateScope.addDependency("serde", "1.0"); + crateScope.addDependency("tokio", { version: "1.42", features: ["rt"] }); + + expect(crateScope.types).toBeDefined(); + expect(crateScope.values).toBeDefined(); + expect(crateScope.childModules.size).toBe(1); + expect(crateScope.childModules.get("net")).toEqual({ + name: "net", + pub: true, + }); + expect(crateScope.dependencies.get("serde")).toBe("1.0"); + expect(crateScope.dependencies.get("tokio")).toEqual({ + version: "1.42", + features: ["rt"], + }); + }); + + it("tracks module imports grouped by path and child modules", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("http", crateScope); + const request = createSymbol( + RustOutputSymbol, + "Request", + moduleScope.values, + ); + const response = createSymbol( + RustOutputSymbol, + "Response", + moduleScope.values, + ); + + moduleScope.addUse("crate::types", request); + moduleScope.addUse("crate::types", response); + moduleScope.addUse("crate::types", request); + moduleScope.addChildModule({ name: "client", pub: "crate" }); + + expect(moduleScope.types).toBeDefined(); + expect(moduleScope.values).toBeDefined(); + expect(moduleScope.imports.get("crate::types")?.has(request)).toBe(true); + expect(moduleScope.imports.get("crate::types")?.has(response)).toBe(true); + expect(moduleScope.imports.get("crate::types")?.size).toBe(2); + expect(moduleScope.childModules.get("client")?.pub).toBe("crate"); + }); + + it("defines lexical and function declaration spaces", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("ops", crateScope); + const lexicalScope = new RustLexicalScope("block", moduleScope); + const functionScope = new RustFunctionScope("run", moduleScope); + + expect(lexicalScope.localVariables).toBeDefined(); + expect(functionScope.localVariables).toBeDefined(); + expect(functionScope.parameters).toBeDefined(); + expect(functionScope.typeParameters).toBeDefined(); + }); + + it("creates impl and trait member scopes", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("types", crateScope); + const implOwner = createSymbol( + RustOutputSymbol, + "Service", + moduleScope.types, + { symbolKind: "struct" }, + ); + const traitOwner = createSymbol( + NamedTypeSymbol, + "Display", + moduleScope.types, + "trait", + { symbolKind: "trait" }, + ); + + const implScope = new RustImplScope(implOwner, moduleScope); + const traitScope = new RustTraitScope(traitOwner, moduleScope); + + expect(implScope.isMemberScope).toBe(true); + expect(traitScope.isMemberScope).toBe(true); + expect(implScope.members).toBe(implOwner.members); + expect(traitScope.members).toBe(traitOwner.members); + expect(traitScope.typeParameters).toBe(traitOwner.typeParameters); + }); + + it("resolves scope hooks", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("mod_a", crateScope); + + function RustScopeProbe() { + return useRustScope().name; + } + + function RustModuleScopeProbe() { + return useRustModuleScope().name; + } + + function RustCrateScopeProbe() { + return useRustCrateScope().name; + } + + expect( + + + + + , + ).toRenderTo("mod_a"); + + expect( + + + + + , + ).toRenderTo("mod_a"); + + expect( + + + + + , + ).toRenderTo("my_crate"); + }); +}); diff --git a/packages/rust/test/smoke.test.ts b/packages/rust/test/smoke.test.ts new file mode 100644 index 000000000..6bb74472b --- /dev/null +++ b/packages/rust/test/smoke.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, it } from "vitest"; + +describe("@alloy-js/rust", () => { + it("package is loadable", async () => { + const mod = await import("@alloy-js/rust"); + expect(mod).toBeDefined(); + }); +}); diff --git a/packages/rust/test/source-file-crate-directory.test.tsx b/packages/rust/test/source-file-crate-directory.test.tsx new file mode 100644 index 000000000..80d48ac5b --- /dev/null +++ b/packages/rust/test/source-file-crate-directory.test.tsx @@ -0,0 +1,180 @@ +import { Output, code, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { InnerDocComment } from "../src/components/doc-comment.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { useCrateContext } from "../src/context/crate-context.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { RustCrateScope } from "../src/scopes/rust-crate-scope.js"; +import { findFile } from "./utils.js"; + +function CrateContextProbe() { + const crate = useCrateContext(); + return `${crate?.name}|${crate?.version ?? "none"}|${crate?.edition}|${crate?.crateType}|${crate?.scope instanceof RustCrateScope}`; +} + +function ModuleScopeProbe() { + const scope = useRustModuleScope(); + return scope.name; +} + +describe("SourceFile", () => { + it("creates a RustModuleScope and renders children in that scope", () => { + expect( + + + + + + + , + ).toRenderTo(d`lib.rs`); + }); + + it("renders files with filetype='rust'", () => { + const output = render( + + + {code`pub fn run() {}`} + + , + ); + + expect(findFile(output, "lib.rs").filetype).toBe("rust"); + }); + + it("keeps placeholder blocks output-neutral", () => { + expect( + + + {code`fn main() {}`} + + , + ).toRenderTo(d`fn main() {}`); + }); + + it("renders module doc comments via headerComment", () => { + expect( + + + Crate docs} + > + {code`fn main() {}`} + + + , + ).toRenderTo(d` + //! Crate docs + + fn main() {} + `); + }); + + it("registers standalone source files with pub(super) visibility", () => { + let moduleScopeName = ""; + let moduleScopeValues = ""; + + function ModuleVisibilityCapture() { + const scope = useRustModuleScope(); + const parentScope = scope.parent; + if (parentScope instanceof RustCrateScope) { + return "missing-parent-module"; + } + + moduleScopeName = parentScope?.name ?? ""; + moduleScopeValues = [...(parentScope?.childModules.values() ?? [])] + .map((entry) => `${entry.name}:${entry.pub ?? "none"}`) + .sort() + .join("|"); + + return <>; + } + + render( + + + + + + {code`fn client() {}`} + + + + {code`fn server() {}`} + + + + , + ); + + expect(moduleScopeName).toBe("net"); + expect(moduleScopeValues).toBe("client:super|server:crate"); + }); +}); + +describe("CrateDirectory", () => { + it("provides crate metadata context with default edition", () => { + expect( + + + + + + + , + ).toRenderTo(d`my_crate|0.1.0|2021|lib|true`); + }); + + it("uses explicit edition override", () => { + expect( + + + + + + + , + ).toRenderTo(d`my_crate|none|2024|lib|true`); + }); + + it("defaults crateType to lib when omitted", () => { + expect( + + + + + + + , + ).toRenderTo(d`my_crate|none|2021|lib|true`); + }); + + it("propagates explicit crateType=bin", () => { + expect( + + + + + + + , + ).toRenderTo(d`my_crate|none|2021|bin|true`); + }); + + it("propagates explicit crateType=lib", () => { + expect( + + + + + + + , + ).toRenderTo(d`my_crate|none|2021|lib|true`); + }); +}); diff --git a/packages/rust/test/static-declaration.test.tsx b/packages/rust/test/static-declaration.test.tsx new file mode 100644 index 000000000..2f302c18a --- /dev/null +++ b/packages/rust/test/static-declaration.test.tsx @@ -0,0 +1,223 @@ +import { Output, refkey, stc } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + SourceFile, + StaticDeclaration, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +function StaticSymbolProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.values) { + if (symbol instanceof RustOutputSymbol && symbol.name === props.name) { + return `${symbol.symbolKind}|${symbol.visibility ?? "none"}`; + } + } + + return "missing"; +} + +describe("StaticDeclaration", () => { + it("renders a basic static declaration", () => { + expect( + + + + + AtomicUsize::new(0) + + + + , + ).toRenderTo(d`static COUNTER: AtomicUsize = AtomicUsize::new(0);`); + }); + + it("renders static mut when mutable is true", () => { + expect( + + + + + Vec::new() + + + + , + ).toRenderTo(d`static mut BUFFER: Vec = Vec::new();`); + }); + + it("renders pub, pub(crate), and pub(super) visibility", () => { + expect( + + + + + AtomicUsize::new(0) + + + + Vec::new() + + + + 7 + + + + , + ).toRenderTo(d` + pub static COUNTER: AtomicUsize = AtomicUsize::new(0); + pub(crate) static BUFFER: Vec = Vec::new(); + pub(super) static OWNER: usize = 7; + `); + }); + + it("exports STC wrapper and supports direct stc wrapper usage", () => { + const DirectStaticDeclaration = stc(StaticDeclaration); + + expect( + + + + {Stc.StaticDeclaration({ name: "SHARED", type: "usize" }).children([ + "10", + ])} + + {DirectStaticDeclaration({ + name: "LOCAL", + mutable: true, + type: "usize", + }).children(["11"])} + + + , + ).toRenderTo(d` + static SHARED: usize = 10; + static mut LOCAL: usize = 11; + `); + }); + + it("creates static symbol metadata with visibility value", () => { + expect( + + + + + 4 + + + + + + , + ).toRenderTo(d` + pub(crate) static WORKER_COUNT: usize = 4; + static|pub(crate) + `); + }); + + it("applies pub visibility for static declarations", () => { + expect( + + + + + 4 + + + + + + , + ).toRenderTo(d` + pub static WORKER_COUNT: usize = 4; + static|pub + `); + }); + + it("supports refkey references", () => { + const staticRef = refkey("global-flag"); + + expect( + + + + + true + + + {staticRef} + + + , + ).toRenderTo(d` + static GLOBAL_FLAG: bool = true; + GLOBAL_FLAG + `); + }); + + it("renders attributes before static declaration", () => { + expect( + + + + ]} + > + 0 + + + + , + ).toRenderTo(d` + #[no_mangle] + static GLOBAL: u32 = 0; + `); + }); + + it("renders attributes with mutable static", () => { + expect( + + + + , + , + ]} + > + Vec::new() + + + + , + ).toRenderTo(d` + #[no_mangle] + #[used] + static mut BUFFER: Vec = Vec::new(); + `); + }); +}); diff --git a/packages/rust/test/stc.test.tsx b/packages/rust/test/stc.test.tsx new file mode 100644 index 000000000..4a3533bf3 --- /dev/null +++ b/packages/rust/test/stc.test.tsx @@ -0,0 +1,405 @@ +import { type Children, Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CargoTomlFile, + ConstDeclaration, + CrateDirectory, + DeriveAttribute, + DocComment, + EnumDeclaration, + EnumVariant, + Field, + FieldInit as FieldInitializer, + FunctionCallExpression, + FunctionDeclaration, + ImplBlock, + ModuleDirectory, + InnerDocComment, + Parameters, + Reference, + SourceFile, + StructDeclaration, + StructExpression, + TraitDeclaration, + TypeAlias, + TypeParameters, + Value, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("STC wrappers", () => { + it("StructDeclaration and Field wrappers match JSX output", () => { + expect( + inFile( + + + , + ), + ).toRenderTo(d` + struct Point { + x: i32, + } + `); + + expect( + inFile( + Stc.StructDeclaration({ name: "Point" }).children([ + Stc.Field({ name: "x", type: "i32" }), + ]), + ), + ).toRenderTo(d` + struct Point { + x: i32, + } + `); + }); + + it("EnumDeclaration and EnumVariant wrappers match JSX output", () => { + expect( + inFile( + + + , + ), + ).toRenderTo(d` + enum Status { + Ok, + } + `); + + expect( + inFile( + Stc.EnumDeclaration({ name: "Status" }).children([ + Stc.EnumVariant({ name: "Ok" }), + ]), + ), + ).toRenderTo(d` + enum Status { + Ok, + } + `); + }); + + it("FunctionDeclaration wrapper supports .code and matches JSX output", () => { + expect( + inFile( + + {'println!("hi");'} + , + ), + ).toRenderTo(d` + fn hello() { + println!("hi"); + } + `); + + expect( + inFile(Stc.FunctionDeclaration({ name: "hello" }).code`println!("hi");`), + ).toRenderTo(d` + fn hello() { + println!("hi"); + } + `); + }); + + it("TraitDeclaration wrapper matches JSX output", () => { + expect(inFile()).toRenderTo( + d`trait Runner {}`, + ); + expect(inFile(Stc.TraitDeclaration({ name: "Runner" }))).toRenderTo( + d`trait Runner {}`, + ); + }); + + it("ImplBlock wrapper matches JSX output", () => { + const itemRef = refkey("item"); + + expect( + inFile( + <> + + + + + + , + ), + ).toRenderTo(d` + struct Item {} + impl Item { + fn run(&self) {} + } + `); + + expect( + inFile( + <> + + + {Stc.ImplBlock({ type: itemRef }).children([ + Stc.FunctionDeclaration({ name: "run" }), + ])} + , + ), + ).toRenderTo(d` + struct Item {} + impl Item { + fn run(&self) {} + } + `); + }); + + it("TypeAlias wrapper matches JSX output", () => { + expect(inFile({"Vec"})).toRenderTo( + d`type Bytes = Vec;`, + ); + expect( + inFile(Stc.TypeAlias({ name: "Bytes" }).children(["Vec"])), + ).toRenderTo(d`type Bytes = Vec;`); + }); + + it("ConstDeclaration wrapper matches JSX output", () => { + expect( + inFile( + + 100 + , + ), + ).toRenderTo(d`const MAX: usize = 100;`); + expect( + inFile( + Stc.ConstDeclaration({ name: "MAX", type: "usize" }).children(["100"]), + ), + ).toRenderTo(d`const MAX: usize = 100;`); + }); + + it("Attribute wrapper matches JSX output", () => { + expect(inFile()).toRenderTo( + d`#[cfg(test)]`, + ); + expect(inFile(Stc.Attribute({ name: "cfg", args: "test" }))).toRenderTo( + d`#[cfg(test)]`, + ); + }); + + it("DeriveAttribute wrapper matches JSX output", () => { + expect(inFile()).toRenderTo( + d`#[derive(Debug, Clone)]`, + ); + expect( + inFile(Stc.DeriveAttribute({ traits: ["Debug", "Clone"] })), + ).toRenderTo(d`#[derive(Debug, Clone)]`); + }); + + it("DocComment wrapper supports .children and matches JSX output", () => { + expect(inFile(Hello)).toRenderTo("/// Hello\n\n"); + expect(inFile(Stc.DocComment().children(["Hello"]))).toRenderTo( + "/// Hello\n\n", + ); + }); + + it("InnerDocComment wrapper supports .children and matches JSX output", () => { + expect( + inFile(Hello module), + ).toRenderTo("//! Hello module\n\n"); + expect( + inFile(Stc.InnerDocComment().children(["Hello module"])), + ).toRenderTo("//! Hello module\n\n"); + }); + + it("CrateDirectory + ModuleDirectory + SourceFile wrappers match JSX output", () => { + expect( + + + + {"fn client() {}"} + + + , + ).toRenderTo(d`fn client() {}`); + + expect( + + {Stc.CrateDirectory({ name: "my_crate" }).children([ + Stc.ModuleDirectory({ path: "net" }).children([ + Stc.SourceFile({ path: "client.rs" }).children(["fn client() {}"]), + ]), + ])} + , + ).toRenderTo(d`fn client() {}`); + }); + + it("CargoTomlFile wrapper matches JSX output", () => { + expect( + + + , + ).toRenderTo(d` + [package] + name = "my_crate" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + + [dependencies] + serde = "1.0" + `); + + expect( + + {Stc.CargoTomlFile({ + name: "my_crate", + dependencies: { + serde: "1.0", + }, + })} + , + ).toRenderTo(d` + [package] + name = "my_crate" + version = "0.1.0" + edition = "2021" + + [lib] + path = "lib.rs" + + [dependencies] + serde = "1.0" + `); + }); + + it("Reference wrapper matches JSX output", () => { + const userRef = refkey("user"); + + expect( + inFile( + <> + + + + + + , + ), + ).toRenderTo(d` + struct User {} + type UserAlias = User; + `); + + expect( + inFile( + <> + + + {Stc.TypeAlias({ name: "UserAlias" }).children([ + Stc.Reference({ refkey: userRef }), + ])} + , + ), + ).toRenderTo(d` + struct User {} + type UserAlias = User; + `); + }); + + it("Parameters and TypeParameters wrappers match JSX output", () => { + expect( + inFile( + + + , + ), + ).toRenderTo(d` + fn render_params() { + (value: i32) + } + `); + expect( + inFile( + Stc.FunctionDeclaration({ name: "render_params" }).children([ + Stc.Parameters({ parameters: [{ name: "value", type: "i32" }] }), + ]), + ), + ).toRenderTo(d` + fn render_params() { + (value: i32) + } + `); + + expect( + inFile(), + ).toRenderTo(d``); + expect( + inFile( + Stc.TypeParameters({ params: [{ name: "T", constraint: "Clone" }] }), + ), + ).toRenderTo(d``); + }); + + it("Value wrapper matches JSX output", () => { + expect().toRenderTo(d`vec![1, "a"]`); + expect(Stc.Value({ value: [1, "a"] })).toRenderTo(d`vec![1, "a"]`); + }); + + it("StructExpression and FieldInit wrappers match JSX output", () => { + expect( + + capacity + , + ).toRenderTo(d` + Self { + max_capacity: capacity, + ..self + } + `); + + expect( + Stc.StructExpression({ type: "Self", spread: "self" }).children([ + Stc.FieldInit({ name: "max_capacity" }).children(["capacity"]), + ]), + ).toRenderTo(d` + Self { + max_capacity: capacity, + ..self + } + `); + }); + + it("FunctionCallExpression wrapper matches JSX output", () => { + expect( + , + ).toRenderTo(d`self.data.insert::(key, entry)`); + + expect( + Stc.FunctionCallExpression({ + target: "self.data.insert", + typeArgs: ["String"], + args: ["key", "entry"], + }), + ).toRenderTo(d`self.data.insert::(key, entry)`); + }); +}); diff --git a/packages/rust/test/struct-expression.test.tsx b/packages/rust/test/struct-expression.test.tsx new file mode 100644 index 000000000..5778bdd5a --- /dev/null +++ b/packages/rust/test/struct-expression.test.tsx @@ -0,0 +1,49 @@ +import { code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + FieldInit, + StructExpression, +} from "../src/components/struct-expression.js"; + +describe("StructExpression", () => { + it("renders field initializers", () => { + expect( + + {code`capacity`} + , + ).toRenderTo(d` + Self { + max_capacity: capacity, + } + `); + }); + + it("renders shorthand field initialization", () => { + expect( + + + , + ).toRenderTo(d` + Entry { + value, + } + `); + }); + + it("renders spread after all fields", () => { + expect( + + {code`capacity`} + {code`None`} + , + ).toRenderTo(d` + Self { + max_capacity: capacity, + default_ttl: None, + ..self + } + `); + }); +}); diff --git a/packages/rust/test/struct.test.tsx b/packages/rust/test/struct.test.tsx new file mode 100644 index 000000000..6613d7d0e --- /dev/null +++ b/packages/rust/test/struct.test.tsx @@ -0,0 +1,345 @@ +import { Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + Field, + SourceFile, + StructDeclaration, +} from "../src/components/index.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; + +function StructKindProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return symbol.typeKind; + } + } + + return "missing"; +} + +describe("StructDeclaration", () => { + it("renders basic struct", () => { + expect( + + + + + + + , + ).toRenderTo(d`struct Foo {}`); + }); + + it("renders pub struct", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub struct Foo {}`); + }); + + it("renders pub(super) struct", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub(super) struct Foo {}`); + }); + + it("renders derives and attributes", () => { + expect( + + + + + + + , + ).toRenderTo(d` + #[repr(C)] + #[derive(Debug, Clone)] + struct Foo {} + `); + }); + + it("renders struct doc comment", () => { + expect( + + + + + + + , + ).toRenderTo(d` + /// Represents foo. + struct Foo {} + `); + }); + + it("renders fields with indentation and trailing commas", () => { + expect( + + + + + + + + + + , + ).toRenderTo(d` + struct Foo { + first: String, + second: u64, + } + `); + }); + + it("renders type parameters and where clause", () => { + expect( + + + + + + + , + ).toRenderTo(d`struct Foo where U: Clone {}`); + }); + + it("renders tuple struct", () => { + expect( + + + + + + + , + ).toRenderTo(d`struct Foo(String, u64);`); + }); + + it("renders tuple struct with visibility, derives, and generics", () => { + expect( + + + + + + + , + ).toRenderTo( + d` + #[derive(Debug, Clone)] + pub struct Foo(T, U) where U: Clone; + `, + ); + }); + + it("renders unit struct", () => { + expect( + + + + + + + , + ).toRenderTo(d`struct Foo;`); + }); + + it("renders unit struct with doc, attributes, and derives", () => { + expect( + + + + + + + , + ).toRenderTo(d` + /// Represents foo. + #[repr(C)] + #[derive(Debug, Clone)] + struct Foo; + `); + }); + + it("creates a NamedTypeSymbol with typeKind struct", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + struct Foo {} + struct + `); + }); +}); + +describe("Field", () => { + it("renders pub, pub(crate), and pub(super) visibility", () => { + expect( + + + + + + + + + + + , + ).toRenderTo(d` + struct Foo { + pub name: String, + pub(crate) id: u64, + pub(super) owner: String, + } + `); + }); + + it("renders field doc comments", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + struct Foo { + /// Primary name. + name: String, + } + `); + }); + + it("renders pub visibility on field", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + struct Foo { + pub id: u64, + } + `); + }); + + it("renders attributes on field", () => { + expect( + + + + + , + ]} + /> + + + + + , + ).toRenderTo(d` + struct Config { + #[serde(rename = "user_name")] + name: String, + age: u32, + } + `); + }); + + it("renders attributes with doc comment on field", () => { + expect( + + + + + ]} + /> + + + + , + ).toRenderTo(d` + struct Config { + /// The port number. + #[serde(default)] + port: u16, + } + `); + }); +}); diff --git a/packages/rust/test/symbol-factories.test.tsx b/packages/rust/test/symbol-factories.test.tsx new file mode 100644 index 000000000..d0da3c14c --- /dev/null +++ b/packages/rust/test/symbol-factories.test.tsx @@ -0,0 +1,246 @@ +import { Output, Scope, render, useBinder } from "@alloy-js/core"; +import { describe, expect, it } from "vitest"; +import { createRustNamePolicy } from "../src/name-policy.js"; +import { + RustCrateScope, + RustFunctionScope, + RustImplScope, + RustModuleScope, + RustTraitScope, +} from "../src/scopes/index.js"; +import { + createConstSymbol, + createEnumSymbol, + createFieldSymbol, + createFunctionSymbol, + createMethodSymbol, + createParameterSymbol, + createStructSymbol, + createTraitSymbol, + createTypeAliasSymbol, + createTypeParameterSymbol, + createVariantSymbol, +} from "../src/symbols/factories.js"; +import { FunctionSymbol } from "../src/symbols/function-symbol.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +function runInScope( + scope: + | RustCrateScope + | RustModuleScope + | RustFunctionScope + | RustImplScope + | RustTraitScope, + run: () => T, +): T { + let result: T | undefined; + + function Probe() { + result = run(); + return ""; + } + + render( + + + + + , + ); + + return result!; +} + +describe("Rust symbol factories", () => { + it("creates type symbols in module type space", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + + const structSymbol = runInScope(moduleScope, () => + createStructSymbol("my-struct"), + ); + const enumSymbol = runInScope(moduleScope, () => + createEnumSymbol("status-kind"), + ); + const traitSymbol = runInScope(moduleScope, () => + createTraitSymbol("display-item"), + ); + const typeAliasSymbol = runInScope(moduleScope, () => + createTypeAliasSymbol("response-value"), + ); + + expect(structSymbol).toBeInstanceOf(NamedTypeSymbol); + expect(structSymbol.name).toBe("MyStruct"); + expect(structSymbol.typeKind).toBe("struct"); + expect(structSymbol.symbolKind).toBe("struct"); + expect(moduleScope.types.has(structSymbol)).toBe(true); + + expect(enumSymbol.name).toBe("StatusKind"); + expect(enumSymbol.typeKind).toBe("enum"); + expect(enumSymbol.symbolKind).toBe("enum"); + + expect(traitSymbol.name).toBe("DisplayItem"); + expect(traitSymbol.typeKind).toBe("trait"); + expect(traitSymbol.symbolKind).toBe("trait"); + + expect(typeAliasSymbol.name).toBe("ResponseValue"); + expect(typeAliasSymbol.typeKind).toBe("type-alias"); + expect(typeAliasSymbol.symbolKind).toBe("type-alias"); + }); + + it("creates function and const symbols in module value space", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + + const functionSymbol = runInScope(moduleScope, () => + createFunctionSymbol("run-work"), + ); + const constSymbol = runInScope(moduleScope, () => + createConstSymbol("max-items"), + ); + + expect(functionSymbol).toBeInstanceOf(FunctionSymbol); + expect(functionSymbol.name).toBe("run_work"); + expect(functionSymbol.symbolKind).toBe("function"); + expect(moduleScope.values.has(functionSymbol)).toBe(true); + + expect(constSymbol).toBeInstanceOf(RustOutputSymbol); + expect(constSymbol.name).toBe("MAX_ITEMS"); + expect(constSymbol.symbolKind).toBe("const"); + expect(moduleScope.values.has(constSymbol)).toBe(true); + }); + + it("creates member symbols in owner member spaces", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + const structOwner = runInScope(moduleScope, () => + createStructSymbol("service"), + ); + const enumOwner = runInScope(moduleScope, () => createEnumSymbol("status")); + const traitOwner = runInScope(moduleScope, () => + createTraitSymbol("runner"), + ); + const structImplScope = new RustImplScope(structOwner, moduleScope); + const enumImplScope = new RustImplScope(enumOwner, moduleScope); + const traitScope = new RustTraitScope(traitOwner, moduleScope); + + const methodSymbol = runInScope(traitScope, () => + createMethodSymbol("run-task"), + ); + const fieldSymbol = runInScope(structImplScope, () => + createFieldSymbol("user-name"), + ); + const variantSymbol = runInScope(enumImplScope, () => + createVariantSymbol("waiting-state"), + ); + + expect(methodSymbol.name).toBe("run_task"); + expect(methodSymbol.symbolKind).toBe("method"); + expect(traitOwner.members.has(methodSymbol)).toBe(true); + + expect(fieldSymbol.name).toBe("user_name"); + expect(fieldSymbol.symbolKind).toBe("field"); + expect(structOwner.members.has(fieldSymbol)).toBe(true); + + expect(variantSymbol.name).toBe("WaitingState"); + expect(variantSymbol.symbolKind).toBe("variant"); + expect(enumOwner.members.has(variantSymbol)).toBe(true); + }); + + it("creates parameter symbols in function scope spaces", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + const functionScope = new RustFunctionScope("run", moduleScope); + + const parameterSymbol = runInScope(functionScope, () => + createParameterSymbol("input-value"), + ); + const typeParameterSymbol = runInScope(functionScope, () => + createTypeParameterSymbol("item-type"), + ); + + expect(parameterSymbol.name).toBe("input_value"); + expect(parameterSymbol.symbolKind).toBe("parameter"); + expect(functionScope.parameters.has(parameterSymbol)).toBe(true); + + expect(typeParameterSymbol.name).toBe("ItemType"); + expect(typeParameterSymbol.symbolKind).toBe("type-parameter"); + expect(functionScope.typeParameters.has(typeParameterSymbol)).toBe(true); + }); + + it("creates type parameter symbols for named type owners", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + const traitOwner = runInScope(moduleScope, () => + createTraitSymbol("cache-item"), + ); + const traitScope = new RustTraitScope(traitOwner, moduleScope); + + const typeParameterSymbol = runInScope(traitScope, () => + createTypeParameterSymbol("response-data"), + ); + + expect(typeParameterSymbol.name).toBe("ResponseData"); + expect(traitOwner.typeParameters.has(typeParameterSymbol)).toBe(true); + }); + + it("falls back to binder from context when scope has no binder", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + let factorySymbol: NamedTypeSymbol | undefined; + let binderFromContext: ReturnType; + + function Probe() { + binderFromContext = useBinder(); + factorySymbol = createStructSymbol("service"); + return ""; + } + + render( + + + + + , + ); + + expect(factorySymbol).toBeDefined(); + expect(factorySymbol!.binder).toBe(binderFromContext); + }); + + it("throws errors for invalid scopes", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("my-module", crateScope); + const functionScope = new RustFunctionScope("run", moduleScope); + const structOwner = runInScope(moduleScope, () => + createStructSymbol("service"), + ); + const traitOwner = runInScope(moduleScope, () => + createTraitSymbol("runner"), + ); + const traitScope = new RustTraitScope(traitOwner, moduleScope); + const structScope = new RustImplScope(structOwner, moduleScope); + + expect(() => + runInScope(functionScope, () => createStructSymbol("inner")), + ).toThrow("Can't create struct symbol outside of a crate or module scope."); + expect(() => + runInScope(moduleScope, () => createMethodSymbol("run")), + ).toThrow("Can't create method symbol outside of an impl or trait scope."); + expect(() => + runInScope(moduleScope, () => createParameterSymbol("value")), + ).toThrow("Can't create parameter symbol outside of a function scope."); + expect(() => + runInScope(moduleScope, () => createTypeParameterSymbol("item")), + ).toThrow( + "Can't create type parameter symbol outside of a function or named type scope.", + ); + expect(() => runInScope(traitScope, () => createFieldSymbol("x"))).toThrow( + "Can't create field symbol for non-struct type trait.", + ); + expect(() => + runInScope(structScope, () => createVariantSymbol("x")), + ).toThrow("Can't create variant symbol for non-enum type struct."); + }); +}); diff --git a/packages/rust/test/symbol-subclasses.test.ts b/packages/rust/test/symbol-subclasses.test.ts new file mode 100644 index 000000000..c9585c70e --- /dev/null +++ b/packages/rust/test/symbol-subclasses.test.ts @@ -0,0 +1,101 @@ +import { createSymbol, watch } from "@alloy-js/core"; +import { describe, expect, it, vi } from "vitest"; +import { FunctionSymbol } from "../src/symbols/function-symbol.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +describe("NamedTypeSymbol", () => { + it("creates with the provided type kind", () => { + const symbol = createSymbol(NamedTypeSymbol, "Item", undefined, "struct"); + + expect(symbol.typeKind).toBe("struct"); + }); + + it("tracks and triggers reactive type kind updates", () => { + const symbol = createSymbol(NamedTypeSymbol, "Item", undefined, "struct"); + const typeKindSpy = vi.fn(); + + watch(() => symbol.typeKind, typeKindSpy); + symbol.typeKind = "enum"; + + expect(symbol.typeKind).toBe("enum"); + expect(typeKindSpy).toHaveBeenCalled(); + }); + + it("exposes members and type-parameters member spaces", () => { + const owner = createSymbol(NamedTypeSymbol, "Owner", undefined, "trait"); + const member = createSymbol(RustOutputSymbol, "member", owner.members); + const typeParameter = createSymbol( + RustOutputSymbol, + "T", + owner.typeParameters, + ); + + expect(owner.members).toBe(owner.memberSpaceFor("members")); + expect(owner.typeParameters).toBe(owner.memberSpaceFor("type-parameters")); + expect(owner.members.has(member)).toBe(true); + expect(owner.typeParameters.has(typeParameter)).toBe(true); + }); + + it("copy preserves values and mirrors reactive updates", () => { + const symbol = createSymbol(NamedTypeSymbol, "Item", undefined, "trait", { + visibility: "pub", + symbolKind: "trait", + isUnsafe: true, + }); + const copy = symbol.copy(); + + expect(copy).toBeInstanceOf(NamedTypeSymbol); + expect(copy.typeKind).toBe("trait"); + expect(copy.visibility).toBe("pub"); + expect(copy.symbolKind).toBe("trait"); + expect(copy.isUnsafe).toBe(true); + + symbol.typeKind = "type-alias"; + symbol.visibility = "pub(crate)"; + + expect(copy.typeKind).toBe("type-alias"); + expect(copy.visibility).toBe("pub(crate)"); + }); +}); + +describe("FunctionSymbol", () => { + it("creates with optional receiverType", () => { + const symbol = createSymbol(FunctionSymbol, "run", undefined, { + receiverType: "&self", + }); + + expect(symbol.receiverType).toBe("&self"); + }); + + it("tracks and triggers reactive receiverType updates", () => { + const symbol = createSymbol(FunctionSymbol, "run", undefined); + const receiverSpy = vi.fn(); + + watch(() => symbol.receiverType, receiverSpy); + symbol.receiverType = "&mut self"; + + expect(symbol.receiverType).toBe("&mut self"); + expect(receiverSpy).toHaveBeenCalled(); + }); + + it("copy preserves receiverType and mirrors updates", () => { + const symbol = createSymbol(FunctionSymbol, "run", undefined, { + receiverType: "self", + visibility: "pub", + symbolKind: "function", + }); + const copy = symbol.copy(); + + expect(copy).toBeInstanceOf(FunctionSymbol); + expect(copy.receiverType).toBe("self"); + expect(copy.visibility).toBe("pub"); + expect(copy.symbolKind).toBe("function"); + + symbol.receiverType = "&self"; + symbol.visibility = "pub(super)"; + + expect(copy.receiverType).toBe("&self"); + expect(copy.visibility).toBe("pub(super)"); + }); +}); diff --git a/packages/rust/test/t045-render-order.test.tsx b/packages/rust/test/t045-render-order.test.tsx new file mode 100644 index 000000000..efb77cc14 --- /dev/null +++ b/packages/rust/test/t045-render-order.test.tsx @@ -0,0 +1,39 @@ +import { Output } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { ModuleDirectory } from "../src/components/module-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; + +describe("T045 — ModDeclarations render order (lib.rs before modules)", () => { + it("renders sorted declarations with visibility when lib.rs is placed BEFORE ModuleDirectory children", () => { + expect( + + + + + + + , + ).toRenderTo(d` + pub mod alpha; + mod zebra; + `); + }); + + it("renders sorted declarations with visibility when lib.rs is placed AFTER ModuleDirectory children", () => { + expect( + + + + + + + , + ).toRenderTo(d` + pub mod alpha; + mod zebra; + `); + }); +}); diff --git a/packages/rust/test/t070-trait-import.test.tsx b/packages/rust/test/t070-trait-import.test.tsx new file mode 100644 index 000000000..5c64be5ab --- /dev/null +++ b/packages/rust/test/t070-trait-import.test.tsx @@ -0,0 +1,147 @@ +import { Output, refkey, render } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + Declaration, + FunctionDeclaration, + ImplBlock, + ModuleDirectory, + SourceFile, + StructDeclaration, + TraitDeclaration, +} from "../src/components/index.js"; +import { findFile } from "./utils.js"; + +describe("T070 - ImplBlock Trait Name Missing Use Import", () => { + it("generates use statement for external trait reference via refkey in impl block", () => { + const storeErrorRef = refkey("store-error"); + const displayRef = refkey("display-trait"); + + const output = render( + + + + + + pub trait Display {} + + + + + + + + + + + + + + , + ); + + const libFile = findFile(output, "lib.rs"); + expect(libFile.contents).toContain("use std::fmt::Display"); + expect(libFile.contents).toContain("impl Display for StoreError"); + }); + + it("generates use statement for local trait reference via refkey in impl block", () => { + const userRef = refkey("user-type"); + const greetableRef = refkey("greetable-trait"); + + const output = render( + + + + + + + + + + + + + + + + + + , + ); + + const modFile = findFile(output, "models/mod.rs"); + expect(modFile.contents).toContain("use crate::traits::Greetable"); + expect(modFile.contents).toContain("impl Greetable for User"); + }); + + it("does not generate duplicate use statements when trait already imported", () => { + const userRef = refkey("user-type"); + const greetableRef = refkey("greetable-trait"); + + const output = render( + + + + + + + + + + + + + , + ); + + const libFile = findFile(output, "lib.rs"); + // Should not contain duplicate use statements + const useCount = (libFile.contents.match(/use.*Greetable/g) || []).length; + expect(useCount).toBeLessThanOrEqual(1); + expect(libFile.contents).toContain("impl Greetable for User"); + }); + + it("generates use for external trait in blanket impl pattern", () => { + const displayRef = refkey("display-trait"); + const genericRef = refkey("generic-type"); + + const output = render( + + + + + + + + + + + + + + + + , + ); + + const libFile = findFile(output, "lib.rs"); + expect(libFile.contents).toContain("use std::fmt::Display"); + expect(libFile.contents).toContain("impl Display for MyType"); + }); +}); diff --git a/packages/rust/test/trait.test.tsx b/packages/rust/test/trait.test.tsx new file mode 100644 index 000000000..f096a41b2 --- /dev/null +++ b/packages/rust/test/trait.test.tsx @@ -0,0 +1,264 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + CrateDirectory, + Reference, + SourceFile, + TraitDeclaration, +} from "../src/components/index.js"; +import { + RustTraitScope, + useRustModuleScope, + useRustScope, +} from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; + +function TraitKindProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return symbol.typeKind; + } + } + + return "missing"; +} + +function TraitScopeProbe() { + const scope = useRustScope(); + return scope instanceof RustTraitScope ? "trait-scope" : "other-scope"; +} + +function TraitVisibilityProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return symbol.visibility ?? "none"; + } + } + + return "missing"; +} + +describe("TraitDeclaration", () => { + it("renders basic trait", () => { + expect( + + + + + + + , + ).toRenderTo(d`trait Displayable {}`); + }); + + it("renders pub trait", () => { + expect( + + + + + + + , + ).toRenderTo(d`pub trait Displayable {}`); + }); + + it("renders pub(crate) and pub(super) trait visibility", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + pub(crate) trait CrateVisible {} + pub(super) trait ParentVisible {} + `); + }); + + it("renders supertraits", () => { + expect( + + + + + + + , + ).toRenderTo(d`trait Printable: Display + Debug {}`); + }); + + it("renders type parameters and where clause", () => { + expect( + + + + + + + , + ).toRenderTo(d`trait Sink where T: Clone {}`); + }); + + it("renders trait with method signature and default implementation", () => { + expect( + + + + + {"fn run(&self);"} + + {"fn run_default(&self) {"} + + {"true"} + + {"}"} + + + + , + ).toRenderTo(d` + trait Runner { + fn run(&self); + fn run_default(&self) { + true + } + } + `); + }); + + it("renders doc comments", () => { + expect( + + + + + + + , + ).toRenderTo(d` + /// Trait docs. + trait Runner {} + `); + }); + + it("creates trait scope for children", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + trait Runner { + trait-scope + } + `); + }); + + it("creates a trait symbol and resolves it by refkey", () => { + const traitRef = refkey("serialize-trait"); + + expect( + + + + + + {"type Alias = "} + + {";"} + + + + + , + ).toRenderTo(d` + trait Serialize {} + type Alias = Serialize; + trait + `); + }); + + it("applies pub visibility on trait symbols", () => { + expect( + + + + + + + + + , + ).toRenderTo(d` + pub trait Serialize {} + pub + `); + }); + + it("renders attributes before trait declaration", () => { + expect( + + + + ]} + /> + + + , + ).toRenderTo(d` + #[deprecated] + pub trait MyTrait {} + `); + }); + + it("renders attributes with doc comment", () => { + expect( + + + + ]} + /> + + + , + ).toRenderTo(d` + /// Handles events. + #[async_trait] + trait Handler {} + `); + }); +}); diff --git a/packages/rust/test/try-expression.test.tsx b/packages/rust/test/try-expression.test.tsx new file mode 100644 index 000000000..0faaf19e8 --- /dev/null +++ b/packages/rust/test/try-expression.test.tsx @@ -0,0 +1,65 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + FunctionCallExpression, + MethodChainExpression, + SourceFile, + TryExpression, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("TryExpression", () => { + it("renders postfix question mark operator", () => { + expect(inFile({code`result`})).toRenderTo( + d`result?`, + ); + }); + + it("composes with function call expression", () => { + expect( + inFile( + + + , + ), + ).toRenderTo(d`std::fs::read_to_string("file.txt")?`); + }); + + it("composes with method chain expression", () => { + expect( + inFile( + + + + + + , + ), + ).toRenderTo(d`map.get("key").ok_or(Error::NotFound)?`); + }); + + it("stc wrapper renders question mark operator", () => { + expect(inFile(Stc.TryExpression().children([code`result`]))).toRenderTo( + d`result?`, + ); + }); +}); diff --git a/packages/rust/test/type-alias-const.test.tsx b/packages/rust/test/type-alias-const.test.tsx new file mode 100644 index 000000000..4665e3e56 --- /dev/null +++ b/packages/rust/test/type-alias-const.test.tsx @@ -0,0 +1,293 @@ +import { Output, refkey } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + Attribute, + ConstDeclaration, + CrateDirectory, + SourceFile, + TypeAlias, +} from "../src/components/index.js"; +import { createRustNamePolicy } from "../src/name-policy.js"; +import { useRustModuleScope } from "../src/scopes/index.js"; +import { NamedTypeSymbol } from "../src/symbols/named-type-symbol.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +function TypeAliasSymbolProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.types) { + if (symbol instanceof NamedTypeSymbol && symbol.name === props.name) { + return `${symbol.typeKind}|${symbol.symbolKind}|${symbol.visibility ?? "none"}`; + } + } + + return "missing"; +} + +function ConstSymbolProbe(props: { name: string }) { + const scope = useRustModuleScope(); + for (const symbol of scope.values) { + if (symbol instanceof RustOutputSymbol && symbol.name === props.name) { + return `${symbol.symbolKind}|${symbol.visibility ?? "none"}`; + } + } + + return "missing"; +} + +describe("TypeAlias", () => { + it("renders a basic type alias", () => { + expect( + + + + Bar + + + , + ).toRenderTo(d`type Foo = Bar;`); + }); + + it("renders public, pub(crate), and pub(super) visibility", () => { + expect( + + + + + String + + + + usize + + + + u32 + + + + , + ).toRenderTo(d` + pub type PublicAlias = String; + pub(crate) type CrateAlias = usize; + pub(super) type ParentAlias = u32; + `); + }); + + it("renders type parameters", () => { + expect( + + + + + {"Vec"} + + + + , + ).toRenderTo(d`type Collection = Vec;`); + }); + + it("creates type-alias symbol with visibility and supports refkey", () => { + const alias = refkey("response-alias"); + + expect( + + + + + String + + + + + {alias} + + + , + ).toRenderTo(d` + pub(crate) type ResponseValue = String; + type-alias|type-alias|pub(crate) + ResponseValue + `); + }); + + it("applies pub visibility for type aliases", () => { + expect( + + + + + String + + + + + + , + ).toRenderTo(d` + pub type ResponseValue = String; + type-alias|type-alias|pub + `); + }); + + it("renders attributes before type alias", () => { + expect( + + + + ]} + > + std::result::Result<T, MyError> + + + + , + ).toRenderTo(d` + #[allow(dead_code)] + pub type Result = std::result::Result; + `); + }); +}); + +describe("ConstDeclaration", () => { + it("renders a basic const declaration", () => { + expect( + + + + + 100 + + + + , + ).toRenderTo(d`const MAX_SIZE: usize = 100;`); + }); + + it("renders visibility and applies SCREAMING_SNAKE_CASE name policy", () => { + expect( + + + + + 100 + + + + 1 + + + + 2 + + + + , + ).toRenderTo(d` + pub const MAX_SIZE: usize = 100; + pub(crate) const WORKER_ID: u64 = 1; + pub(super) const PARENT_ID: u64 = 2; + `); + }); + + it("applies SCREAMING_SNAKE_CASE when rust name policy is configured", () => { + expect( + + + + + 100 + + + + , + ).toRenderTo(d`const MAX_SIZE: usize = 100;`); + }); + + it("creates const symbol with visibility and supports refkey", () => { + const maxItems = refkey("max-items"); + + expect( + + + + + 16 + + + + + {maxItems} + + + , + ).toRenderTo(d` + pub const MAX_ITEMS: usize = 16; + const|pub + MAX_ITEMS + `); + }); + + it("applies pub visibility for const declarations", () => { + expect( + + + + + 16 + + + + + + , + ).toRenderTo(d` + pub const MAX_ITEMS: usize = 16; + const|pub + `); + }); + + it("renders attributes before const declaration", () => { + expect( + + + + ]} + > + 100 + + + + , + ).toRenderTo(d` + #[allow(dead_code)] + const MAX: u32 = 100; + `); + }); +}); diff --git a/packages/rust/test/type-parameters.test.tsx b/packages/rust/test/type-parameters.test.tsx new file mode 100644 index 000000000..d86e5b0a6 --- /dev/null +++ b/packages/rust/test/type-parameters.test.tsx @@ -0,0 +1,109 @@ +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + TypeParameters, + WhereClause, +} from "../src/components/type-parameters.js"; + +describe("TypeParameters", () => { + it("renders a single type parameter", () => { + expect().toRenderTo(d``); + }); + + it("renders multiple type parameters", () => { + expect( + , + ).toRenderTo(d``); + }); + + it("renders a single constrained parameter", () => { + expect( + , + ).toRenderTo(d``); + }); + + it("renders mixed constrained and unconstrained parameters", () => { + expect( + , + ).toRenderTo(d``); + }); + + it("renders a single lifetime parameter", () => { + expect().toRenderTo( + d`<'a>`, + ); + }); + + it("renders lifetimes before type parameters", () => { + expect( + , + ).toRenderTo(d`<'a, 'b, T, U: 'a + Display>`); + }); + + it("renders lifetime bounds", () => { + expect( + , + ).toRenderTo(d`<'a, 'b: 'a>`); + }); + + it("renders type parameter lifetime bounds", () => { + expect( + , + ).toRenderTo(d`<'a, T: 'a + Clone>`); + }); + + it("renders nothing for empty params", () => { + expect( + <> + fn value + + , + ).toRenderTo(d`fn value`); + }); + + it("renders nothing for undefined params", () => { + expect( + <> + fn value + + , + ).toRenderTo(d`fn value`); + }); +}); + +describe("WhereClause", () => { + it("renders a single where clause constraint", () => { + expect(T: Display + Clone).toRenderTo( + d`where T: Display + Clone`, + ); + }); + + it("renders multiple where clause constraints", () => { + expect(T: Display + Clone, U: Debug).toRenderTo( + d`where T: Display + Clone, U: Debug`, + ); + }); + + it("renders nothing when children are missing", () => { + expect( + <> + {"impl Thing"} + + , + ).toRenderTo(d`impl Thing`); + }); +}); diff --git a/packages/rust/test/unsafe-block.test.tsx b/packages/rust/test/unsafe-block.test.tsx new file mode 100644 index 000000000..684b8f4d8 --- /dev/null +++ b/packages/rust/test/unsafe-block.test.tsx @@ -0,0 +1,77 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + SourceFile, + UnsafeBlock, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("UnsafeBlock", () => { + it("renders a simple unsafe block", () => { + expect(inFile({code`*ptr`})).toRenderTo(d` + unsafe { + *ptr + } + `); + }); + + it("renders multi-statement block body with indentation", () => { + expect( + inFile( + + {code`let ptr = &value as *const i32;`} + {code`let val = *ptr;`} + , + ), + ).toRenderTo(d` + unsafe { + let ptr = &value as *const i32; + let val = *ptr; + } + `); + }); + + it("renders empty unsafe block", () => { + expect(inFile()).toRenderTo(d` + unsafe {} + `); + }); + + it("supports nested control-flow content", () => { + expect( + inFile( + + {code`if ptr.is_null() { return None; }`} + {code`Some(*ptr)`} + , + ), + ).toRenderTo(d` + unsafe { + if ptr.is_null() { return None; } + Some(*ptr) + } + `); + }); + + it("stc wrapper renders the same output", () => { + expect(inFile(Stc.UnsafeBlock().children(["libc::free(ptr);"]))) + .toRenderTo(d` + unsafe { + libc::free(ptr); + } + `); + }); +}); diff --git a/packages/rust/test/use-statements.test.tsx b/packages/rust/test/use-statements.test.tsx new file mode 100644 index 000000000..dc2e6dff5 --- /dev/null +++ b/packages/rust/test/use-statements.test.tsx @@ -0,0 +1,140 @@ +import { Output, Scope, code, createSymbol } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; +import { + UseStatement, + UseStatements, +} from "../src/components/use-statement.js"; +import { RustCrateScope } from "../src/scopes/rust-crate-scope.js"; +import { RustModuleScope } from "../src/scopes/rust-module-scope.js"; +import { RustOutputSymbol } from "../src/symbols/rust-output-symbol.js"; + +describe("UseStatement", () => { + it("renders a single use line", () => { + expect().toRenderTo( + d`use std::fmt::Display;`, + ); + }); +}); + +describe("UseStatements", () => { + it("reads imports from RustModuleScope", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("lib.rs", crateScope); + const display = createSymbol( + RustOutputSymbol, + "Display", + moduleScope.values, + ); + moduleScope.addUse("std::fmt", display); + + expect( + + + + + , + ).toRenderTo(d`use std::fmt::Display;`); + }); + + it("renders grouped statements for multiple symbols from same path", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("lib.rs", crateScope); + const display = createSymbol( + RustOutputSymbol, + "Display", + moduleScope.values, + ); + const debug = createSymbol(RustOutputSymbol, "Debug", moduleScope.values); + moduleScope.addUse("std::fmt", display); + moduleScope.addUse("std::fmt", debug); + + expect( + + + + + , + ).toRenderTo(d` + use std::fmt::{Debug, Display}; + `); + }); + + it("sorts groups as std, external, crate with blank lines and integrates in SourceFile", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("lib.rs", crateScope); + moduleScope.addUse( + "crate::types", + createSymbol(RustOutputSymbol, "Response", moduleScope.values), + ); + moduleScope.addUse( + "std::fmt", + createSymbol(RustOutputSymbol, "Debug", moduleScope.values), + ); + moduleScope.addUse( + "serde::de", + createSymbol(RustOutputSymbol, "DeserializeOwned", moduleScope.values), + ); + moduleScope.addUse( + "crate::models", + createSymbol(RustOutputSymbol, "User", moduleScope.values), + ); + moduleScope.addUse( + "std::collections", + createSymbol(RustOutputSymbol, "HashMap", moduleScope.values), + ); + moduleScope.addUse( + "tokio::runtime", + createSymbol(RustOutputSymbol, "Runtime", moduleScope.values), + ); + + expect( + + + + + , + ).toRenderTo(d` + use std::collections::HashMap; + use std::fmt::Debug; + + use serde::de::DeserializeOwned; + use tokio::runtime::Runtime; + + use crate::models::User; + use crate::types::Response; + `); + }); + + it("does not add blank lines for missing groups", () => { + const crateScope = new RustCrateScope("my_crate"); + const moduleScope = new RustModuleScope("lib.rs", crateScope); + moduleScope.addUse( + "crate::types", + createSymbol(RustOutputSymbol, "OnlyType", moduleScope.values), + ); + + expect( + + + + + , + ).toRenderTo(d` + use crate::types::OnlyType; + `); + }); + + it("integrates into SourceFile output position", () => { + expect( + + + {code`fn main() {}`} + + , + ).toRenderTo(d`fn main() {}`); + }); +}); diff --git a/packages/rust/test/utils.tsx b/packages/rust/test/utils.tsx new file mode 100644 index 000000000..c1eab21ed --- /dev/null +++ b/packages/rust/test/utils.tsx @@ -0,0 +1,168 @@ +import { + Children, + ContentOutputFile, + Output, + OutputDirectory, + OutputFile, + PrintTreeOptions, + render, +} from "@alloy-js/core"; +import { dedent } from "@alloy-js/core/testing"; +import { expect } from "vitest"; +import { CrateDirectory } from "../src/components/crate-directory.js"; +import { SourceFile } from "../src/components/source-file.js"; + +/** + * Renders a single Rust source file for testing. + * + * Wraps component tree in a minimal Output + SourceFile context, renders to string. + * Useful for single-file unit tests of declarations, expressions, and syntax. + * + * @param c - Component tree to render (typically code` ... ` or JSX components) + * @param options - Prettier print options (optional). Defaults: insertFinalNewLine=false + * @returns Formatted source code as string + * + * @example + * ```tsx + * const code = toSourceText( + * code`fn main() { println!("hello"); }` + * ); + * expect(code).toContain("fn main"); + * ``` + */ +export function toSourceText(c: Children, options?: PrintTreeOptions): string { + const res = render( + + + {c} + + , + { insertFinalNewLine: false, ...options }, + ); + + return findFile(res, "test.rs").contents; +} + +/** + * Renders multiple Rust source files and returns the output directory. + * + * Useful for testing cross-file references, module structures, imports, + * and other multi-file scenarios. Each child should typically be a SourceFile component. + * + * @param children - Array of component trees (typically SourceFile components) + * @returns Output directory structure containing all rendered files + * + * @example + * ```tsx + * const output = toSourceTextMultiple([ + * {code`pub fn hello() {}`}, + * {code`pub fn world() {}`}, + * ]); + * const libContents = findFile(output, "lib.rs").contents; + * expect(libContents).toContain("pub fn hello"); + * ``` + */ +export function toSourceTextMultiple(children: Children[]): OutputDirectory { + return render( + + {children} + , + ); +} + +/** + * Recursively finds a file in the output directory tree by path. + * + * Traverses the directory structure returned by render() and returns the matching file. + * + * @param res - Output directory to search + * @param path - File path to find (e.g., "src/main.rs", "test.rs") + * @returns The ContentOutputFile if found + * @throws Error if file is not found, with helpful list of available files + * + * @example + * ```tsx + * const output = toSourceTextMultiple([...]); + * const file = findFile(output, "src/lib.rs"); + * expect(file.contents).toMatch(/^pub fn/); + * ``` + */ +export function findFile( + res: OutputDirectory, + path: string, +): ContentOutputFile { + const result = findFileWorker(res, path); + + if (!result) { + const available = collectPaths(res); + const hint = + available.length > 0 ? + `Available files: ${available.join(", ")}` + : "No files found in output"; + throw new Error(`Expected to find file "${path}" in output. ${hint}`); + } + return result as ContentOutputFile; + + function findFileWorker( + dir: OutputDirectory, + targetPath: string, + ): OutputFile | null { + for (const item of dir.contents) { + if (item.kind === "file") { + if (item.path === targetPath) { + return item; + } + } else if (item.kind === "directory") { + const found = findFileWorker(item, targetPath); + if (found) { + return found; + } + } + } + return null; + } + + function collectPaths(dir: OutputDirectory): string[] { + const paths: string[] = []; + for (const item of dir.contents) { + if (item.kind === "file") { + paths.push(item.path); + } else if (item.kind === "directory") { + paths.push(...collectPaths(item)); + } + } + return paths; + } +} + +/** + * Validates multiple files' contents against expected values using exact match. + * + * Batch assertion helper for multi-file tests. Each expected content string + * is dedented and compared exactly (using `toBe`). + * + * @param res - Output directory structure (from toSourceTextMultiple or render) + * @param expectedFiles - Record mapping file paths to expected contents (dedented) + * @throws vitest assertion error if any file content doesn't match + * + * @example + * ```tsx + * const output = toSourceTextMultiple([ + * {code`pub fn hello() {}`}, + * {code`pub struct Thing;`}, + * ]); + * assertFileContents(output, { + * "lib.rs": code`pub fn hello() {}`, + * "module.rs": code`pub struct Thing;`, + * }); + * ``` + */ +export function assertFileContents( + res: OutputDirectory, + expectedFiles: Record, +): void { + for (const [path, expectedContent] of Object.entries(expectedFiles)) { + const file = findFile(res, path); + expect(file.contents).toBe(dedent(expectedContent)); + } +} diff --git a/packages/rust/test/value.test.tsx b/packages/rust/test/value.test.tsx new file mode 100644 index 000000000..33d8b50ee --- /dev/null +++ b/packages/rust/test/value.test.tsx @@ -0,0 +1,59 @@ +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { Value } from "../src/components/value.js"; + +describe("Value", () => { + it("renders strings", () => { + expect().toRenderTo(d`"hello"`); + }); + + it("renders integers", () => { + expect().toRenderTo(d`42`); + }); + + it("renders floats", () => { + expect().toRenderTo(d`3.14`); + }); + + it("renders boolean true", () => { + expect().toRenderTo(d`true`); + }); + + it("renders boolean false", () => { + expect().toRenderTo(d`false`); + }); + + it("renders null as None", () => { + expect().toRenderTo(d`None`); + }); + + it("renders undefined as None", () => { + expect().toRenderTo(d`None`); + }); + + it("renders arrays as vec literals", () => { + expect().toRenderTo(d`vec![1, 2, 3]`); + }); + + it("renders nested arrays", () => { + expect( + , + ).toRenderTo(d`vec![vec![1, 2], vec![3, 4]]`); + }); + + it("renders empty arrays", () => { + expect().toRenderTo(d`vec![]`); + }); + + it("escapes strings with special characters", () => { + expect().toRenderTo( + d`"line1\\nline2\\t\\"quoted\\"\\\\path"`, + ); + }); +}); diff --git a/packages/rust/test/vitest.setup.ts b/packages/rust/test/vitest.setup.ts new file mode 100644 index 000000000..96cd44191 --- /dev/null +++ b/packages/rust/test/vitest.setup.ts @@ -0,0 +1 @@ +import "@alloy-js/core/testing"; diff --git a/packages/rust/test/while-loop-expression.test.tsx b/packages/rust/test/while-loop-expression.test.tsx new file mode 100644 index 000000000..d05ceef73 --- /dev/null +++ b/packages/rust/test/while-loop-expression.test.tsx @@ -0,0 +1,111 @@ +import { Children, Output, code } from "@alloy-js/core"; +import "@alloy-js/core/testing"; +import { d } from "@alloy-js/core/testing"; +import { describe, expect, it } from "vitest"; +import { + CrateDirectory, + LoopExpression, + SourceFile, + WhileExpression, +} from "../src/components/index.js"; +import * as Stc from "../src/components/stc/index.js"; + +function inFile(children: Children) { + return ( + + + {children} + + + ); +} + +describe("WhileExpression + LoopExpression", () => { + it("renders a simple while loop", () => { + expect( + inFile( + + {code`process(stack.pop());`} + , + ), + ).toRenderTo(d` + while !stack.is_empty() { + process(stack.pop()); + } + `); + }); + + it("supports while let via condition prop", () => { + expect( + inFile( + + {code`process(item);`} + , + ), + ).toRenderTo(d` + while let Some(item) = iter.next() { + process(item); + } + `); + }); + + it("renders while with label", () => { + expect( + inFile( + + {code`attempts += 1;`} + , + ), + ).toRenderTo(d` + 'retry: while attempts < max_attempts { + attempts += 1; + } + `); + }); + + it("renders loop expression", () => { + expect( + inFile( + {code`if done { break result; }`}, + ), + ).toRenderTo(d` + loop { + if done { break result; } + } + `); + }); + + it("renders loop expression with label", () => { + expect( + inFile( + {code`run_once();`}, + ), + ).toRenderTo(d` + 'outer: loop { + run_once(); + } + `); + }); + + it("stc wrappers render same output", () => { + expect( + inFile( + <> + {Stc.WhileExpression({ + condition: "let Some(item) = queue.pop_front()", + label: "'retry", + }).children(["process(item);"])} + + {Stc.LoopExpression({ label: "'outer" }).children(["break;"])} + , + ), + ).toRenderTo(d` + 'retry: while let Some(item) = queue.pop_front() { + process(item); + } + 'outer: loop { + break; + } + `); + }); +}); diff --git a/packages/rust/tsconfig.json b/packages/rust/tsconfig.json new file mode 100644 index 000000000..07c57df68 --- /dev/null +++ b/packages/rust/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "emitDeclarationOnly": true, + "declaration": true, + "outDir": "dist", + "types": ["@alloy-js/core/testing/matchers"], + "paths": { + "#components/*": ["./src/components/*"] + } + }, + "references": [{ "path": "../core" }], + "include": ["src/**/*.ts", "src/**/*.tsx", "test/**/*.ts", "test/**/*.tsx"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/rust/vitest.config.ts b/packages/rust/vitest.config.ts new file mode 100644 index 000000000..5fa2542c0 --- /dev/null +++ b/packages/rust/vitest.config.ts @@ -0,0 +1,21 @@ +import alloyPlugin from "@alloy-js/rollup-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + resolve: { + conditions: ["source"], + }, + ssr: { + resolve: { + conditions: ["source"], + }, + }, + esbuild: { + jsx: "preserve", + sourcemap: "both", + }, + test: { + setupFiles: ["./test/vitest.setup.ts"], + }, + plugins: [alloyPlugin()], +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7e42c0b3..a967c6feb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -834,6 +834,40 @@ importers: specifier: 'catalog:' version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.9)(esbuild@0.25.8)(jiti@2.6.1)(tsx@4.20.3)(yaml@2.8.0) + packages/rust: + dependencies: + '@alloy-js/core': + specifier: workspace:~ + version: link:../core + change-case: + specifier: 'catalog:' + version: 5.4.4 + pathe: + specifier: 'catalog:' + version: 2.0.3 + devDependencies: + '@alloy-js/cli': + specifier: workspace:~ + version: link:../cli + '@alloy-js/rollup-plugin': + specifier: workspace:~ + version: link:../rollup-plugin + '@microsoft/api-extractor': + specifier: 'catalog:' + version: 7.52.9(@types/node@24.10.9) + '@rollup/plugin-typescript': + specifier: 'catalog:' + version: 12.1.4(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.3) + concurrently: + specifier: 'catalog:' + version: 9.2.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: 'catalog:' + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.9)(esbuild@0.25.8)(jiti@2.6.1)(tsx@4.20.3)(yaml@2.8.0) + packages/trace-cli: dependencies: diff-match-patch: @@ -1029,6 +1063,34 @@ importers: specifier: 'catalog:' version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.9)(esbuild@0.25.8)(jiti@2.6.1)(tsx@4.20.3)(yaml@2.8.0) + samples/rust-example: + dependencies: + '@alloy-js/core': + specifier: workspace:~ + version: link:../../packages/core + '@alloy-js/rust': + specifier: workspace:~ + version: link:../../packages/rust + devDependencies: + '@alloy-js/cli': + specifier: workspace:~ + version: link:../../packages/cli + '@alloy-js/rollup-plugin': + specifier: workspace:~ + version: link:../../packages/rollup-plugin + '@types/node': + specifier: 'catalog:' + version: 24.10.9 + concurrently: + specifier: 'catalog:' + version: 9.2.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: 'catalog:' + version: 3.2.4(@types/debug@4.1.12)(@types/node@24.10.9)(esbuild@0.25.8)(jiti@2.6.1)(tsx@4.20.3)(yaml@2.8.0) + samples/scaffold-generator: dependencies: '@alloy-js/core': @@ -8395,7 +8457,7 @@ snapshots: '@rollup/plugin-typescript@12.1.4(rollup@4.46.2)(tslib@2.8.1)(typescript@5.9.3)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.46.2) + '@rollup/pluginutils': 5.3.0(rollup@4.46.2) resolve: 1.22.10 typescript: 5.9.3 optionalDependencies: diff --git a/samples/rust-example/output/Cargo.toml b/samples/rust-example/output/Cargo.toml new file mode 100644 index 000000000..c5509bde0 --- /dev/null +++ b/samples/rust-example/output/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "kv_store" +version = "0.1.0" +edition = "2021" + +[lib] +path = "lib.rs" diff --git a/samples/rust-example/output/config.rs b/samples/rust-example/output/config.rs new file mode 100644 index 000000000..30ced1d3e --- /dev/null +++ b/samples/rust-example/output/config.rs @@ -0,0 +1,53 @@ +use std::time::Duration; +/// Configuration for the key-value store. +pub const MAX_ENTRIES: usize = 10_000; +pub const DEFAULT_TTL_SECS: u64 = 3600; +/// Configuration options for initializing a Store. +/// +/// Use the builder methods to customize behavior. +#[derive(Debug, Clone)] +pub struct Config { + pub max_capacity: usize, + pub default_ttl: Option, + pub enable_eviction: bool, + pub name: String, +} +impl Config { + /// Creates a new Config with sensible defaults. + pub fn new() -> Self { + Self { + max_capacity: MAX_ENTRIES, + default_ttl: Some(Duration::from_secs(DEFAULT_TTL_SECS)), + enable_eviction: true, + name: String::from("default"), + } + } + /// Sets the maximum number of entries. + #[must_use]pub fn with_max_capacity(self, capacity: usize) -> Self { + Self { + max_capacity: capacity, + ..self + } + } + /// Sets the default TTL for entries. + #[must_use]pub fn with_ttl(self, ttl: Duration) -> Self { + Self { + default_ttl: Some(ttl), + ..self + } + } + /// Disables automatic eviction of expired entries. + #[must_use]pub fn disable_eviction(self) -> Self { + Self { + enable_eviction: false, + ..self + } + } + /// Sets the name of this store instance. + #[must_use]pub fn with_name(self, name: &str) -> Self { + Self { + name: name.to_owned(), + ..self + } + } +} diff --git a/samples/rust-example/output/error/mod.rs b/samples/rust-example/output/error/mod.rs new file mode 100644 index 000000000..3ba399145 --- /dev/null +++ b/samples/rust-example/output/error/mod.rs @@ -0,0 +1,29 @@ +use std::fmt::{Display, Formatter}; +/// Error types for the key-value store. +#[derive(Debug, Clone)] +pub enum StoreError { + /// The requested key was not found. + NotFound, + /// The store has reached its maximum capacity. + StorageFull, + /// Failed to serialize or deserialize a value. + SerializationError(String), + /// Failed to acquire a lock on the store. + LockError(String), +} +impl Display for StoreError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::NotFound => write!(f, "key not found"), + Self::StorageFull => write!(f, "storage is full"), + Self::SerializationError(msg) => write!( + f, + "serialization error: {}", + msg + ), + Self::LockError(msg) => write!(f, "lock error: {}", msg), + } + } +} +/// A specialized Result type for store operations. +pub type Result = std::result::Result; diff --git a/samples/rust-example/output/lib.rs b/samples/rust-example/output/lib.rs new file mode 100644 index 000000000..796fdab2f --- /dev/null +++ b/samples/rust-example/output/lib.rs @@ -0,0 +1,10 @@ +//! A generic, thread-safe key-value store library. +//! +//! This crate provides a configurable in-memory store +//! with support for TTL-based expiration, capacity limits, +//! and trait-based extensibility. + +mod config; +pub mod error; +pub mod store; +pub mod traits; diff --git a/samples/rust-example/output/store/mod.rs b/samples/rust-example/output/store/mod.rs new file mode 100644 index 000000000..93f47f229 --- /dev/null +++ b/samples/rust-example/output/store/mod.rs @@ -0,0 +1,114 @@ +use std::collections::HashMap; +use std::time::{Duration, Instant}; + +use crate::error::{Result, StoreError}; +use crate::traits::Cacheable; +/// Core storage engine for the key-value store. +/// +/// Provides a generic, thread-safe store with support +/// for expiration and capacity limits. +/// Represents the current status of a cached entry. +#[derive(Debug, Clone, PartialEq)] +pub enum EntryStatus { + /// The entry is valid and accessible. + Active, + /// The entry has passed its time-to-live. + Expired, + /// The entry was removed to make room for new entries. + Evicted, +} +/// A single entry in the store, holding a value and metadata. +#[derive(Debug, Clone)] +pub struct Entry { + pub value: V, + pub created_at: Instant, + pub ttl: Option, + pub status: EntryStatus, +} +/// A generic key-value store with capacity limits and TTL support. +pub struct Store { + data: HashMap>, + max_capacity: usize, + default_ttl: Option, +} +impl Store { + /// Creates a new store with the given maximum capacity. + pub fn new(max_capacity: usize) -> Self { + Self { + data: HashMap::new(), + max_capacity, + default_ttl: None, + } + } + /// Sets a default time-to-live for new entries. + pub fn with_default_ttl(self, ttl: Duration) -> Self { + Self { + default_ttl: Some(ttl), + ..self + } + } + /// Inserts a value into the store, returning an error if full. + pub fn insert(&mut self, key: K, value: V) -> Result<()> { + if self.data.len() >= self.max_capacity && !self.data.contains_key(&key) { + return Err(StoreError::StorageFull) + ; + }let entry = Entry { + value, + created_at: Instant::now(), + ttl: self.default_ttl, + status: EntryStatus::Active, + };self.data.insert(key, entry); Ok(()) + } + /// Retrieves a value by key, checking for expiration. + pub fn get(&self, key: &K) -> Result<&V> { + match self.data.get(key) { + Some(entry) => { + if entry.status == EntryStatus::Expired { + return Err(StoreError::NotFound) + ; + } + if let Some(ttl) = entry.ttl { + if entry.created_at.elapsed() > ttl { + return Err(StoreError::NotFound) + ; + } + } + Ok(&entry.value) + }, + None => Err(StoreError::NotFound), + } + } + /// Removes an entry from the store. + pub fn remove(&mut self, key: &K) -> Result { + self.data.remove(key).map(|entry| entry.value).ok_or(StoreError::NotFound) + } + /// Returns the number of entries in the store. + #[inline]pub fn len(&self) -> usize { + self.data.len() + } + /// Returns true if the store is empty. + #[inline]pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + /// Evicts all expired entries from the store. + pub fn evict_expired(&mut self) -> usize { + let before = self.data.len();self.data.retain(|_, entry| { + if let Some(ttl) = entry.ttl { + entry.created_at.elapsed() <= ttl + } else { + true + } + });before - self.data.len() + } +} +impl Cacheable for Store { + fn cache_key(&self) -> String { + format!("store::{}", self.data.len()) + } + fn is_expired(&self) -> bool { + self.data.is_empty() + } + fn cached_value(&self) -> Option<&V> { + None + } +} diff --git a/samples/rust-example/output/traits/mod.rs b/samples/rust-example/output/traits/mod.rs new file mode 100644 index 000000000..7a884a47c --- /dev/null +++ b/samples/rust-example/output/traits/mod.rs @@ -0,0 +1,13 @@ +use crate::error::Result; +/// Traits defining serialization and caching behavior. +/// A trait for types that can be serialized to and deserialized from bytes. +pub trait Serializable { + fn to_bytes(&self) -> Result>; + fn from_bytes(bytes: &[u8]) -> Result where Self: Sized; +} +/// A trait for types that support caching with expiration. +pub trait Cacheable { + fn cache_key(&self) -> String; + fn is_expired(&self) -> bool; + fn cached_value(&self) -> Option<&V>; +} diff --git a/samples/rust-example/package.json b/samples/rust-example/package.json new file mode 100644 index 000000000..be414889f --- /dev/null +++ b/samples/rust-example/package.json @@ -0,0 +1,41 @@ +{ + "name": "rust-example", + "version": "1.0.0", + "description": "Example of generating Rust code using @alloy-js/rust", + "repository": { + "type": "git", + "url": "git+https://github.com/alloy-framework/alloy.git" + }, + "main": "index.js", + "type": "module", + "private": true, + "dependencies": { + "@alloy-js/core": "workspace:~", + "@alloy-js/rust": "workspace:~" + }, + "devDependencies": { + "@alloy-js/cli": "workspace:~", + "@alloy-js/rollup-plugin": "workspace:~", + "@types/node": "catalog:", + "concurrently": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "engines": { + "node": ">=20.0.0" + }, + "scripts": { + "build": "alloy build", + "clean": "rimraf dist/ .temp/ alloy-output/", + "test": "vitest run", + "test:watch": "vitest -w", + "watch": "alloy build --watch", + "generate": "node dist/src/index.js" + }, + "exports": { + ".": { + "import": "./dist/src/index.js", + "types": "./dist/src/index.d.ts" + } + } +} diff --git a/samples/rust-example/src/components/config-file.tsx b/samples/rust-example/src/components/config-file.tsx new file mode 100644 index 000000000..184f7ffd3 --- /dev/null +++ b/samples/rust-example/src/components/config-file.tsx @@ -0,0 +1,157 @@ +import { Children, refkey } from "@alloy-js/core"; +import { + Attribute, + ConstDeclaration, + DocComment, + Field, + FieldInit, + FunctionDeclaration, + ImplBlock, + SourceFile, + StructDeclaration, + StructExpression, + std, +} from "@alloy-js/rust"; + +export const configKey = refkey(); +export const maxEntriesKey = refkey(); +export const defaultTtlSecsKey = refkey(); + +export interface ConfigFileProps { + children?: Children; +} + +export function ConfigFile(props: ConfigFileProps) { + return ( + + Configuration for the key-value store. + + + 10_000 + + + + + + 3600 + + + + + + {`Configuration options for initializing a Store.\n\nUse the builder methods to customize behavior.`} + + + + + {"Option<"} + {std.time.Duration} + {">"} + + } + /> + + + + + + + + Creates a new Config with sensible defaults. + + + MAX_ENTRIES + + {"Some("} + {std.time.Duration}::from_secs(DEFAULT_TTL_SECS){")"} + + true + String::from("default") + + + + + + Sets the maximum number of entries. + + + + capacity + + + + + + Sets the default TTL for entries. + + + + Some(ttl) + + + + + + Disables automatic eviction of expired entries. + + + + false + + + + + + Sets the name of this store instance. + + + + name.to_owned() + + + + + ); +} diff --git a/samples/rust-example/src/components/error-module.tsx b/samples/rust-example/src/components/error-module.tsx new file mode 100644 index 000000000..9d3f80ec7 --- /dev/null +++ b/samples/rust-example/src/components/error-module.tsx @@ -0,0 +1,112 @@ +import { Children, refkey } from "@alloy-js/core"; +import { + DocComment, + EnumDeclaration, + EnumVariant, + FunctionDeclaration, + ImplBlock, + MacroCall, + MatchArm, + MatchExpression, + ModuleDirectory, + SourceFile, + TypeAlias, + std, +} from "@alloy-js/rust"; + +export const storeErrorKey = refkey(); +export const resultAliasKey = refkey(); + +export interface ErrorModuleProps { + children?: Children; +} + +export function ErrorModule(props: ErrorModuleProps) { + return ( + + + Error types for the key-value store. + + + + + + + + + + + + {"&mut "} + {std.fmt.Formatter} + {"<'_>"} + + ), + }, + ]} + returnType={std.fmt.Result} + > + + + + + + + + + + + + + + + + + + + + A specialized Result type for store operations. + + {std.result.Result} + {""} + + + + ); +} diff --git a/samples/rust-example/src/components/store-module.tsx b/samples/rust-example/src/components/store-module.tsx new file mode 100644 index 000000000..fe2e112ee --- /dev/null +++ b/samples/rust-example/src/components/store-module.tsx @@ -0,0 +1,419 @@ +import { Children, code, refkey } from "@alloy-js/core"; +import { + Attribute, + ClosureExpression, + DocComment, + EnumDeclaration, + EnumVariant, + Field, + FieldInit, + FunctionDeclaration, + IfExpression, + ImplBlock, + LetBinding, + MacroCall, + MatchArm, + MatchExpression, + MethodChainExpression, + ModuleDirectory, + ReturnExpression, + SourceFile, + StructDeclaration, + StructExpression, + std, +} from "@alloy-js/rust"; +import { resultAliasKey, storeErrorKey } from "./error-module.js"; +import { cacheableKey } from "./traits-module.js"; + +export const storeKey = refkey(); +export const entryKey = refkey(); +export const entryStatusKey = refkey(); + +export interface StoreModuleProps { + children?: Children; +} + +export function StoreModule(props: StoreModuleProps) { + return ( + + + + {`Core storage engine for the key-value store.\n\nProvides a generic, thread-safe store with support\nfor expiration and capacity limits.`} + + + + + + + + + + + + + + + {"Option<"} + {std.time.Duration} + {">"} + + } + /> + + + + + + + {std.cmp.Eq} + {std.hash.Hash} + {std.clone.Clone} + + ), + }, + { + name: "V", + constraint: ( + <> + {std.clone.Clone} + {std.marker.Send} + {std.marker.Sync} + + ), + }, + ]} + doc="A generic key-value store with capacity limits and TTL support." + > + + {std.collections.HashMap} + {">"} + + } + /> + + + {"Option<"} + {std.time.Duration} + {">"} + + } + /> + + + + + + {std.cmp.Eq} + {std.hash.Hash} + {std.clone.Clone} + + ), + }, + { + name: "V", + constraint: ( + <> + {std.clone.Clone} + {std.marker.Send} + {std.marker.Sync} + + ), + }, + ]} + > + + Creates a new store with the given maximum capacity. + + + + + {std.collections.HashMap}::new() + + + None + + + + + + Sets a default time-to-live for new entries. + + + Some(ttl) + + + + + + + Inserts a value into the store, returning an error if full. + + + {resultAliasKey} + {"<()>"} + + } + > + + <> + + Err(StoreError::StorageFull) + + ; + + + + + + + {std.time.Instant}::now() + + self.default_ttl + EntryStatus::Active + + + self.data.insert(key, entry); Ok(()) + + + + + + Retrieves a value by key, checking for expiration. + + + {resultAliasKey} + {"<&V>"} + + } + > + + + + <> + + Err(StoreError::NotFound) + + ; + + + + + <> + + Err(StoreError::NotFound) + + ; + + + + Ok(&entry.value) + + Err(StoreError::NotFound) + + + + + + Removes an entry from the store. + + {resultAliasKey} + {""} + + } + > + + + + entry.value + , + ]} + /> + {storeErrorKey}::NotFound]} + /> + + + + + + Returns the number of entries in the store. + + + self.data.len() + + + + + Returns true if the store is empty. + + + self.data.is_empty() + + + + + Evicts all expired entries from the store. + + self.data.len() + {code`self.data.retain(|_, entry| { + if let Some(ttl) = entry.ttl { + entry.created_at.elapsed() <= ttl + } else { + true + } + });`} + before - self.data.len() + + + + + + + {cacheableKey} + {""} + + } + typeParameters={[ + { + name: "K", + constraint: ( + <> + {std.cmp.Eq} + {std.hash.Hash} + {std.clone.Clone} + + ), + }, + { + name: "V", + constraint: ( + <> + {std.clone.Clone} + {std.marker.Send} + {std.marker.Sync} + + ), + }, + ]} + > + + + + + + + + self.data.is_empty() + + + + + + None + + + + + ); +} diff --git a/samples/rust-example/src/components/traits-module.tsx b/samples/rust-example/src/components/traits-module.tsx new file mode 100644 index 000000000..8e4f9c995 --- /dev/null +++ b/samples/rust-example/src/components/traits-module.tsx @@ -0,0 +1,105 @@ +import { Children, refkey } from "@alloy-js/core"; +import { + DocComment, + FunctionDeclaration, + ModuleDirectory, + SourceFile, + TraitDeclaration, + std, +} from "@alloy-js/rust"; +import { resultAliasKey } from "./error-module.js"; + +export const serializableKey = refkey(); +export const cacheableKey = refkey(); + +export interface TraitsModuleProps { + children?: Children; +} + +export function TraitsModule(props: TraitsModuleProps) { + return ( + + + + Traits defining serialization and caching behavior. + + + + + {resultAliasKey} + {">"} + + } + /> + + + + + {resultAliasKey} + {""} + + } + whereClause="Self: Sized" + /> + + + + + + A trait for types that support caching with expiration. + + + {std.clone.Clone} + {std.marker.Send} + {std.marker.Sync} + + ), + }, + ]} + > + + + + + + + + + + + + + ); +} diff --git a/samples/rust-example/src/index.tsx b/samples/rust-example/src/index.tsx new file mode 100644 index 000000000..064f2f745 --- /dev/null +++ b/samples/rust-example/src/index.tsx @@ -0,0 +1,40 @@ +import { Output, render, writeOutput } from "@alloy-js/core"; +import { + CrateDirectory, + ModuleDocComment, + SourceFile, + createRustNamePolicy, + std, +} from "@alloy-js/rust"; +import { ConfigFile } from "./components/config-file.js"; +import { ErrorModule } from "./components/error-module.js"; +import { StoreModule } from "./components/store-module.js"; +import { TraitsModule } from "./components/traits-module.js"; + +const output = render( + + + + + + + + + {`A generic, thread-safe key-value store library.\n\nThis crate provides a configurable in-memory store\nwith support for TTL-based expiration, capacity limits,\nand trait-based extensibility.`} + + } + /> + + , +); + +await writeOutput(output, "./output"); diff --git a/samples/rust-example/test/smoke.test.tsx b/samples/rust-example/test/smoke.test.tsx new file mode 100644 index 000000000..3c23a15dd --- /dev/null +++ b/samples/rust-example/test/smoke.test.tsx @@ -0,0 +1,215 @@ +import { Output, render, writeOutput } from "@alloy-js/core"; +import { + CrateDirectory, + ModuleDocComment, + SourceFile, + createRustNamePolicy, +} from "@alloy-js/rust"; +import { execSync } from "child_process"; +import { existsSync, rmSync } from "fs"; +import { tmpdir } from "os"; +import { join } from "path"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { ConfigFile } from "../src/components/config-file.js"; +import { ErrorModule } from "../src/components/error-module.js"; +import { StoreModule } from "../src/components/store-module.js"; +import { TraitsModule } from "../src/components/traits-module.js"; +import { stdCrate } from "../src/externals.js"; + +function hasRustToolchain(): boolean { + try { + // Check standard PATH first, then common cargo install location + const cargoPath = + process.env.CARGO_HOME ? + join(process.env.CARGO_HOME, "bin", "cargo") + : join(process.env.HOME ?? "", ".cargo", "bin", "cargo"); + const cmd = existsSync(cargoPath) ? cargoPath : "cargo"; + execSync(`${cmd} --version`, { stdio: "pipe" }); + return true; + } catch { + return false; + } +} + +function getCargoBin(): string { + const cargoPath = + process.env.CARGO_HOME ? + join(process.env.CARGO_HOME, "bin", "cargo") + : join(process.env.HOME ?? "", ".cargo", "bin", "cargo"); + return existsSync(cargoPath) ? cargoPath : "cargo"; +} + +/** + * Post-process generated Rust code to fix known framework formatting issues. + * + * The alloy framework currently renders items without proper newline separation + * between doc comments, attributes, and declarations. This causes Rust's + * line-comment syntax (`///`) to consume following declarations. + * + * Tracked as backlog item: + * - framework-newline-rendering: Missing newlines between items + */ +function postProcessRustOutput(content: string): string { + // Fix doc comments running into declarations on the same line + content = content.replace( + /\.(pub |impl[ <{]|fn |struct |enum |const |mod |use |trait [A-Z]|type [A-Z])/g, + ".\n$1", + ); + content = content.replace(/\.#\[/g, ".\n#["); + + // Fix attributes running into declarations + content = content.replace(/\](pub |fn |impl )/g, "]\n$1"); + + // Fix closing braces running into next items + content = content.replace( + /\}(pub |fn |let |impl |struct |enum |type |const |mod |use |trait |#\[|\/\/\/|\/\/!)/g, + "}\n$1", + ); + + // Fix semicolons running into next statements + content = content.replace( + /;(pub |fn |let |self\.|Ok\(|\/\/\/|#\[)/g, + ";\n$1", + ); + + // Fix return value with semicolon on separate line + content = content.replace(/(return [^;\n]+)\n\s*;/g, "$1;"); + + // Fix closure end running into next statement + content = content.replace(/\}\);([a-z])/g, "});\n$1"); + + return content; +} + +const hasCargo = hasRustToolchain(); + +describe.skipIf(!hasCargo)("rust smoke test", () => { + const outputDir = join(tmpdir(), `alloy-rust-smoke-${Date.now()}`); + + beforeAll(async () => { + const output = render( + + + + + + + + + A generic, thread-safe key-value store library. + + } + /> + + , + ); + + await writeOutput(output, outputDir); + }, 30_000); + + afterAll(() => { + if (existsSync(outputDir)) { + rmSync(outputDir, { recursive: true, force: true }); + } + }); + + it("generates all expected files", () => { + expect(existsSync(join(outputDir, "Cargo.toml"))).toBe(true); + expect(existsSync(join(outputDir, "lib.rs"))).toBe(true); + expect(existsSync(join(outputDir, "config.rs"))).toBe(true); + expect(existsSync(join(outputDir, "error", "mod.rs"))).toBe(true); + expect(existsSync(join(outputDir, "store", "mod.rs"))).toBe(true); + expect(existsSync(join(outputDir, "traits", "mod.rs"))).toBe(true); + }); + + it("generates proper use statements from References", () => { + const { readFileSync } = require("fs"); + const storeMod = readFileSync(join(outputDir, "store", "mod.rs"), "utf-8"); + + // References should generate proper use statements + expect(storeMod).toContain("use std::collections::HashMap"); + expect(storeMod).toContain("use std::time::"); + expect(storeMod).toMatch(/use crate::error::\{?.*StoreError/); + expect(storeMod).toMatch(/use crate::error::\{?.*Result/); + + // Types should use short names (not fully-qualified) + expect(storeMod).toMatch(/data: HashMap/); + + // Prelude-shadowed Result should use short name in return types + expect(storeMod).toMatch(/-> Result<\(\)>/); + expect(storeMod).not.toMatch(/-> crate::error::Result/); + + // Traits module should also import Result via use statement + const traitsMod = readFileSync( + join(outputDir, "traits", "mod.rs"), + "utf-8", + ); + expect(traitsMod).toMatch(/use crate::error::\{?.*Result/); + expect(traitsMod).toMatch(/-> Result>/); + expect(traitsMod).not.toMatch(/-> crate::error::Result/); + }); + + it("generates correct enum variant syntax", () => { + const { readFileSync } = require("fs"); + const errorMod = readFileSync(join(outputDir, "error", "mod.rs"), "utf-8"); + + // Enum variants should be tuple variants, not struct variants + expect(errorMod).toContain("SerializationError(String)"); + expect(errorMod).toContain("LockError(String)"); + expect(errorMod).not.toContain("SerializationError {"); + }); + + it("generates method chain for remove", () => { + const { readFileSync } = require("fs"); + const storeMod = readFileSync(join(outputDir, "store", "mod.rs"), "utf-8"); + + // MethodChainExpression should generate proper chain + expect(storeMod).toMatch(/\.remove\(key\)/); + expect(storeMod).toMatch(/\.map\(\|entry\| entry\.value\)/); + expect(storeMod).toMatch(/\.ok_or\(StoreError::NotFound\)/); + }); + + it("compiles with cargo check after post-processing", async () => { + const { + readFileSync, + writeFileSync, + readdirSync, + statSync, + } = require("fs"); + + // Post-process all .rs files to fix known framework formatting issues + function processDir(dir: string) { + for (const entry of readdirSync(dir)) { + const fullPath = join(dir, entry); + if (statSync(fullPath).isDirectory()) { + processDir(fullPath); + } else if (entry.endsWith(".rs")) { + const content = readFileSync(fullPath, "utf-8"); + writeFileSync(fullPath, postProcessRustOutput(content)); + } + } + } + processDir(outputDir); + + const cargo = getCargoBin(); + const result = execSync( + `${cargo} check --manifest-path ${join(outputDir, "Cargo.toml")} 2>&1`, + { + encoding: "utf-8", + timeout: 60_000, + }, + ); + expect(result).toContain("Finished"); + }, 60_000); +}); diff --git a/samples/rust-example/tsconfig.json b/samples/rust-example/tsconfig.json new file mode 100644 index 000000000..92ebc2554 --- /dev/null +++ b/samples/rust-example/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true, + "outDir": "dist" + }, + "references": [ + { "path": "../../packages/core" }, + { "path": "../../packages/rust" } + ], + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["node_modules", "dist"] +} diff --git a/samples/rust-example/vitest.config.ts b/samples/rust-example/vitest.config.ts new file mode 100644 index 000000000..ba661c0a9 --- /dev/null +++ b/samples/rust-example/vitest.config.ts @@ -0,0 +1,10 @@ +import alloyPlugin from "@alloy-js/rollup-plugin"; +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + esbuild: { + jsx: "preserve", + sourcemap: "both", + }, + plugins: [alloyPlugin()], +});