Repository: tokio-rs/axum
File: axum/src/routing/path_router.rs (Rust)

Code before:

    pub(super) fn set_fallback(&mut self, endpoint: Endpoint<S>) {
        self.replace_endpoint("/", endpoint.clone());
        self.replace_endpoint(FALLBACK_PARAM_PATH, endpoint);
    }
}

// Validates a path in compile time, used with the vpath macro.
// Allow dead code needed because this is only used within the vpath macro
// and if there's no call to it, then, there's no users of it.
#[allow(dead_code)]
#[doc(hidden)]
pub const fn __private_validate_static_path(path: &'static str) -> &'static str {
    if path.is_empty() {
        panic!("Paths must start with a `/`. Use \"/\" for root routes")
    }
    if path.as_bytes()[0] != b'/' {
        panic!("Paths must start with /");
    }
    path
}

#[rustversion::since(1.80)]
#[macro_export]
/// This macro checks that your string is the correct format to be used as a route,
/// and fails in compilation time instead of runtime, if it's not.
///
/// This example will stop the compialtion:
///
/// ```compile_fail
/// use axum::routing::*;
/// use axum::vpath;
///
/// let handler = async {};
/// let router = axum::Router::<()>::new()
///     .route(vpath!("invalid_path"), get(root))
///     .to_owned();
///
/// async fn root() {}
/// ```
///
/// This one will compile without problems:
///
/// ```no_run
/// use axum::routing::*;
/// use axum::vpath;
///
/// let router = axum::Router::<()>::new()
///     .route(vpath!("/valid_path"), get(root))
///     .to_owned();
///

Review comment (suggestion):
This is a slightly confusingly-written sentence. Would recommend perhaps:

> Checks that a path is usable as a route at compile time.

Code after:

    pub(super) fn set_fallback(&mut self, endpoint: Endpoint<S>) {
        self.replace_endpoint("/", endpoint.clone());
        self.replace_endpoint(FALLBACK_PARAM_PATH, endpoint);
    }
}

fn validate_path(v7_checks: bool, path: &str) -> Result<(), &'static str> {
    if path.is_empty() {
        return Err("Paths must start with a `/`. Use \"/\" for root routes");
    } else if !path.starts_with('/') {
        return Err("Paths must start with a `/`");
    }

    if v7_checks {
        validate_v07_paths(path)?;
    }

    Ok(())
}

fn validate_v07_paths(path: &str) -> Result<(), &'static str> {
    path.split('/')
        .find_map(|segment| {
            if segment.starts_with(':') {
                Some(Err(
                    "Path segments must not start with `:`. For capture groups, use \
                `{capture}`. If you meant to literally match a segment starting with \
                a colon, call `without_v07_checks` on the router.",
                ))
            } else if segment.starts_with('*') {
                Some(Err(
                    "Path segments must not start with `*`. For wildcard capture, use \
                `{*wildcard}`. If you meant to literally match a segment starting with \
                an asterisk, call `without_v07_checks` on the router.",
                ))
            } else {
                None
            }
        })
        .unwrap_or(Ok(()))
}

impl<S, const IS_FALLBACK: bool> PathRouter<S, IS_FALLBACK>
where
    S: Clone + Send + Sync + 'static,
{
    pub(super) fn without_v07_checks(&mut self) {
        self.v7_checks = false;
    }