use typeway_core::*;
use typeway_macros::*;
use typeway_server::*;
#[derive(Clone)]
struct AuthUser {
username: String,
role: String,
}
impl FromRequestParts for AuthUser {
type Error = JsonError;
fn from_request_parts(parts: &http::request::Parts) -> Result<Self, Self::Error> {
let token = parts
.headers
.get(http::header::AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.ok_or_else(|| JsonError::unauthorized("missing or invalid Authorization header"))?;
match token {
"admin-token" => Ok(AuthUser {
username: "admin".into(),
role: "admin".into(),
}),
"user-token" => Ok(AuthUser {
username: "user".into(),
role: "user".into(),
}),
_ => Err(JsonError::unauthorized("invalid token")),
}
}
}
typeway_path!(type PublicPath = "public");
typeway_path!(type ProtectedPath = "protected");
typeway_path!(type AdminPath = "admin");
type API = (
GetEndpoint<PublicPath, String>,
GetEndpoint<ProtectedPath, String>,
GetEndpoint<AdminPath, String>,
);
async fn public_handler() -> &'static str {
"This is public — no auth needed"
}
async fn protected_handler(user: AuthUser) -> String {
format!("Hello, {}! You have role: {}", user.username, user.role)
}
async fn admin_handler(user: AuthUser) -> Result<String, JsonError> {
if user.role != "admin" {
return Err(JsonError::forbidden("admin access required"));
}
Ok(format!("Welcome to the admin panel, {}!", user.username))
}
#[tokio::main]
async fn main() {
let server = Server::<API>::new((
bind!(public_handler),
bind!(protected_handler),
bind!(admin_handler),
));
println!("Auth example on http://127.0.0.1:3000");
println!(" GET /public — no auth");
println!(" GET /protected — requires Bearer token");
println!(" GET /admin — requires admin token");
server
.serve("127.0.0.1:3000".parse().unwrap())
.await
.unwrap();
}