Skip to main content

openapi_to_rust/server/
mod.rs

1//! Server codegen — opt-in Axum scaffolding for user-selected operations.
2//!
3//! Phase 1 (this file): read-only operation index for the
4//! `openapi-to-rust server list` command. Later phases build on
5//! [`OperationIndex`] for selector resolution, codegen, and TOML edits.
6//!
7//! See `docs/planning/server-codegen.md` for the full design.
8
9use crate::analysis::{OperationInfo, SchemaAnalysis};
10
11pub mod codegen;
12pub mod edit;
13pub mod list;
14pub mod selector;
15
16pub use selector::{Resolution, Selector, SelectorParseError, SelectorResolveError, resolve};
17
18/// Read-only snapshot of every operation in a spec, in the order the
19/// analyzer surfaced them. Built once per command invocation.
20#[derive(Debug, Clone)]
21pub struct OperationIndex {
22    operations: Vec<OperationSummary>,
23}
24
25/// Display-friendly subset of [`OperationInfo`] used by `server list`
26/// and (later) by selector resolution and `server add`/`remove`.
27#[derive(Debug, Clone, serde::Serialize)]
28pub struct OperationSummary {
29    pub operation_id: String,
30    pub method: String,
31    pub path: String,
32    pub tags: Vec<String>,
33    /// True when any response declares `text/event-stream`. Surfaces
34    /// in the listing so users can spot SSE endpoints at a glance.
35    pub supports_streaming: bool,
36}
37
38impl OperationIndex {
39    /// Build an index from an already-completed [`SchemaAnalysis`].
40    pub fn from_analysis(analysis: &SchemaAnalysis) -> Self {
41        let operations = analysis
42            .operations
43            .values()
44            .map(OperationSummary::from)
45            .collect();
46        Self { operations }
47    }
48
49    /// Build directly from pre-built summaries. Used by tests and by
50    /// callers that have already projected `OperationInfo` to the
51    /// display shape.
52    pub fn from_summaries(operations: Vec<OperationSummary>) -> Self {
53        Self { operations }
54    }
55
56    pub fn operations(&self) -> &[OperationSummary] {
57        &self.operations
58    }
59
60    /// Count of distinct tags across all operations. Untagged ops are
61    /// counted under a synthetic `<untagged>` bucket only for display;
62    /// they do not appear in this count.
63    pub fn tag_count(&self) -> usize {
64        let mut tags: std::collections::BTreeSet<&str> = std::collections::BTreeSet::new();
65        for op in &self.operations {
66            for t in &op.tags {
67                tags.insert(t.as_str());
68            }
69        }
70        tags.len()
71    }
72}
73
74impl From<&OperationInfo> for OperationSummary {
75    fn from(op: &OperationInfo) -> Self {
76        Self {
77            operation_id: op.operation_id.clone(),
78            method: op.method.clone(),
79            path: op.path.clone(),
80            tags: op.tags.clone(),
81            supports_streaming: op.supports_streaming,
82        }
83    }
84}