Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/server macro defaults #3403

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
7 changes: 7 additions & 0 deletions packages/cli/src/config/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ pub(crate) struct ApplicationConfig {

#[serde(default)]
pub(crate) sub_package: Option<String>,

#[serde(default = "out_dir_default")]
pub(crate) out_dir: PathBuf,
}

pub(crate) fn asset_dir_default() -> PathBuf {
PathBuf::from("assets")
}

pub(crate) fn out_dir_default() -> PathBuf {
PathBuf::from("dist")
}
1 change: 1 addition & 0 deletions packages/cli/src/config/dioxus_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl Default for DioxusConfig {
application: ApplicationConfig {
asset_dir: asset_dir_default(),
sub_package: None,
out_dir: out_dir_default(),
},
web: WebConfig {
app: WebAppConfig {
Expand Down
18 changes: 15 additions & 3 deletions packages/cli/src/dioxus_crate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,21 @@ impl DioxusCrate {
/// is "distributed" after building an application (configurable in the
/// `Dioxus.toml`).
fn out_dir(&self) -> PathBuf {
let dir = self.workspace_dir().join("target").join("dx");
std::fs::create_dir_all(&dir).unwrap();
dir
// Resolve the out_dir based on the configuration or default
let out_dir_config = self.config.application.out_dir.clone();
// Resolve relative paths against the workspace root
let resolved_out_dir = if out_dir_config.is_relative() {
self.workspace_dir().join(out_dir_config)
} else {
out_dir_config
};

// Create the directory and handle potential errors
if let Err(e) = std::fs::create_dir_all(&resolved_out_dir) {
tracing::error!("Failed to create output directory: {}", e);
}

resolved_out_dir
}

/// Create a workdir for the given platform
Expand Down
16 changes: 9 additions & 7 deletions packages/cli/src/wasm_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,14 @@ impl WasmBindgen {
args.push(&self.out_name);

// Out dir
let out_dir = self
let canonical_out_dir = self
.out_dir
.canonicalize()
.expect("out_dir should resolve to a valid path");

let out_dir = canonical_out_dir
.to_str()
.expect("input_path should be valid utf8");
.expect("out_dir should be valid UTF-8");

args.push("--out-dir");
args.push(out_dir);
Expand Down Expand Up @@ -342,11 +346,9 @@ impl WasmBindgenBuilder {
}
}

pub fn out_dir(self, out_dir: &Path) -> Self {
Self {
out_dir: out_dir.to_path_buf(),
..self
}
pub fn out_dir(mut self, out_dir: &Path) -> Self {
self.out_dir = out_dir.canonicalize().expect("Invalid out_dir path");
self
}

pub fn out_name(self, out_name: &str) -> Self {
Expand Down
108 changes: 93 additions & 15 deletions packages/server-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,86 @@ use syn::__private::ToTokens;
///
/// ## Named Arguments
///
/// You can any combination of the following named arguments:
/// You can use any combination of the following named arguments:
/// - `name`: sets the identifier for the server function’s type, which is a struct created
/// to hold the arguments (defaults to the function identifier in PascalCase)
/// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`)
/// to hold the arguments (defaults to the function identifier in PascalCase).
/// Example: `name = MyServerFunction`.
/// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`).
/// Example: `prefix = "/my_api"`.
/// - `endpoint`: specifies the exact path at which the server function handler will be mounted,
/// relative to the prefix (defaults to the function name followed by unique hash)
/// - `input`: the encoding for the arguments (defaults to `PostUrl`)
/// - `output`: the encoding for the response (defaults to `Json`)
/// - `client`: a custom `Client` implementation that will be used for this server fn
/// relative to the prefix (defaults to the function name followed by unique hash).
/// Example: `endpoint = "my_fn"`.
/// - `input`: the encoding for the arguments (defaults to `PostUrl`).
/// - The `input` argument specifies how the function arguments are encoded for transmission.
/// - Acceptable values include:
/// - `PostUrl`: A `POST` request with URL-encoded arguments, suitable for form-like submissions.
/// - `Json`: A `POST` request where the arguments are encoded as JSON. This is a common choice for modern APIs.
/// - `Cbor`: A `POST` request with CBOR-encoded arguments, useful for binary data transmission with compact encoding.
/// - `GetUrl`: A `GET` request with URL-encoded arguments, suitable for simple queries or when data fits in the URL.
/// - `GetCbor`: A `GET` request with CBOR-encoded arguments, useful for query-style APIs when the payload is binary.
/// - `output`: the encoding for the response (defaults to `Json`).
/// - The `output` argument specifies how the server should encode the response data.
/// - Acceptable values include:
/// - `Json`: A response encoded as JSON (default). This is ideal for most web applications.
/// - `Cbor`: A response encoded in the CBOR format for efficient, binary-encoded data.
/// - `client`: a custom `Client` implementation that will be used for this server function. This allows
/// customization of the client-side behavior if needed.
/// - `encoding`: (legacy, may be deprecated in future) specifies the encoding, which may be one
/// of the following (not case sensitive)
/// of the following (not case sensitive):
/// - `"Url"`: `POST` request with URL-encoded arguments and JSON response
/// - `"GetUrl"`: `GET` request with URL-encoded arguments and JSON response
/// - `"Cbor"`: `POST` request with CBOR-encoded arguments and response
/// - `"GetCbor"`: `GET` request with URL-encoded arguments and CBOR response
/// - `req` and `res` specify the HTTP request and response types to be used on the server (these
/// should usually only be necessary if you are integrating with a server other than Actix/Axum)
/// - `req` and `res`: specify the HTTP request and response types to be used on the server. These
/// are typically necessary if you are integrating with a custom server framework (other than Actix/Axum).
/// Example: `req = SomeRequestType`, `res = SomeResponseType`.
///
///
/// ## Advanced Usage of `input` and `output` Fields
///
/// The `input` and `output` fields allow you to customize how arguments and responses are encoded and decoded.
/// These fields impose specific trait bounds on the types you use. Here are detailed examples for different scenarios:
///
/// ### `output = StreamingJson`
///
/// Setting the `output` type to `StreamingJson` requires the return type to implement `From<JsonStream<T>>`,
/// where `T` implements `serde::Serialize` and `serde::de::DeserializeOwned`.
///
/// ```rust,ignore
/// #[server(output = StreamingJson)]
/// pub async fn json_stream_fn() -> Result<JsonStream<String>, ServerFnError> {
/// todo!()
/// }
/// ```
///
/// ### `output = StreamingText`
///
/// Setting the `output` type to `StreamingText` requires the return type to implement `From<TextStream>`.
///
/// ```rust,ignore
/// #[server(output = StreamingText)]
/// pub async fn text_stream_fn() -> Result<TextStream, ServerFnError> {
/// todo!()
/// }
/// ```
///
/// ### `output = PostUrl`
///
/// Setting the `output` type to `PostUrl` requires the return type to implement `Serialize` and `Deserialize`.
/// Note that this uses `serde_qs`, which imposes the following constraints:
/// - The structure must be less than 5 levels deep.
/// - The structure must not contain any `serde(flatten)` attributes.
///
/// ```rust,ignore
/// #[server(output = PostUrl)]
/// pub async fn form_fn() -> Result<TextStream, ServerFnError> {
/// todo!()
/// }
/// ```
///
/// These examples illustrate how the `output` type impacts the bounds and expectations for your server function. Ensure your return types comply with these requirements.
///
///
/// ```rust,ignore
/// #[server(
/// name = SomeStructName,
Expand Down Expand Up @@ -143,15 +206,30 @@ use syn::__private::ToTokens;
/// ```
#[proc_macro_attribute]
pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
let default_prefix: &str = "/api";
let default_input: Option<syn::Path> = Some(syn::parse_quote!(server_fn));
let default_output: Option<syn::Type> = None;
let default_preset: Option<syn::Type> = None;

match server_macro_impl(
args.into(),
s.into(),
Some(syn::parse_quote!(server_fn)),
"/api",
None,
None,
default_input,
default_prefix,
default_output,
default_preset,
) {
Err(e) => e.to_compile_error().into(),
// Generate detailed error message with context when macro fails.
Err(e) => {
let detailed_error = format!(
"Failed to process the `server` macro. Check your arguments and function signature. Error: {}",
e
);
syn::Error::new(proc_macro2::Span::call_site(), detailed_error)
.to_compile_error()
.into()
}
// Successful case: return the generated token stream.
Ok(s) => s.to_token_stream().into(),
}
}