1#![allow(
18 clippy::missing_errors_doc,
19 clippy::missing_panics_doc,
20 clippy::must_use_candidate
21)]
22
23pub mod ast;
24pub mod compiler;
25pub mod effects;
26pub mod extensions;
27pub mod heap;
28pub mod identifiers;
29pub mod runtime;
30pub mod testing;
31pub mod topology;
32pub mod tracing;
33
34pub use runtime::{SystemClock, SystemRng};
36
37pub use identifiers::{Endpoint as TopologyEndpoint, Namespace, Region, RoleName};
39
40pub use ast::{Choreography, MessageType, Protocol, Role};
42pub use compiler::generate_effects_protocol;
43pub use compiler::{
44 create_standard_extension_parser, format_choreography, format_choreography_str,
45 format_choreography_with_config, ExtensionParseError, ExtensionParser, ExtensionParserBuilder,
46 GrammarComposer, GrammarComposerBuilder, GrammarCompositionError, PrettyConfig,
47};
48pub use effects::middleware::{Metrics, Retry, Trace};
49pub use effects::NoOpHandler;
50pub use effects::{
51 interpret, validate_handler_contract_profile, validated_contract_profile, ChoreoHandler,
52 ChoreoHandlerExt, ChoreoResult, ChoreographyError, DeliveryModel, DocumentedHandlerContract,
53 Effect, Endpoint, ExtensionDispatchContract, ExtensionDispatchMode, HandlerContractProfile,
54 HandlerContractTier, HandlerContractViolation, InterpretResult, InterpreterState, LabelId,
55 MessageTag, Program, ProgramBuilder, ProgramMessage, ProtocolSemanticContract, RetryPolicy,
56 RoleId, TimeoutPolicy, TransportPolicyContract,
57};
58pub use effects::{InMemoryHandler, RecordedEvent, RecordingHandler};
59pub use effects::{TelltaleEndpoint, TelltaleHandler, TelltaleSession};
60pub use extensions::{
61 CodegenContext, ExtensionRegistry, ExtensionValidationError, GrammarExtension, ParseContext,
62 ParseError, ProjectionContext, ProtocolExtension, StatementParser,
63};
64pub use runtime::{spawn, spawn_local};
65pub use topology::{
66 parse_topology, validate_transport_contract_profile, validated_transport_contract_profile,
67 ByteMessage, DocumentedTransportContract, InMemoryChannelTransport, Location, ParsedTopology,
68 RoleFamilyConstraint, RoleFamilyConstraintError, Topology, TopologyBuilder, TopologyConstraint,
69 TopologyError, TopologyHandler, TopologyHandlerBuilder, TopologyLoadError, TopologyMode,
70 TopologyParseError, TopologyValidation, Transport, TransportContractProfile,
71 TransportContractTier, TransportContractViolation, TransportError, TransportFactory,
72 TransportMessage, TransportOperationalContract, TransportResult, TransportSemanticContract,
73 TransportStartupMode, TransportType,
74};
75
76pub use heap::{
78 ChannelState, Direction, Heap, HeapCommitment, HeapError, MerkleProof, MerkleTree,
79 Message as HeapMessage, MessagePayload, ProofStep, Resource, ResourceId,
80};
81
82pub use testing::{
84 AsyncClock, BlockedOn, Checkpoint, Clock, InMemoryTransport, MockClock, NullObserver,
85 ProtocolEnvelope, ProtocolObserver, ProtocolStateMachine, RecordingObserver, Rng, SeededRng,
86 SimulatedTransport, StepInput, StepOutput, WallClock,
87};
88
89pub use telltale_macros::tell;
91pub use telltale_types::{ChannelCapacity, MessageLenBytes, QueueCapacity};
92
93#[doc(hidden)]
98pub mod unstable {
99 pub use crate::extensions::{CodegenContext, ParseContext, ProjectionContext, StatementParser};
100}
101
102pub fn parse_and_generate_with_extensions(
106 input: &str,
107 extension_registry: &ExtensionRegistry,
108) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
109 use compiler::codegen::generate_choreography_code_with_extensions;
110 use compiler::parser::parse_choreography_str_with_extensions;
111 use compiler::projection::project;
112
113 let (choreography, extensions) =
114 parse_choreography_str_with_extensions(input, extension_registry)
115 .map_err(CompilationError::Parse)?;
116
117 choreography
119 .validate()
120 .map_err(|e| CompilationError::Validation(e.to_string()))?;
121
122 let mut local_types = Vec::new();
124 for role in &choreography.roles {
125 let local_type = project(&choreography, role)
126 .map_err(|e| CompilationError::Projection(e.to_string()))?;
127 local_types.push((role.clone(), local_type));
128 }
129
130 let generated_code =
132 generate_choreography_code_with_extensions(&choreography, &local_types, &extensions);
133
134 Ok(generated_code)
135}
136
137pub fn compile_choreography_with_extensions(
139 input: &str,
140) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
141 let registry = ExtensionRegistry::with_builtin_extensions();
142 parse_and_generate_with_extensions(input, ®istry)
143}
144
145pub fn parse_choreography_with_extensions(
147 input: &str,
148 extension_registry: &ExtensionRegistry,
149) -> std::result::Result<(Choreography, Vec<Box<dyn ProtocolExtension>>), CompilationError> {
150 use compiler::parser::parse_choreography_str_with_extensions;
151
152 parse_choreography_str_with_extensions(input, extension_registry)
153 .map_err(CompilationError::Parse)
154}
155
156#[derive(Debug, thiserror::Error)]
158pub enum CompilationError {
159 #[error("parse error: {0}")]
160 Parse(#[from] compiler::parser::ParseError),
161
162 #[error("validation error: {0}")]
163 Validation(String),
164
165 #[error("projection error: {0}")]
166 Projection(String),
167
168 #[error("code generation error: {0}")]
169 Codegen(String),
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175 use crate::effects::{LabelId, RoleId};
176 use crate::identifiers::RoleName;
177
178 #[allow(dead_code)]
180 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
181 enum TestRole {
182 Alice,
183 Bob,
184 }
185
186 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
187 enum TestLabel {
188 Test,
189 }
190
191 impl LabelId for TestLabel {
192 fn as_str(&self) -> &'static str {
193 match self {
194 TestLabel::Test => "test",
195 }
196 }
197
198 fn from_str(label: &str) -> Option<Self> {
199 match label {
200 "test" => Some(TestLabel::Test),
201 _ => None,
202 }
203 }
204 }
205
206 impl RoleId for TestRole {
207 type Label = TestLabel;
208
209 fn role_name(&self) -> RoleName {
210 match self {
211 TestRole::Alice => RoleName::from_static("Alice"),
212 TestRole::Bob => RoleName::from_static("Bob"),
213 }
214 }
215 }
216
217 #[test]
218 fn test_module_structure() {
219 let _choreography: Option<Choreography> = None;
221 let _protocol: Option<Protocol> = None;
222 let _role: Option<Role> = None;
223 let _message_type: Option<MessageType> = None;
224
225 let _program: Option<Program<TestRole, String>> = None;
227 let _result: Option<ChoreoResult<()>> = None;
228 let _label: Option<TestLabel> = None;
229 }
230
231 #[test]
232 fn test_free_algebra_integration() {
233 use std::time::Duration;
234
235 let program = Program::<TestRole, String>::new()
237 .send(TestRole::Bob, "hello".to_string())
238 .recv::<String>(TestRole::Bob)
239 .choose(TestRole::Bob, TestLabel::Test)
240 .offer(TestRole::Bob)
241 .with_timeout(
242 TestRole::Bob,
243 Duration::from_millis(100),
244 Program::new().end(),
245 )
246 .parallel(vec![Program::new().end()])
247 .end();
248
249 assert_eq!(program.send_count(), 1);
251 assert_eq!(program.recv_count(), 1);
252 assert!(program.has_timeouts());
253 assert!(program.has_parallel());
254 }
255}