use serde::{Deserialize, Serialize};
use yew_server_hook::yewserverhook;
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub enum DataState<G> {
Loading,
Error(String),
Data(G),
Empty,
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
pub struct ApiHook<G> {
pub state: DataState<G>,
pub is_loading: bool,
pub is_updating: bool,
}
#[cfg(feature = "ssr")]
pub mod api {
pub struct AppState;
}
#[cfg(feature = "ssr")]
pub mod apperror {
pub use super::AppError;
}
pub type RecordId = String;
#[derive(Debug)]
#[allow(dead_code)]
pub struct AppError(String);
impl From<&str> for AppError {
fn from(s: &str) -> Self {
AppError(s.to_string())
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct User {
pub id: RecordId,
pub name: Option<String>,
pub email: Option<String>,
}
#[yewserverhook(path = "/api/users")]
pub async fn get_users() -> Result<Vec<User>, AppError> {
let users = vec![
User {
id: "user_001".to_string(),
name: Some("Alice Johnson".to_string()),
email: Some("alice@example.com".to_string()),
},
User {
id: "user_002".to_string(),
name: Some("Bob Smith".to_string()),
email: Some("bob@example.com".to_string()),
},
User {
id: "user_003".to_string(),
name: Some("Charlie Brown".to_string()),
email: None,
},
];
Ok(users)
}
#[yew::function_component]
pub fn GetUsers() -> yew::Html {
let usersapi = use_get_users();
yew::html! {
<div class="container mx-auto p-6">
<h1 class="text-3xl font-bold mb-6">{ "Users List" }</h1>
{ match &usersapi.state {
DataState::Loading => yew::html! {
<div class="text-center p-8">
<p class="text-lg text-gray-600">{ "Loading users..." }</p>
</div>
},
DataState::Error(err) => yew::html! {
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
<p>
<strong>{ "Error: " }</strong>
{ err }
</p>
</div>
},
DataState::Empty => yew::html! {
<div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded">
<p>{ "No users found in the database." }</p>
</div>
},
DataState::Data(data) => yew::html! {
<div class="bg-white shadow-md rounded-lg overflow-hidden">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{ "ID" }
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{ "Name" }
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
{ "Email" }
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
{ data.iter().map(|user| {
yew::html! {
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{&user.id.to_string()}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{user.name.as_ref().unwrap_or(&"N/A".to_string())}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{user.email.as_ref().unwrap_or(&"N/A".to_string())}
</td>
</tr>
}
}).collect::<yew::Html>() }
</tbody>
</table>
</div>
}
}}
</div>
}
}
#[yewserverhook(path = "/api/user")]
pub async fn get_user_by_id(id: String) -> Result<User, AppError> {
let users = vec![
User {
id: "user_001".to_string(),
name: Some("Alice Johnson".to_string()),
email: Some("alice@example.com".to_string()),
},
User {
id: "user_002".to_string(),
name: Some("Bob Smith".to_string()),
email: Some("bob@example.com".to_string()),
},
User {
id: "user_003".to_string(),
name: Some("Charlie Brown".to_string()),
email: None,
},
];
users
.into_iter()
.find(|u| u.id == id)
.ok_or_else(|| AppError(format!("User with id {} not found", id)))
}
#[derive(yew::Properties, PartialEq)]
pub struct UserDetailsProps {
pub user_id: String,
}
#[yew::function_component]
pub fn UserDetails(props: &UserDetailsProps) -> yew::Html {
let user_id = props.user_id.clone();
let user_api = use_get_user_by_id(user_id.clone());
let onclick = {
let user_id = user_id.clone();
yew::Callback::from(move |_| {
let user_id = user_id.clone();
wasm_bindgen_futures::spawn_local(async move {
match get_user_by_id(user_id).await {
Ok(_user) => {
}
Err(_err) => {
}
}
});
})
};
yew::html! {
<div class="container mx-auto p-6">
<button {onclick} class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
{"Get User by ID"}
</button>
{ match &user_api.state {
DataState::Loading => yew::html! {
<div class="text-center p-8">
<p class="text-lg text-gray-600">{ "Loading user..." }</p>
</div>
},
DataState::Error(err) => yew::html! {
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded">
<p>
<strong>{ "Error: " }</strong>
{ err }
</p>
</div>
},
DataState::Empty => yew::html! {
<div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded">
<p>{ "User not found." }</p>
</div>
},
DataState::Data(user) => yew::html! {
<div class="bg-white shadow-md rounded-lg p-6">
<h2 class="text-2xl font-bold mb-4">{ "User Details" }</h2>
<div class="space-y-2">
<p>
<strong>{ "ID: " }</strong>
{ &user.id }
</p>
<p>
<strong>{ "Name: " }</strong>
{ user.name.as_ref().unwrap_or(&"N/A".to_string()) }
</p>
<p>
<strong>{ "Email: " }</strong>
{ user.email.as_ref().unwrap_or(&"N/A".to_string()) }
</p>
</div>
</div>
}
}}
</div>
}
}
fn main() {
println!("This is just an example demonstrating the macro usage");
println!("The macro generates:");
println!("1. Server-side handler function for Axum");
println!("2. Client-side Yew hook (use_users)");
println!("3. Wrapper function (get_users) that calls the hook");
println!("4. DataState and ApiHook types for state management");
}