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//! For the current formal-verification claim, only the shipped first-party
12//! handler/transport implementations with documented contract profiles are
13//! inside the first-party runtime boundary. User-supplied third-party handlers
14//! and transports remain outside that claim unless they separately satisfy the
15//! same contract.
16
17#![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
34// Re-export utility-layer support types
35pub use util::{SystemClock, SystemRng};
36
37// Re-export typed identifiers
38pub use identifiers::{Endpoint as TopologyEndpoint, Namespace, Region, RoleName};
39
40// Re-export main APIs
41pub 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
76// Re-export heap types for resource management, canonical encoding, and
77// vector-stable hashing helpers.
78pub 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
85// Re-export testing types for protocol testing
86pub use testing::{
87    AsyncClock, BlockedOn, Checkpoint, Clock, InMemoryTransport, MockClock, NullObserver,
88    ProtocolEnvelope, ProtocolObserver, ProtocolStateMachine, RecordingObserver, Rng, SeededRng,
89    SimulatedTransport, StepInput, StepOutput, WallClock,
90};
91
92// Re-export macros from telltale-macros
93pub use telltale_macros::tell;
94pub use telltale_types::{ChannelCapacity, MessageLenBytes, QueueCapacity};
95
96/// Unstable low-level extension integration surfaces.
97///
98/// These items are exposed for advanced integrations and may evolve faster than
99/// the stable root-level API.
100#[doc(hidden)]
101pub mod unstable {
102    pub use crate::extensions::{CodegenContext, ParseContext, ProjectionContext, StatementParser};
103}
104
105// High-level API functions for extension-aware compilation
106
107/// Parse and generate choreography code with extension support
108pub 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    // Validate the choreography
121    choreography
122        .validate()
123        .map_err(|e| CompilationError::Validation(e.to_string()))?;
124
125    // Project to local types
126    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    // Generate code with extensions
134    let generated_code =
135        generate_choreography_code_with_extensions(&choreography, &local_types, &extensions);
136
137    Ok(generated_code)
138}
139
140/// Convenience function for compiling choreography with built-in extensions
141pub 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, &registry)
146}
147
148/// Parse choreography with extension support
149pub 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/// Compilation errors that can occur during choreography processing
160#[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    // Simple test role type for unit tests
182    #[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        // Test that main re-exports are available
223        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        // Test effect system is available
229        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        // Test that Program can be built using the free algebra API
239        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        // Basic analysis should work
253        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}