137 lines
3.8 KiB
Rust
137 lines
3.8 KiB
Rust
use axum::{
|
|
extract::FromRequestParts,
|
|
http::request::Parts,
|
|
response::{IntoResponse as _, Redirect, Response},
|
|
};
|
|
use derive_builder::Builder;
|
|
use sqlx::postgres::types::Oid;
|
|
use uuid::Uuid;
|
|
|
|
use crate::{app::App, errors::AppError};
|
|
|
|
pub(crate) trait NavigatorPage {
|
|
/// Returns the path component of the URL to the corresonding web page.
|
|
/// Starts with a "/" character and the `root_path` if one is specified for
|
|
/// [`crate::settings::Settings`].
|
|
fn get_path(&self) -> String;
|
|
|
|
/// Wrap [`Self::get_path`] in an Axum HTTP 303 redirection response for
|
|
/// convenience.
|
|
fn redirect_to(&self) -> Response {
|
|
Redirect::to(&self.get_path()).into_response()
|
|
}
|
|
}
|
|
|
|
/// Helper type for semantically generating URI paths, e.g. for redirects.
|
|
#[derive(Clone, Debug)]
|
|
pub(crate) struct Navigator {
|
|
root_path: String,
|
|
sub_path: String,
|
|
}
|
|
|
|
impl Navigator {
|
|
pub(crate) fn workspace_page(&self, workspace_id: Uuid) -> Self {
|
|
Self {
|
|
sub_path: format!("/w/{0}/", workspace_id.simple()),
|
|
..self.clone()
|
|
}
|
|
}
|
|
|
|
pub(crate) fn portal_page(&self) -> PortalPageBuilder {
|
|
PortalPageBuilder {
|
|
root_path: Some(self.get_root_path()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
/// Returns a [`NavigatorPage`] builder for navigating to a relation's
|
|
/// "settings" page.
|
|
pub(crate) fn rel_settings_page(&self) -> RelSettingsPageBuilder {
|
|
RelSettingsPageBuilder {
|
|
root_path: Some(self.get_root_path()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
pub(crate) fn get_root_path(&self) -> String {
|
|
self.root_path.to_owned()
|
|
}
|
|
|
|
pub(crate) fn abs_path(&self) -> String {
|
|
format!("{0}{1}", self.root_path, self.sub_path)
|
|
}
|
|
|
|
pub(crate) fn redirect_to(&self) -> Response {
|
|
Redirect::to(&self.abs_path()).into_response()
|
|
}
|
|
}
|
|
|
|
impl FromRequestParts<App> for Navigator {
|
|
type Rejection = AppError;
|
|
|
|
async fn from_request_parts(_: &mut Parts, state: &App) -> Result<Self, Self::Rejection> {
|
|
Ok(Navigator {
|
|
root_path: state.settings.root_path.clone(),
|
|
sub_path: "/".to_owned(),
|
|
})
|
|
}
|
|
}
|
|
|
|
#[derive(Builder, Clone, Debug)]
|
|
pub(crate) struct PortalPage {
|
|
portal_id: Uuid,
|
|
|
|
rel_oid: Oid,
|
|
|
|
#[builder(setter(custom))]
|
|
root_path: String,
|
|
|
|
/// Any value provided for `suffix` will be appended (without %-encoding) to
|
|
/// the final path value. This may be used for sub-paths and/or search
|
|
/// parameters.
|
|
#[builder(default, setter(strip_option))]
|
|
suffix: Option<String>,
|
|
|
|
workspace_id: Uuid,
|
|
}
|
|
|
|
impl NavigatorPage for PortalPage {
|
|
fn get_path(&self) -> String {
|
|
format!(
|
|
"{root_path}/w/{workspace_id}/r/{rel_oid}/p/{portal_id}/{suffix}",
|
|
root_path = self.root_path,
|
|
workspace_id = self.workspace_id.simple(),
|
|
rel_oid = self.rel_oid.0,
|
|
portal_id = self.portal_id.simple(),
|
|
suffix = self.suffix.clone().unwrap_or_default()
|
|
)
|
|
}
|
|
}
|
|
|
|
#[derive(Builder, Clone, Debug)]
|
|
pub(crate) struct RelSettingsPage {
|
|
rel_oid: Oid,
|
|
|
|
#[builder(setter(custom))]
|
|
root_path: String,
|
|
|
|
/// Any value provided for `suffix` will be appended (without %-encoding) to
|
|
/// the final path value. This may be used for sub-paths and/or search
|
|
/// parameters.
|
|
#[builder(default, setter(strip_option))]
|
|
suffix: Option<String>,
|
|
|
|
workspace_id: Uuid,
|
|
}
|
|
|
|
impl NavigatorPage for RelSettingsPage {
|
|
fn get_path(&self) -> String {
|
|
format!(
|
|
"{root_path}/w/{workspace_id}/r/{rel_oid}/settings/{suffix}",
|
|
root_path = self.root_path,
|
|
workspace_id = self.workspace_id.simple(),
|
|
rel_oid = self.rel_oid.0,
|
|
suffix = self.suffix.clone().unwrap_or_default(),
|
|
)
|
|
}
|
|
}
|