codify "platform" into resolution logic, server-fn/fullstack overhaul (#4596)

overhaul dioxus fullstack using a new implementation of server_fn

This PR is a complete rewrite of dioxus fullstack designed to improve compatibility with axum and to fix a number of compilation errors based on feature flag unification plumbing through macros.
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-10-03 18:26:19 -07:00
1 parent 8421c3aef0
commit f2b1c6d97f
424 files changed
+17284 -6624

No files matched your search

+138 -41
View File
@@ -1,59 +1,156 @@
use std::{path::PathBuf, pin::Pin, prelude::rust_2024::Future};
use bytes::Bytes;
use futures_util::Stream;
#[derive(Clone)]
pub struct FileData {
inner: std::sync::Arc<dyn NativeFileData>,
}
impl FileData {
pub fn new(inner: impl NativeFileData + 'static) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
pub fn content_type(&self) -> Option<String> {
self.inner.content_type()
}
pub fn name(&self) -> String {
self.inner.name()
}
pub fn size(&self) -> u64 {
self.inner.size()
}
pub fn last_modified(&self) -> u64 {
self.inner.last_modified()
}
pub async fn read_bytes(&self) -> Result<Bytes, dioxus_core::Error> {
self.inner.read_bytes().await
}
pub async fn read_string(&self) -> Result<String, dioxus_core::Error> {
self.inner.read_string().await
}
pub fn byte_stream(
&self,
) -> Pin<Box<dyn Stream<Item = Result<Bytes, dioxus_core::Error>> + Send + 'static>> {
self.inner.byte_stream()
}
pub fn inner(&self) -> &dyn std::any::Any {
self.inner.inner()
}
pub fn pathbuf(&self) -> PathBuf {
self.inner.path()
}
}
pub trait NativeFileData: Send + Sync {
fn name(&self) -> String;
fn size(&self) -> u64;
fn last_modified(&self) -> u64;
fn path(&self) -> PathBuf;
fn content_type(&self) -> Option<String>;
fn read_bytes(
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::Error>> + 'static>>;
fn byte_stream(
&self,
) -> Pin<Box<dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::Error>> + 'static + Send>>;
fn read_string(
&self,
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::Error>> + 'static>>;
fn inner(&self) -> &dyn std::any::Any;
}
impl std::fmt::Debug for FileData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FileData")
.field("name", &self.inner.name())
.field("size", &self.inner.size())
.field("last_modified", &self.inner.last_modified())
.finish()
}
}
impl std::cmp::PartialEq for FileData {
fn eq(&self, other: &Self) -> bool {
self.inner.name() == other.inner.name()
&& self.inner.size() == other.inner.size()
&& self.inner.last_modified() == other.inner.last_modified()
}
}
pub trait HasFileData: std::any::Any {
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
None
}
fn files(&self) -> Vec<FileData>;
}
/// A serializable representation of file data\
#[cfg(feature = "serialize")]
/// A file engine that serializes files to bytes
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
pub struct SerializedFileEngine {
pub files: std::collections::HashMap<String, Vec<u8>>,
pub struct SerializedFileData {
pub name: String,
pub path: PathBuf,
pub size: u64,
pub last_modified: u64,
pub content_type: Option<String>,
pub contents: bytes::Bytes,
}
#[cfg(feature = "serialize")]
#[async_trait::async_trait(?Send)]
impl FileEngine for SerializedFileEngine {
fn files(&self) -> Vec<String> {
self.files.keys().cloned().collect()
impl NativeFileData for SerializedFileData {
fn name(&self) -> String {
self.name.clone()
}
async fn file_size(&self, file: &str) -> Option<u64> {
let file = self.files.get(file)?;
Some(file.len() as u64)
fn size(&self) -> u64 {
self.size
}
async fn read_file(&self, file: &str) -> Option<Vec<u8>> {
self.files.get(file).cloned()
fn last_modified(&self) -> u64 {
self.last_modified
}
async fn read_file_to_string(&self, file: &str) -> Option<String> {
self.read_file(file)
.await
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
fn read_bytes(
&self,
) -> Pin<Box<dyn Future<Output = Result<Bytes, dioxus_core::Error>> + 'static>> {
let contents = self.contents.clone();
Box::pin(async move { Ok(contents) })
}
async fn get_native_file(&self, file: &str) -> Option<Box<dyn std::any::Any>> {
self.read_file(file)
.await
.map(|val| Box::new(val) as Box<dyn std::any::Any>)
fn read_string(
&self,
) -> Pin<Box<dyn Future<Output = Result<String, dioxus_core::Error>> + 'static>> {
let contents = self.contents.clone();
Box::pin(async move { Ok(String::from_utf8(contents.to_vec())?) })
}
fn byte_stream(
&self,
) -> Pin<Box<dyn futures_util::Stream<Item = Result<Bytes, dioxus_core::Error>> + 'static + Send>>
{
let contents = self.contents.clone();
Box::pin(futures_util::stream::once(async move { Ok(contents) }))
}
fn inner(&self) -> &dyn std::any::Any {
self
}
fn path(&self) -> PathBuf {
self.path.clone()
}
fn content_type(&self) -> Option<String> {
self.content_type.clone()
}
}
#[async_trait::async_trait(?Send)]
pub trait FileEngine {
// get a list of file names
fn files(&self) -> Vec<String>;
// get the size of a file
async fn file_size(&self, file: &str) -> Option<u64>;
// read a file to bytes
async fn read_file(&self, file: &str) -> Option<Vec<u8>>;
// read a file to string
async fn read_file_to_string(&self, file: &str) -> Option<String>;
// returns a file in platform's native representation
async fn get_native_file(&self, file: &str) -> Option<Box<dyn std::any::Any>>;
}