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 testing;
30pub mod topology;
31pub mod tracing;
32pub mod util;
33
34pub use util::{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 topology::{
65 parse_topology, validate_transport_contract_profile, validated_transport_contract_profile,
66 ByteMessage, DocumentedTransportContract, InMemoryChannelTransport, Location, ParsedTopology,
67 RoleFamilyConstraint, RoleFamilyConstraintError, Topology, TopologyBuilder, TopologyConstraint,
68 TopologyError, TopologyHandler, TopologyHandlerBuilder, TopologyLoadError, TopologyMode,
69 TopologyParseError, TopologyValidation, Transport, TransportContractProfile,
70 TransportContractTier, TransportContractViolation, TransportError, TransportFactory,
71 TransportMessage, TransportOperationalContract, TransportResult, TransportSemanticContract,
72 TransportStartupMode, TransportType,
73};
74pub use util::{spawn, spawn_local};
75
76pub use heap::{
79 merkle_node_hash, nullifier_leaf_hash, resource_leaf_hash, CanonicalHeapEncoder,
80 CanonicalHeapEncoding, ChannelState, DefaultHeapHasher, Direction, Hasher, Heap,
81 HeapCommitment, HeapError, MerkleProof, MerkleTree, Message as HeapMessage, MessagePayload,
82 ProofStep, Resource, ResourceId, HEAP_ENCODING_MAGIC, HEAP_ENCODING_VERSION,
83};
84
85pub use testing::{
87 AsyncClock, BlockedOn, Checkpoint, Clock, InMemoryTransport, MockClock, NullObserver,
88 ProtocolEnvelope, ProtocolObserver, ProtocolStateMachine, RecordingObserver, Rng, SeededRng,
89 SimulatedTransport, StepInput, StepOutput, WallClock,
90};
91
92pub use telltale_macros::tell;
94pub use telltale_types::{ChannelCapacity, MessageLenBytes, QueueCapacity};
95
96#[doc(hidden)]
101pub mod unstable {
102 pub use crate::extensions::{CodegenContext, ParseContext, ProjectionContext, StatementParser};
103}
104
105pub fn parse_and_generate_with_extensions(
109 input: &str,
110 extension_registry: &ExtensionRegistry,
111) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
112 use compiler::codegen::generate_choreography_code_with_extensions;
113 use compiler::parser::parse_choreography_str_with_extensions;
114 use compiler::projection::project;
115
116 let (choreography, extensions) =
117 parse_choreography_str_with_extensions(input, extension_registry)
118 .map_err(CompilationError::Parse)?;
119
120 choreography
122 .validate()
123 .map_err(|e| CompilationError::Validation(e.to_string()))?;
124
125 let mut local_types = Vec::new();
127 for role in &choreography.roles {
128 let local_type = project(&choreography, role)
129 .map_err(|e| CompilationError::Projection(e.to_string()))?;
130 local_types.push((role.clone(), local_type));
131 }
132
133 let generated_code =
135 generate_choreography_code_with_extensions(&choreography, &local_types, &extensions);
136
137 Ok(generated_code)
138}
139
140pub fn compile_choreography_with_extensions(
142 input: &str,
143) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
144 let registry = ExtensionRegistry::with_builtin_extensions();
145 parse_and_generate_with_extensions(input, ®istry)
146}
147
148pub fn parse_choreography_with_extensions(
150 input: &str,
151 extension_registry: &ExtensionRegistry,
152) -> std::result::Result<(Choreography, Vec<Box<dyn ProtocolExtension>>), CompilationError> {
153 use compiler::parser::parse_choreography_str_with_extensions;
154
155 parse_choreography_str_with_extensions(input, extension_registry)
156 .map_err(CompilationError::Parse)
157}
158
159#[derive(Debug, thiserror::Error)]
161pub enum CompilationError {
162 #[error("parse error: {0}")]
163 Parse(#[from] compiler::parser::ParseError),
164
165 #[error("validation error: {0}")]
166 Validation(String),
167
168 #[error("projection error: {0}")]
169 Projection(String),
170
171 #[error("code generation error: {0}")]
172 Codegen(String),
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::effects::{LabelId, RoleId};
179 use crate::identifiers::RoleName;
180
181 #[allow(dead_code)]
183 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
184 enum TestRole {
185 Alice,
186 Bob,
187 }
188
189 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
190 enum TestLabel {
191 Test,
192 }
193
194 impl LabelId for TestLabel {
195 fn as_str(&self) -> &'static str {
196 match self {
197 TestLabel::Test => "test",
198 }
199 }
200
201 fn from_str(label: &str) -> Option<Self> {
202 match label {
203 "test" => Some(TestLabel::Test),
204 _ => None,
205 }
206 }
207 }
208
209 impl RoleId for TestRole {
210 type Label = TestLabel;
211
212 fn role_name(&self) -> RoleName {
213 match self {
214 TestRole::Alice => RoleName::from_static("Alice"),
215 TestRole::Bob => RoleName::from_static("Bob"),
216 }
217 }
218 }
219
220 #[test]
221 fn test_module_structure() {
222 let _choreography: Option<Choreography> = None;
224 let _protocol: Option<Protocol> = None;
225 let _role: Option<Role> = None;
226 let _message_type: Option<MessageType> = None;
227
228 let _program: Option<Program<TestRole, String>> = None;
230 let _result: Option<ChoreoResult<()>> = None;
231 let _label: Option<TestLabel> = None;
232 }
233
234 #[test]
235 fn test_free_algebra_integration() {
236 use std::time::Duration;
237
238 let program = Program::<TestRole, String>::new()
240 .send(TestRole::Bob, "hello".to_string())
241 .recv::<String>(TestRole::Bob)
242 .choose(TestRole::Bob, TestLabel::Test)
243 .offer(TestRole::Bob)
244 .with_timeout(
245 TestRole::Bob,
246 Duration::from_millis(100),
247 Program::new().end(),
248 )
249 .parallel(vec![Program::new().end()])
250 .end();
251
252 assert_eq!(program.send_count(), 1);
254 assert_eq!(program.recv_count(), 1);
255 assert!(program.has_timeouts());
256 assert!(program.has_parallel());
257 }
258}