#[cfg(feature = "field-extractors")]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct AppUrl<const KEY: &'static str = CURRENT_APP_KEY> {
pub url: Option<String>,
pub scheme: ft_sdk::Scheme,
pub host: ft_sdk::Host,
}
#[cfg(feature = "field-extractors")]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct RequiredAppUrl<const KEY: &'static str = CURRENT_APP_KEY> {
pub url: String,
scheme: ft_sdk::Scheme,
host: ft_sdk::Host,
}
pub const APP_URL_HEADER: &str = "x-fastn-app-url";
pub const APP_URLS_HEADER: &str = "x-fastn-app-urls";
pub const CURRENT_APP_KEY: &str = "current-app";
#[cfg(feature = "field-extractors")]
impl<const KEY: &'static str> AppUrl<KEY> {
pub fn join<P: AsRef<str>>(&self, path: P) -> ft_sdk::Result<String> {
join(KEY, &self.url, &self.scheme, &self.host, path)
}
pub fn is_set(&self) -> bool {
self.url.is_some()
}
pub fn root(&self) -> ft_sdk::Result<String> {
join(KEY, &self.url, &self.scheme, &self.host, "/")
}
}
#[cfg(feature = "field-extractors")]
impl<const KEY: &'static str> RequiredAppUrl<KEY> {
pub fn join<P: AsRef<str>>(&self, path: P) -> String {
join(KEY, &Some(self.url.clone()), &self.scheme, &self.host, path).expect("")
}
pub fn root(&self) -> String {
join(KEY, &Some(self.url.clone()), &self.scheme, &self.host, "/").unwrap()
}
}
#[cfg(feature = "field-extractors")]
impl<const KEY: &'static str> ft_sdk::FromRequest for RequiredAppUrl<KEY> {
fn from_request(req: &http::Request<serde_json::Value>) -> ft_sdk::Result<RequiredAppUrl<KEY>> {
let a: AppUrl<KEY> = ft_sdk::FromRequest::from_request(req)?;
let url = a
.url
.ok_or_else(|| ft_sdk::server_error!("{KEY} not install?"))?;
Ok(RequiredAppUrl {
url,
scheme: a.scheme,
host: a.host,
})
}
}
#[cfg(feature = "field-extractors")]
impl<const KEY: &'static str> ft_sdk::FromRequest for AppUrl<KEY> {
fn from_request(req: &http::Request<serde_json::Value>) -> ft_sdk::Result<AppUrl<KEY>> {
let scheme = ft_sdk::Scheme::from_request(req)?;
let host = ft_sdk::Host::from_request(req)?;
from_request(KEY, req).map(|u| AppUrl {
url: u,
scheme,
host,
})
}
}
pub(crate) fn join<P: AsRef<str>>(
key: &str,
app_url: &Option<String>,
scheme: &ft_sdk::Scheme,
host: &ft_sdk::Host,
path: P,
) -> ft_sdk::Result<String> {
let v = match app_url {
Some(v) => v,
None => return Err(anyhow::anyhow!("app-url not found for {key}")),
};
if path.as_ref() == "/" {
return Ok(format!("{scheme}://{host}{v}"));
}
Ok(format!(
"{scheme}://{host}{v}{path}/",
path = path.as_ref().trim_matches('/')
))
}
pub(crate) fn from_request(
key: &str,
req: &http::Request<serde_json::Value>,
) -> ft_sdk::Result<Option<String>> {
let v = if key == CURRENT_APP_KEY {
Some(
req.headers()
.get(APP_URL_HEADER)
.expect("host always provides this header")
.to_str()?
.to_string(),
)
} else {
serde_json::from_str::<std::collections::HashMap<String, String>>(
req.headers()
.get(APP_URLS_HEADER)
.expect("host always provides this header")
.to_str()?,
)?
.remove(key)
};
if v.is_none() {
ft_sdk::println!("app-url not found for {key}");
}
Ok(v.map(|v| {
if v == "/" {
return "/".to_string();
}
format!("/{}/", v.trim_matches('/'))
}))
}