Skip to main content

telltale_runtime/
lib.rs

1//! Choreographic Programming for Telltale
2//!
3//! This crate provides a choreographic programming layer on top of Telltale's
4//! session types, enabling global protocol specification with automatic projection.
5//!
6//! The choreographic approach allows you to write distributed protocols from a
7//! global viewpoint, with automatic generation of local session types for each
8//! participant. This includes an effect handler system that decouples protocol
9//! logic from transport implementation.
10
11#![allow(
12    clippy::missing_errors_doc,
13    clippy::missing_panics_doc,
14    clippy::must_use_candidate
15)]
16
17pub mod ast;
18pub mod compiler;
19pub mod effects;
20pub mod extensions;
21pub mod heap;
22pub mod identifiers;
23pub mod runtime;
24pub mod testing;
25pub mod topology;
26pub mod tracing;
27
28// Re-export runtime support types
29pub use runtime::{SystemClock, SystemRng};
30
31// Re-export typed identifiers
32pub use identifiers::{Endpoint as TopologyEndpoint, Namespace, Region, RoleName};
33
34// Re-export main APIs
35pub 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::{TelltaleEndpoint, TelltaleHandler, TelltaleSession};
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,
58    RoleFamilyConstraint, RoleFamilyConstraintError, Topology, TopologyBuilder, TopologyConstraint,
59    TopologyError, TopologyHandler, TopologyHandlerBuilder, TopologyLoadError, TopologyMode,
60    TopologyParseError, TopologyValidation, Transport, TransportError, TransportFactory,
61    TransportMessage, TransportResult, TransportType,
62};
63
64// Re-export heap types for resource management
65pub use heap::{
66    ChannelState, Direction, Heap, HeapCommitment, HeapError, MerkleProof, MerkleTree,
67    Message as HeapMessage, MessagePayload, ProofStep, Resource, ResourceId,
68};
69
70// Re-export testing types for protocol testing
71pub use testing::{
72    AsyncClock, BlockedOn, Checkpoint, Clock, InMemoryTransport, MockClock, NullObserver,
73    ProtocolEnvelope, ProtocolObserver, ProtocolStateMachine, RecordingObserver, Rng, SeededRng,
74    SimulatedTransport, StepInput, StepOutput, WallClock,
75};
76
77// Re-export macros from telltale-macros
78pub use telltale_macros::tell;
79pub use telltale_types::{ChannelCapacity, MessageLenBytes, QueueCapacity};
80
81/// Unstable low-level extension integration surfaces.
82///
83/// These items are exposed for advanced integrations and may evolve faster than
84/// the stable root-level API.
85#[doc(hidden)]
86pub mod unstable {
87    pub use crate::extensions::{CodegenContext, ParseContext, ProjectionContext, StatementParser};
88}
89
90// High-level API functions for extension-aware compilation
91
92/// Parse and generate choreography code with extension support
93pub fn parse_and_generate_with_extensions(
94    input: &str,
95    extension_registry: &ExtensionRegistry,
96) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
97    use compiler::codegen::generate_choreography_code_with_extensions;
98    use compiler::parser::parse_choreography_str_with_extensions;
99    use compiler::projection::project;
100
101    let (choreography, extensions) =
102        parse_choreography_str_with_extensions(input, extension_registry)
103            .map_err(CompilationError::Parse)?;
104
105    // Validate the choreography
106    choreography
107        .validate()
108        .map_err(|e| CompilationError::Validation(e.to_string()))?;
109
110    // Project to local types
111    let mut local_types = Vec::new();
112    for role in &choreography.roles {
113        let local_type = project(&choreography, role)
114            .map_err(|e| CompilationError::Projection(e.to_string()))?;
115        local_types.push((role.clone(), local_type));
116    }
117
118    // Generate code with extensions
119    let generated_code =
120        generate_choreography_code_with_extensions(&choreography, &local_types, &extensions);
121
122    Ok(generated_code)
123}
124
125/// Convenience function for compiling choreography with built-in extensions
126pub fn compile_choreography_with_extensions(
127    input: &str,
128) -> std::result::Result<proc_macro2::TokenStream, CompilationError> {
129    let registry = ExtensionRegistry::with_builtin_extensions();
130    parse_and_generate_with_extensions(input, &registry)
131}
132
133/// Parse choreography with extension support
134pub fn parse_choreography_with_extensions(
135    input: &str,
136    extension_registry: &ExtensionRegistry,
137) -> std::result::Result<(Choreography, Vec<Box<dyn ProtocolExtension>>), CompilationError> {
138    use compiler::parser::parse_choreography_str_with_extensions;
139
140    parse_choreography_str_with_extensions(input, extension_registry)
141        .map_err(CompilationError::Parse)
142}
143
144/// Compilation errors that can occur during choreography processing
145#[derive(Debug, thiserror::Error)]
146pub enum CompilationError {
147    #[error("parse error: {0}")]
148    Parse(#[from] compiler::parser::ParseError),
149
150    #[error("validation error: {0}")]
151    Validation(String),
152
153    #[error("projection error: {0}")]
154    Projection(String),
155
156    #[error("code generation error: {0}")]
157    Codegen(String),
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163    use crate::effects::{LabelId, RoleId};
164    use crate::identifiers::RoleName;
165
166    // Simple test role type for unit tests
167    #[allow(dead_code)]
168    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
169    enum TestRole {
170        Alice,
171        Bob,
172    }
173
174    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
175    enum TestLabel {
176        Test,
177    }
178
179    impl LabelId for TestLabel {
180        fn as_str(&self) -> &'static str {
181            match self {
182                TestLabel::Test => "test",
183            }
184        }
185
186        fn from_str(label: &str) -> Option<Self> {
187            match label {
188                "test" => Some(TestLabel::Test),
189                _ => None,
190            }
191        }
192    }
193
194    impl RoleId for TestRole {
195        type Label = TestLabel;
196
197        fn role_name(&self) -> RoleName {
198            match self {
199                TestRole::Alice => RoleName::from_static("Alice"),
200                TestRole::Bob => RoleName::from_static("Bob"),
201            }
202        }
203    }
204
205    #[test]
206    fn test_module_structure() {
207        // Test that main re-exports are available
208        let _choreography: Option<Choreography> = None;
209        let _protocol: Option<Protocol> = None;
210        let _role: Option<Role> = None;
211        let _message_type: Option<MessageType> = None;
212
213        // Test effect system is available
214        let _program: Option<Program<TestRole, String>> = None;
215        let _result: Option<ChoreoResult<()>> = None;
216        let _label: Option<TestLabel> = None;
217    }
218
219    #[test]
220    fn test_free_algebra_integration() {
221        use std::time::Duration;
222
223        // Test that Program can be built using the free algebra API
224        let program = Program::<TestRole, String>::new()
225            .send(TestRole::Bob, "hello".to_string())
226            .recv::<String>(TestRole::Bob)
227            .choose(TestRole::Bob, TestLabel::Test)
228            .offer(TestRole::Bob)
229            .with_timeout(
230                TestRole::Bob,
231                Duration::from_millis(100),
232                Program::new().end(),
233            )
234            .parallel(vec![Program::new().end()])
235            .end();
236
237        // Basic analysis should work
238        assert_eq!(program.send_count(), 1);
239        assert_eq!(program.recv_count(), 1);
240        assert!(program.has_timeouts());
241        assert!(program.has_parallel());
242    }
243}