iota_rest_kv/
extractors.rs

1// Copyright (c) 2025 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! This module includes custom extractors needed for validation and custom
5//! errors messages on the input provided by the client.
6
7use core::str;
8
9use axum::{
10    extract::{FromRequestParts, Path},
11    http::request::Parts,
12};
13use iota_storage::http_key_value_store::{ItemType, Key};
14use serde::Deserialize;
15
16use crate::errors::ApiError;
17
18/// Path segment labels will be matched with struct field names.
19#[derive(Deserialize, Debug)]
20struct RequestParams {
21    /// The supported items that are associated with the [`Key`].
22    item_type: ItemType,
23    /// The **digest**, **object id**, or a **checkpoint sequence number**
24    /// encoded as [`base64_url`].
25    key: String,
26}
27
28/// We define our own extractor that includes validation and custom error
29/// message.
30///
31/// This custom extractor matches [`Path`] segments and deserilize them
32/// internally into [`RequestParams`] and constructs a [`Key`].
33pub struct ExtractPath(pub Key);
34
35impl<S> FromRequestParts<S> for ExtractPath
36where
37    S: Send + Sync,
38{
39    type Rejection = ApiError;
40
41    async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
42        match Path::<RequestParams>::from_request_parts(parts, state).await {
43            Ok(value) => {
44                // based on the item type and encoded key construct the Key enum
45                let key = Key::new(value.item_type.to_string().as_str(), value.key.as_str())
46                    .map_err(|err| ApiError::BadRequest(format!("invalid input: {err}")))?;
47                Ok(ExtractPath(key))
48            }
49            Err(e) => Err(ApiError::BadRequest(format!(
50                "invalid path parameter provided: {e}",
51            ))),
52        }
53    }
54}