81 lines
3.0 KiB
Rust
81 lines
3.0 KiB
Rust
//! 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<Expression, Diagnostic> {
|
|
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,
|
|
)
|
|
}
|
|
}
|