use crate::hir::{Expression, HandlerTarget}; use crate::specialize::{self, Specialization}; use crate::types::{Parameter, Shape, Type}; use super::{DatabaseContext, Emitter}; #[derive(Clone, Copy)] enum Direction { ToRuntime, ToLanguage, } impl Emitter<'_> { pub(super) fn live_network( &self, operation: crate::intrinsic::Intrinsic, arguments: &[Expression], result: &Expression, ) -> String { use crate::intrinsic::Intrinsic::*; let input = |index: usize| { self.network_value( &format!("a{index}"), &arguments[index].ty, Direction::ToRuntime, ) }; let call = match operation { TlsClient => "dever_runtime::tls::client(&a0)".into(), TlsServer => "dever_runtime::tls::server(&a0, &a1)".into(), HttpClientNew => format!( "dever_runtime::http::client(&a0, a1, {}, {}).await", input(2), input(3) ), HttpCloseClient => "dever_runtime::http::close_client(&a0).await".into(), HttpRequest | HttpOpen | HttpOpenStream => { let method = match operation { HttpRequest => "request", HttpOpen => "open", _ => "open_stream", }; return self.http_result( &format!("dever_runtime::http::{method}(&a0, {}).await", input(1)), result, ); } HttpRespond => format!("dever_runtime::http::respond(&a0, {}).await", input(1)), HttpStart => format!("dever_runtime::http::start(&a0, a1, {}).await", input(2)), HttpWrite => "dever_runtime::http::write(&a0, a1).await".into(), HttpFinish => "dever_runtime::http::finish(&a0).await".into(), SseStart => format!("dever_runtime::sse::start(&a0, {}).await", input(1)), SseSend => format!("dever_runtime::sse::send(&a0, {}).await", input(1)), WsAccept => format!("dever_runtime::websocket::accept(&a0, {}).await", input(1)), WsConnect => format!( "dever_runtime::websocket::connect(&a0, a1, &a2, {}).await", input(3) ), WsOpen => format!( "dever_runtime::websocket::open(&a0, a1, {}).await", input(2) ), WsSend => format!("dever_runtime::websocket::send(&a0, {}).await", input(1)), WsClose => "dever_runtime::websocket::close(&a0, a1, &a2).await".into(), WsReceive | WsMessages => { let event = match &result.ty { Type::AsyncStream(event) => event.as_ref(), event => event, }; let Type::Named(id) = event else { unreachable!() }; let Shape::Choice(variants) = &self.program.types[*id].shape else { unreachable!() }; let read = variants .iter() .find(|variant| variant.name == "Read") .unwrap(); let message = self.network_value("message", &read.fields[0].ty, Direction::ToLanguage); if matches!(operation, WsMessages) { let event = self.resource_result( &format!("event.map(|message| {message})"), event, "Read", false, ); return format!("dever_runtime::websocket::messages(a0).map(|event| {event})"); } return self.resource_result(&format!( "dever_runtime::websocket::receive(&a0).await.map(|message| message.map(|message| {message}))" ), event, "Read", true); } _ => unreachable!("live network intrinsic"), }; let success = match operation { TlsClient | TlsServer | HttpClientNew => "Ready", WsAccept | WsConnect | WsOpen => "Connected", HttpFinish | WsClose | HttpCloseClient => "Closed", _ => "Done", }; self.resource_result(&call, &result.ty, success, false) } pub(super) fn http_serve( &self, handler: HandlerTarget, arguments: &[Expression], result: &Expression, live: bool, tls: bool, ) -> String { let target = Specialization { function: specialize::resolve_handler(handler, &self.bindings), handlers: Vec::new(), }; let wait = if specialize::suspends(self.program, &target) { ".await" } else { "" }; let function = &self.program.functions[target.function]; let Parameter::Value(request) = &function.parameters[0] else { unreachable!("checked HTTP request") }; let request = self.network_value("request", request, Direction::ToLanguage); let tls_index = if live { 3 } else { 2 }; let context_index = tls_index + usize::from(tls); let tls_argument = if tls { format!(", a{tls_index}") } else { String::new() }; let suffix = if tls { "_tls" } else { "" }; let (capture, context) = if arguments.len() > context_index { ( format!("let context = a{context_index}.clone();"), ", context", ) } else { (String::new(), "") }; if live { let limits = self.network_value("a1", &arguments[1].ty, Direction::ToRuntime); let live_limits = self.network_value("a2", &arguments[2].ty, Direction::ToRuntime); let invoke = self.invoke( &target, format!("{request}, reply{context}"), DatabaseContext::Detached, ); return self.failure(&format!( "dever_runtime::http::serve_live{suffix}(move |request, reply| {{ {capture} async move {{ {invoke}{wait}.map_err(|error| error.to_string()) }} }}, a0, {limits}, {live_limits}{tls_argument}).await", ), result.span); } let response = self.network_value("response", &function.outputs[0].ty, Direction::ToRuntime); let limits = self.network_value("a1", &arguments[1].ty, Direction::ToRuntime); let invoke = self.invoke( &target, format!("{request}{context}"), DatabaseContext::Detached, ); self.failure(&format!( "dever_runtime::http::serve{suffix}(move |request| {{ {capture} async move {{ let response = {invoke}{wait}.map_err(|error| error.to_string())?; Ok({response}) }} }}, a0, {limits}{tls_argument}).await", ), result.span) } pub(super) fn http_send(&self, arguments: &[Expression], result: &Expression) -> String { let request = self.network_value("a2", &arguments[2].ty, Direction::ToRuntime); let limits = self.network_value("a3", &arguments[3].ty, Direction::ToRuntime); self.http_result( &format!("dever_runtime::http::send(&a0, a1, {request}, {limits}).await"), result, ) } fn http_result(&self, call: &str, result: &Expression) -> String { let Type::Named(id) = &result.ty else { unreachable!("checked HTTP result") }; let Shape::Choice(variants) = &self.program.types[*id].shape else { unreachable!() }; let read = variants .iter() .find(|variant| variant.name == "Read") .expect("checked Read variant"); let response = self.network_value("response", &read.fields[0].ty, Direction::ToLanguage); self.resource_result( &format!("{call}.map(|response| {response})"), &result.ty, "Read", false, ) } // 校验器固定报文记录的字段契约;桥接按字段名生成,源码重排字段不会错位。 fn network_value(&self, value: &str, ty: &Type, direction: Direction) -> String { match ty { Type::Named(id) => { let definition = &self.program.types[*id]; let Shape::Record(fields) = &definition.shape else { let Shape::Choice(variants) = &definition.shape else { unreachable!() }; let arms = variants .iter() .enumerate() .map(|(index, variant)| { let runtime = format!( "dever_runtime::{}::{}", definition .name .strip_prefix("dever.") .unwrap() .replace('.', "::"), variant.name ); let language = format!("T{id}::V{index}"); let payload = self.network_value("payload", &variant.fields[0].ty, direction); let (source, target) = match direction { Direction::ToRuntime => (language, runtime), Direction::ToLanguage => (runtime, language), }; format!("{source}(payload) => {target}({payload})") }) .collect::>() .join(", "); return format!("match {value} {{ {arms} }}"); }; let name = match direction { Direction::ToRuntime => format!( "dever_runtime::{}", definition .name .strip_prefix("dever.") .expect("official record") .replace('.', "::"), ), Direction::ToLanguage => format!("T{id}"), }; let fields = fields .iter() .enumerate() .map(|(index, field)| { let (source, target) = match direction { Direction::ToRuntime => { (format!("record.f{index}"), field.name.clone()) } Direction::ToLanguage => { (format!("record.{}", field.name), format!("f{index}")) } }; format!( "{target}: {}", self.network_value(&source, &field.ty, direction) ) }) .collect::>() .join(", "); format!("{{ let record = {value}; {name} {{ {fields} }} }}") } Type::List(element) => { let element = self.network_value("header", element, direction); match direction { Direction::ToRuntime => { format!("{value}.into_values().map(|header| {element}).collect()") } Direction::ToLanguage => { format!("List::new({value}.into_iter().map(|header| {element}).collect())") } } } Type::Nullable(element) => { let element = self.network_value("element", element, direction); format!("{value}.map(|element| {element})") } Type::AsyncStream(event) => { let conversion = match direction { Direction::ToLanguage => self.resource_result("event", event, "Chunk", false), Direction::ToRuntime => { let Type::Named(id) = event.as_ref() else { unreachable!() }; let Shape::Choice(variants) = &self.program.types[*id].shape else { unreachable!() }; let arms = variants .iter() .enumerate() .map(|(index, variant)| { let constructor = if variant.name == "Chunk" { "Ok" } else { "Err" }; format!("T{id}::V{index}(payload) => {constructor}(payload)") }) .collect::>() .join(", "); format!("match event {{ {arms} }}") } }; format!("{value}.map(|event| {conversion})") } Type::Text | Type::Bytes | Type::Int | Type::ClientTls | Type::ServerTls | Type::HttpClient => value.into(), _ => unreachable!("checked HTTP field type"), } } }