Skip to main content

protoc_gen_rust_temporal/
model.rs

1//! Internal representation produced by `parse.rs` and consumed by `render.rs`.
2//!
3//! The schema deliberately mirrors cludden's `temporal.v1.*` annotation
4//! surface (see `proto/temporal/v1/temporal.proto`), only retaining the
5//! fields needed for v1 Rust client emit. Anything we read but ignore (XNS,
6//! patches, CLI options) lives in the descriptor pool and is silently
7//! dropped here.
8
9use std::time::Duration;
10
11/// One Temporal-bearing proto service after parsing + validation.
12#[derive(Debug)]
13pub struct ServiceModel {
14    /// Fully-qualified proto package, e.g. `"jobs.v1"`.
15    pub package: String,
16    /// Service name from the proto, e.g. `"JobService"`.
17    pub service: String,
18    /// Source `.proto` file path as `protoc` saw it.
19    pub source_file: String,
20    /// `temporal.v1.service.task_queue` if the service carries the annotation.
21    /// Used as the default `task_queue` when a workflow does not override it.
22    pub default_task_queue: Option<String>,
23    pub workflows: Vec<WorkflowModel>,
24    pub signals: Vec<SignalModel>,
25    pub queries: Vec<QueryModel>,
26    pub updates: Vec<UpdateModel>,
27    pub activities: Vec<ActivityModel>,
28}
29
30#[derive(Debug)]
31pub struct WorkflowModel {
32    /// Rpc method name as declared in proto (e.g. `"RunJob"`).
33    pub rpc_method: String,
34    /// Cross-language workflow registration name. Defaults to
35    /// `"<package>.<Service>/<rpc>"` when `WorkflowOptions.name` is empty.
36    pub registered_name: String,
37    pub input_type: ProtoType,
38    pub output_type: ProtoType,
39    /// Effective task queue: `WorkflowOptions.task_queue` if set, else the
40    /// service-level default. `None` means neither was supplied — render
41    /// will require the caller to pass one.
42    pub task_queue: Option<String>,
43    /// Parsed form of cludden's `id` Go-template expression, compiled at
44    /// parse time against the workflow's input message descriptor. Each
45    /// segment is either a literal piece of the template or a reference to
46    /// a field on the input message. Render emits a private
47    /// `<wf>_id(input: &Input) -> String` function that walks the segments
48    /// via `format!`, so the substitution happens at codegen time — no
49    /// runtime template engine required.
50    pub id_expression: Option<Vec<IdTemplateSegment>>,
51    pub id_reuse_policy: Option<IdReusePolicy>,
52    pub execution_timeout: Option<Duration>,
53    pub run_timeout: Option<Duration>,
54    pub task_timeout: Option<Duration>,
55    /// Additional names this workflow is also registered under.
56    pub aliases: Vec<String>,
57    pub attached_signals: Vec<SignalRef>,
58    pub attached_queries: Vec<QueryRef>,
59    pub attached_updates: Vec<UpdateRef>,
60}
61
62/// Reference from a `WorkflowOptions.signal` entry to a sibling signal rpc.
63#[derive(Debug, Clone)]
64pub struct SignalRef {
65    /// Value of the `ref` field — must match a sibling rpc method name.
66    pub rpc_method: String,
67    /// If `true`, emit a `_with_start` free function alongside the client.
68    pub start: bool,
69}
70
71#[derive(Debug, Clone)]
72pub struct QueryRef {
73    pub rpc_method: String,
74}
75
76#[derive(Debug, Clone)]
77pub struct UpdateRef {
78    pub rpc_method: String,
79    pub start: bool,
80    pub validate: Option<bool>,
81}
82
83#[derive(Debug)]
84pub struct SignalModel {
85    pub rpc_method: String,
86    /// Cross-language signal name. Defaults to `rpc_method`.
87    pub registered_name: String,
88    pub input_type: ProtoType,
89    /// Must be `google.protobuf.Empty` — validated.
90    pub output_type: ProtoType,
91}
92
93#[derive(Debug)]
94pub struct QueryModel {
95    pub rpc_method: String,
96    pub registered_name: String,
97    pub input_type: ProtoType,
98    pub output_type: ProtoType,
99}
100
101#[derive(Debug)]
102pub struct UpdateModel {
103    pub rpc_method: String,
104    pub registered_name: String,
105    pub input_type: ProtoType,
106    pub output_type: ProtoType,
107    /// Whether `UpdateOptions.validate` was set on this rpc.
108    pub validate: bool,
109}
110
111#[derive(Debug)]
112pub struct ActivityModel {
113    /// Rpc method name. Activity emit is validate-only in v1, but we still
114    /// resolve names so collisions with workflow / signal / query / update
115    /// can be rejected.
116    pub rpc_method: String,
117    pub registered_name: String,
118    pub input_type: ProtoType,
119    pub output_type: ProtoType,
120}
121
122/// A proto type reference, resolved to its fully-qualified name.
123#[derive(Debug, Clone, PartialEq, Eq)]
124pub struct ProtoType {
125    /// Fully-qualified proto type name, e.g. `"jobs.v1.JobInput"` — never
126    /// includes the leading `.` that descriptors use.
127    pub full_name: String,
128    /// `true` when the type is `google.protobuf.Empty`.
129    pub is_empty: bool,
130}
131
132impl ProtoType {
133    pub fn new(full_name: impl Into<String>) -> Self {
134        let full_name = full_name.into();
135        let normalised = full_name
136            .strip_prefix('.')
137            .unwrap_or(&full_name)
138            .to_string();
139        let is_empty = normalised == "google.protobuf.Empty";
140        Self {
141            full_name: normalised,
142            is_empty,
143        }
144    }
145
146    /// Final path segment of `full_name`. For `Empty`, returns `"()"` to
147    /// reflect the render-time substitution.
148    pub fn rust_name(&self) -> &str {
149        if self.is_empty {
150            return "()";
151        }
152        self.full_name.rsplit('.').next().unwrap_or(&self.full_name)
153    }
154}
155
156/// One segment of a workflow's `id` template, resolved against the
157/// workflow input message's descriptor at parse time.
158#[derive(Debug, Clone, PartialEq, Eq)]
159pub enum IdTemplateSegment {
160    /// A literal piece of the template — emitted verbatim into the
161    /// generated `format!`.
162    Literal(String),
163    /// A reference to a field on the workflow input message. The string
164    /// is the **Rust** field name (snake_case), so generated code can
165    /// substitute `input.<field>` directly. Validated to exist on the
166    /// input descriptor at parse time.
167    Field(String),
168}
169
170#[derive(Debug, Clone, Copy, PartialEq, Eq)]
171pub enum IdReusePolicy {
172    AllowDuplicate,
173    AllowDuplicateFailedOnly,
174    RejectDuplicate,
175    TerminateIfRunning,
176}
177
178impl IdReusePolicy {
179    /// Variant identifier on `temporalio_common::WorkflowIdReusePolicy`.
180    pub fn rust_variant(self) -> &'static str {
181        match self {
182            Self::AllowDuplicate => "AllowDuplicate",
183            Self::AllowDuplicateFailedOnly => "AllowDuplicateFailedOnly",
184            Self::RejectDuplicate => "RejectDuplicate",
185            Self::TerminateIfRunning => "TerminateIfRunning",
186        }
187    }
188}