use bytes::Bytes;
use futures_util::stream::BoxStream;
use primitives_rs::tsgen::{self, TsGen, TypeDef, TypeGraph, TypePhase, TypeRef, TypeRegistry};
use std::error::Error as StdError;
pub use primitives_rs::http::form_data::UploadedFile;
#[doc(hidden)]
pub type FormBody = BoxStream<'static, Result<Bytes, Box<dyn StdError + Send + Sync>>>;
#[derive(Clone, Debug)]
pub struct FormInput<Fields>(pub Fields);
impl<Fields: TsGen> TsGen for FormInput<Fields> {
fn type_ref() -> TypeRef {
Fields::type_ref()
}
fn collect_type_defs(registry: &mut TypeRegistry) -> tsgen::Result<()> {
collect_form_type_defs::<Fields>(TypePhase::Deserialize, registry)
}
fn type_ref_for(phase: TypePhase) -> TypeRef {
Fields::type_ref_for(phase)
}
fn collect_type_defs_for(phase: TypePhase, registry: &mut TypeRegistry) -> tsgen::Result<()> {
collect_form_type_defs::<Fields>(phase, registry)
}
}
fn collect_form_type_defs<Fields: TsGen>(
phase: TypePhase,
registry: &mut TypeRegistry,
) -> tsgen::Result<()> {
let mut collected = TypeRegistry::default();
Fields::collect_type_defs_for(phase, &mut collected)?;
let graph = TypeGraph::new(Fields::type_ref_for(phase), collected.clone())?;
check_form_fields(&graph.root, &graph.type_defs).map_err(tsgen::Error::new)?;
let defs = collected.into_defs();
for def in defs {
registry.try_define(def)?;
}
Ok(())
}
fn resolve_form_type<'a>(mut type_ref: &'a TypeRef, defs: &'a [TypeDef]) -> &'a TypeRef {
for _ in 0..=defs.len() {
let TypeRef::Named { key, .. } = type_ref else {
break;
};
match defs.iter().find(|def| def.key() == key) {
Some(def) => type_ref = def.type_ref(),
None => break,
}
}
type_ref
}
fn check_form_fields(type_ref: &TypeRef, defs: &[TypeDef]) -> Result<(), String> {
let TypeRef::Object(fields) = resolve_form_type(type_ref, defs) else {
return Err(
"form input must be a struct with named fields, without flattened fields".to_owned(),
);
};
for field in fields {
check_form_field(field.type_ref(), true, defs).map_err(|problem| {
format!(
"the form field `{}` is {problem}, which form input cannot read",
field.name()
)
})?;
}
Ok(())
}
fn check_form_field(type_ref: &TypeRef, repeated: bool, defs: &[TypeDef]) -> Result<(), String> {
let described = match resolve_form_type(type_ref, defs) {
TypeRef::Bool
| TypeRef::String
| TypeRef::Number
| TypeRef::Integer
| TypeRef::WideInteger { .. }
| TypeRef::Blob
| TypeRef::StringLiteral(_) => return Ok(()),
TypeRef::Nullable(inner) => return check_form_field(inner, repeated, defs),
TypeRef::Array(inner) if repeated => return check_form_field(inner, false, defs),
TypeRef::Union(members)
if !members.is_empty()
&& members
.iter()
.all(|member| matches!(member, TypeRef::StringLiteral(_))) =>
{
return Ok(());
}
TypeRef::Array(_) => "a sequence inside a sequence",
TypeRef::Object(_) => "a nested struct",
TypeRef::Record(..) => "a map",
TypeRef::Tuple(_) => "a tuple",
TypeRef::Intersection(_) => "a flattened struct",
TypeRef::Union(_) => "an enum with data",
TypeRef::Undefined | TypeRef::Null => "an empty value",
TypeRef::Unknown | TypeRef::Raw(_) => "a value of unknown shape",
TypeRef::FormData => "a form",
TypeRef::Parameter(_) | TypeRef::Documented { .. } => {
unreachable!("TypeGraph contains concrete undocumented expressions")
}
TypeRef::Named { .. } => "a type that the declarations do not define",
};
Err(described.to_owned())
}