Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion packages/fullstack/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ dioxus-cli-config = { workspace = true, optional = true }
dioxus-devtools = { workspace = true, optional = true }
aws-lc-rs = { version = "1.13.1", optional = true }
dioxus-history = { workspace = true }
http.workspace = true

[dev-dependencies]
dioxus = { workspace = true, features = ["fullstack"] }
Expand All @@ -82,7 +83,7 @@ devtools = ["dioxus-web?/devtools", "dep:dioxus-devtools"]
mounted = ["dioxus-web?/mounted"]
file_engine = ["dioxus-web?/file_engine"]
document = ["dioxus-web?/document"]
web = ["dep:dioxus-web", "dep:web-sys", "dioxus-fullstack-hooks/web", "server_fn/browser"]
web = ["dep:dioxus-web", "dep:web-sys", "dioxus-fullstack-hooks/web", "server_fn/browser", "dioxus_server_macro/browser"]
desktop = ["server_fn/reqwest", "dioxus_server_macro/reqwest"]
mobile = ["server_fn/reqwest", "dioxus_server_macro/reqwest"]
default-tls = ["server_fn/default-tls"]
Expand Down
4 changes: 2 additions & 2 deletions packages/fullstack/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,8 @@ impl<T: FromStr> FromStr for ServerFnError<T> {
impl<T: Display> Display for ServerFnError<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ServerFnError::ServerError(err) => write!(f, "Server error: {}", err),
ServerFnError::CommunicationError(err) => write!(f, "Communication error: {}", err),
ServerFnError::ServerError(err) => write!(f, "Server error: {err}"),
ServerFnError::CommunicationError(err) => write!(f, "Communication error: {err}"),
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions packages/fullstack/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,27 @@
mod web;

mod error;
#[doc(hidden)]
pub mod mock_client;

#[cfg(all(feature = "web", feature = "document"))]
pub use web::FullstackWebDocument;

pub use dioxus_fullstack_hooks::history::FullstackHistory;

pub use crate::error::{ServerFnError, ServerFnResult};
#[doc(inline)]
pub use dioxus_fullstack_hooks::*;
#[cfg(feature = "server")]
#[doc(inline)]
pub use dioxus_server::*;
#[doc(inline)]
pub use dioxus_server_macro::*;
pub use server_fn::ServerFn as _;
#[doc(inline)]
pub use server_fn::{
self, client,
client::{get_server_url, set_server_url},
codec, server, BoxedStream, ContentType, Decodes, Encodes, Format, FormatType, ServerFn as _,
ServerFn, Websocket,
codec, server, BoxedStream, ContentType, Decodes, Encodes, Format, FormatType, ServerFn,
Websocket,
};
153 changes: 153 additions & 0 deletions packages/fullstack/src/mock_client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
//! A mock [`server_fn::client::Client`] implementation used when no client feature is enabled.

use std::future::Future;

use futures_util::Stream;
use server_fn::{request::ClientReq, response::ClientRes};

/// A placeholder [`server_fn::client::Client`] used when no client feature is enabled. The
/// [`server_fn::client::browser::BrowserClient`] is used on web clients, and [`server_fn::client::reqwest::ReqwestClient`]
/// is used on native clients
#[non_exhaustive]
pub struct MockServerFnClient {}

impl Default for MockServerFnClient {
fn default() -> Self {
Self::new()
}
}

impl MockServerFnClient {
/// Create a new mock server function client
pub fn new() -> Self {
Self {}
}
}

impl<Error, InputStreamError, OutputStreamError>
server_fn::client::Client<Error, InputStreamError, OutputStreamError> for MockServerFnClient
{
type Request = MockServerFnClientRequest;

type Response = MockServerFnClientResponse;

async fn send(_: Self::Request) -> Result<Self::Response, Error> {
unimplemented!()
}

#[allow(unreachable_code)]
async fn open_websocket(
_: &str,
) -> Result<
(
impl Stream<Item = Result<server_fn::Bytes, server_fn::Bytes>> + std::marker::Send + 'static,
impl futures_util::Sink<server_fn::Bytes> + std::marker::Send + 'static,
),
Error,
> {
unimplemented!()
as Result<
(
futures_util::stream::Once<futures_util::future::Pending<_>>,
futures_util::sink::Drain<server_fn::Bytes>,
),
_,
>
}

fn spawn(_: impl Future<Output = ()> + Send + 'static) {
unimplemented!()
}
}

/// A placeholder [`ClientReq`] used when no client feature is enabled.
#[non_exhaustive]
pub struct MockServerFnClientRequest {}

impl<E> ClientReq<E> for MockServerFnClientRequest {
type FormData = ();

fn try_new_req_query(_: &str, _: &str, _: &str, _: &str, _: http::Method) -> Result<Self, E> {
unimplemented!()
}

fn try_new_req_text(_: &str, _: &str, _: &str, _: String, _: http::Method) -> Result<Self, E> {
unimplemented!()
}

fn try_new_req_bytes(
_: &str,
_: &str,
_: &str,
_: bytes::Bytes,
_: http::Method,
) -> Result<Self, E> {
unimplemented!()
}

fn try_new_req_form_data(
_: &str,
_: &str,
_: &str,
_: Self::FormData,
_: http::Method,
) -> Result<Self, E> {
unimplemented!()
}

fn try_new_req_multipart(
_: &str,
_: &str,
_: Self::FormData,
_: http::Method,
) -> Result<Self, E> {
unimplemented!()
}

fn try_new_req_streaming(
_: &str,
_: &str,
_: &str,
_: impl Stream<Item = bytes::Bytes> + Send + 'static,
_: http::Method,
) -> Result<Self, E> {
unimplemented!()
}
}

/// A placeholder [`ClientRes`] used when no client feature is enabled.
pub struct MockServerFnClientResponse;

impl<E> ClientRes<E> for MockServerFnClientResponse {
async fn try_into_string(self) -> Result<String, E> {
unimplemented!()
}

async fn try_into_bytes(self) -> Result<bytes::Bytes, E> {
unimplemented!()
}

#[allow(unreachable_code)]
fn try_into_stream(
self,
) -> Result<impl Stream<Item = Result<bytes::Bytes, bytes::Bytes>> + Send + Sync + 'static, E>
{
unimplemented!() as Result<futures_util::stream::Once<futures_util::future::Pending<_>>, _>
}

fn status(&self) -> u16 {
unimplemented!()
}

fn status_text(&self) -> String {
unimplemented!()
}

fn location(&self) -> String {
unimplemented!()
}

fn has_redirect(&self) -> bool {
unimplemented!()
}
}
1 change: 1 addition & 0 deletions packages/server-macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ proc-macro = true
[features]
axum = ["server_fn_macro/axum"]
server = ["server_fn_macro/ssr"]
browser = []
reqwest = ["server_fn_macro/reqwest"]
generic = ["server_fn_macro/generic"]

Expand Down
14 changes: 13 additions & 1 deletion packages/server-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,23 @@ use syn::{__private::ToTokens, parse_quote};
#[proc_macro_attribute]
pub fn server(args: proc_macro::TokenStream, body: TokenStream) -> TokenStream {
// If there is no input codec, use json as the default
let parsed = match ServerFnCall::parse("/api", args.into(), body.into()) {
#[allow(unused_mut)]
let mut parsed = match ServerFnCall::parse("/api", args.into(), body.into()) {
Ok(parsed) => parsed,
Err(e) => return e.to_compile_error().into(),
};

#[cfg(not(any(feature = "browser", feature = "reqwest")))]
{
let client = &mut parsed.get_args_mut().client;
// If no client is enabled, use the mock client
if client.is_none() {
*client = Some(parse_quote!(
dioxus::fullstack::mock_client::MockServerFnClient
));
}
}

parsed
.default_protocol(Some(
parse_quote!(server_fn::Http<server_fn::codec::Json, server_fn::codec::Json>),
Expand Down
Loading