//! Upload 只在 HTTP App 与同领域 Port/Adapter 之间移动,不放宽事务和并发检查。 mod ownership; use super::Context; use crate::compiler::model::*; use crate::syntax::{ast, diagnostic::Diagnostic}; pub(super) use ownership::check; pub(super) fn nested(kind: &Type) -> bool { match kind { Type::Upload => true, Type::Optional(inner) | Type::List(inner) | Type::Stream(inner) | Type::AsyncStream(inner) | Type::Channel(inner) => nested(inner), Type::Map(key, value) | Type::MapEntry(key, value) => nested(key) || nested(value), Type::Outputs(fields) => fields.iter().any(|(_, kind)| nested(kind)), _ => false, } } impl Context<'_> { pub(super) fn upload( &mut self, operation: upload::Operation, arguments: &[ast::Expression], expected: Option<&Type>, source: &ast::Expression, ) -> Result { let role = package::role(&self.definition_names.packages[self.source]); let allowed = if operation == upload::Operation::Put { self.definition_names.packages[self.source] == "dever.storage" } else { matches!(role, Some((_, "app" | "adapter"))) }; if !allowed { return Err(self.error( source, "Upload operations require App; storage.put requires its Adapter", )); } let [argument] = arguments else { return Err(self.error(source, "Upload operation requires one Upload")); }; let value = self.expression(argument, Some(&Type::Upload))?; let value_type = match operation { upload::Operation::Size => Type::Int, upload::Operation::Filename | upload::Operation::ContentType => Type::Text, upload::Operation::Close => Type::Unit, upload::Operation::Put => { let index = self .definition_names .resolve("dever.storage.PutResult", self.source, source.span)? .ok_or_else(|| { self.error(source, "storage.put requires the official PutResult") })?; let valid = matches!(&self.definitions[index].shape, Shape::Choice(variants) if variants.len() == 2 && variants[0].name == "Stored" && !variants[0].error && variants[0].fields == [("key".into(), Type::Uuid)] && variants[1].name == "Failed" && variants[1].error && variants[1].fields == [("message".into(), Type::Text)]); if !valid { return Err(self.error(source, "invalid storage.PutResult contract")); } Type::Named(index) } }; self.coerce( Expression { kind: Node::Upload(operation, vec![value]), value_type, span: source.span, }, expected, ) } }