упр.12 задача 1
- Краен срок:
- 14.01.2026 23:59
- Точки:
- 4
Срокът за предаване на решения е отминал
Преди да започнете задачата, е добре да си припомните какво са макроси, като тук има съкратена информация за тях: Обобщена информация
Да се реализира макрос api_routes!, който генерира логика за маршрутизация (routing) на HTTP заявки. Макросът трябва да превръща декларативно описание на пътищата във функция pub fn route(req: Request) -> Response.
Изисквания:
- Статични пътища: При съвпадение на метод и точен път (напр. /hello), да се извиква съответната функция.
- Динамични пътища: При пътища с параметър (напр. /square/:x), макросът трябва да:
- Разпознава префикса (всичко преди :).
- Извлича стойността на параметъра от URL-а.
- Опитва да парсне параметъра към посочения тип (напр. i32).
- При неуспешно парсване да връща 400 Bad Request.
- Обработка на грешки:
- Резултатът от handler функциите (Ok или Err) трябва да се преобразува в съответния Response.
- Ако никой маршрут не съвпадне, да се връща 404 Not Found.
// =========================================================
// ДАДЕНИ ТИПОВЕ (НЕ СЕ ПРОМЕНЯТ)
// =========================================================
#[derive(Debug)]
pub struct Request<'a> {
pub method: &'a str,
pub path: &'a str,
pub body: Option<&'a str>,
}
#[derive(Debug, PartialEq)]
pub struct Response {
pub status: u16,
pub body: String,
}
impl Response {
pub fn ok(body: String) -> Self {
Self { status: 200, body }
}
pub fn bad_request() -> Self {
Self { status: 400, body: String::new() }
}
pub fn not_found() -> Self {
Self { status: 404, body: String::new() }
}
}
#[derive(Debug)]
pub enum ApiError {
BadRequest,
NotFound,
}
// =========================================================
// TODO: МАКРОС api_routes!
// Имплементирайте генерирането на функцията route()
// =========================================================
macro_rules! api_routes {
(
$(
$method:ident $handler:ident $( ( $param_name:ident : $param_ty:ty ) )?;
)*
) => {
pub fn route(req: Request) -> Response {
$(
// Съвет: stringify!($method) ще ви даде името на метода като низ
if req.method == stringify!($method) && ... {
// Handle
// req.method == $method
// req.path == /$handler/...
todo!()
}
)*
Response::not_found()
}
};
}
// =========================================================
// HANDLER ФУНКЦИИ (ПОПЪЛВАТ СЕ ОТ СТУДЕНТА)
// =========================================================
fn hello() -> Result<String, ApiError> {
// Върнете "Hello, world!"
todo!("Имплементирайте hello")
}
fn square(x: i32) -> Result<String, ApiError> {
// Върнете квадрата на x като низ
todo!("Имплементирайте square")
}
// =========================================================
// ИЗВИКВАНЕ НА МАКРОСА (НЕ СЕ ПРОМЕНЯ)
// =========================================================
api_routes! {
GET hello;
GET square(x : i32);
}
| input | Макросът прави това | output |
|---|---|---|
| GET /hello | извикай функцията hello() | 200 OK: "Hello world!" |
| GET /square/5 | вземи "5", направи го на число и извикай square(5) | 200 OK: 25 |
| GET /square/abc | взима "abc", вижда, че не е число и връща грешка | 400 Bad Request |
| GET /something | не открива нито един маршрут и връща грешка | 404 Not Found |
Хитринка: когато искате да разграничите различни ситуации в имплементацията на едно макро, можете да изполвате помощно макро.
(За използване от външна библиотека помощните макрота трябва да са публично видими (export-нати), но по конвенция името започва с долни черти, за да означи, че това е имплементационен детайл.)
macro_rules! __helper {
( /* some tokens */ ) => { /* ... */ };
( /* other tokens */ ) => { /* ... */ };
}
Друг вариант е да използвате private ръкави на същото macro.
По конвенция, ако ръкав започва с @some_identifier, той е предназначен за вътрешно ползване от самото макро.
macro_rules! api_routes {
// публични ръкави
( $( $method:ident $handler:ident $( ( $param_name:ident : $param_ty:ty ) )? )* ) => {
// имплементация...
// по някое време се изиква api_routes!(@handle_route ....)
};
// private ръкави
(@handle_route /* some tokens */ ) => { /* ... */ };
(@handle_route /* other tokens */ ) => { /* ... */ };
}
Задължително прочетете (или си припомнете): Указания за предаване на домашни
Погрижете се решението ви да се компилира с базовия тест:
