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