mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2024-12-26 03:38:21 +01:00
codename mycelium became lib-humus
This commit is contained in:
parent
912a119361
commit
f2e9e36e99
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -492,6 +492,7 @@ dependencies = [
|
||||
"governor",
|
||||
"idna 0.3.0",
|
||||
"lazy_static",
|
||||
"lib-humus",
|
||||
"maxminddb",
|
||||
"mime",
|
||||
"parking_lot",
|
||||
@ -1011,6 +1012,19 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lib-humus"
|
||||
version = "0.1.0"
|
||||
source = "git+https://codeberg.org/slatian/lib-humus.git#5c9315b314ba0c247d236d6318041c6872c90b04"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"axum-extra",
|
||||
"mime",
|
||||
"serde",
|
||||
"tera",
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.149"
|
||||
|
@ -7,6 +7,8 @@ authors = ["Slatian <baschdel@disroot.org>"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
lib-humus = { version="0.1", features=["axum-view+cookie"], git="https://codeberg.org/slatian/lib-humus.git" }
|
||||
|
||||
axum = { version = "0.6", features = ["macros", "headers"] }
|
||||
axum-extra = { version = "0.7", features = ["cookie"] }
|
||||
axum-client-ip = "0.4"
|
||||
|
10
src/main.rs
10
src/main.rs
@ -34,11 +34,14 @@ use std::collections::HashMap;
|
||||
use std::net::IpAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use lib_humus::TemplateEngineLoader;
|
||||
use lib_humus::read_toml_from_file;
|
||||
use lib_humus::HumusEngine;
|
||||
|
||||
mod config;
|
||||
mod geoip;
|
||||
mod idna;
|
||||
mod ipinfo;
|
||||
mod mycelium;
|
||||
mod ratelimit;
|
||||
mod settings;
|
||||
mod simple_dns;
|
||||
@ -51,15 +54,12 @@ use crate::geoip::{
|
||||
LocationResult,
|
||||
};
|
||||
use crate::idna::IdnaName;
|
||||
use crate::mycelium::MycEngine;
|
||||
use crate::mycelium::TemplateEngineLoader;
|
||||
use crate::mycelium::read_toml_from_file;
|
||||
use crate::simple_dns::DnsLookupResult;
|
||||
use crate::settings::*;
|
||||
use crate::view::View;
|
||||
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
|
||||
|
||||
type TemplatingEngine = MycEngine<View,QuerySettings,ResponseFormat>;
|
||||
type TemplatingEngine = HumusEngine<View,QuerySettings,ResponseFormat>;
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct SettingsQuery {
|
||||
|
@ -1,146 +0,0 @@
|
||||
|
||||
use axum::{
|
||||
body::{Bytes,Full},
|
||||
headers::HeaderValue,
|
||||
http::StatusCode,
|
||||
http::header,
|
||||
http::header::SET_COOKIE,
|
||||
response::IntoResponse,
|
||||
response::Response,
|
||||
};
|
||||
use tera::Tera;
|
||||
use toml::Table;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::mycelium::MycView;
|
||||
use crate::mycelium::MycFormat;
|
||||
use crate::mycelium::MycFormatFamily;
|
||||
use crate::mycelium::MycQuerySettings;
|
||||
|
||||
/* The engine itself */
|
||||
|
||||
/// A constructable version of MycEngine without the types attached.
|
||||
///
|
||||
/// Can be converted to a MycEngine using `into()`.
|
||||
pub struct MycProtoEngine {
|
||||
pub tera: Tera,
|
||||
pub template_config: Option<Table>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MycEngine<V, S, F>
|
||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
||||
pub tera: Tera,
|
||||
pub template_config: Option<Table>,
|
||||
|
||||
phantom_view: PhantomData<V>,
|
||||
phantom_settings: PhantomData<S>,
|
||||
phantom_format: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<V, S, F> MycEngine<V, S, F>
|
||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
||||
|
||||
pub fn new(tera: Tera, template_config: Option<Table>) -> Self {
|
||||
Self {
|
||||
tera: tera,
|
||||
template_config: template_config,
|
||||
phantom_view: PhantomData,
|
||||
phantom_settings: PhantomData,
|
||||
phantom_format: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes settings and a view, converting it to a serveable response.
|
||||
pub async fn render_view(
|
||||
&self,
|
||||
settings: &S,
|
||||
view: V,
|
||||
) -> Response {
|
||||
let status_code = view.get_status_code(&settings);
|
||||
let cookie_string = view.get_cookie_header(&settings);
|
||||
let format = settings.get_format();
|
||||
|
||||
let mut response = match format.get_family() {
|
||||
MycFormatFamily::Template => {
|
||||
let template_name = view.get_template_name();
|
||||
let mime_type = format.get_mime_type();
|
||||
|
||||
let mut context = tera::Context::new();
|
||||
context.insert("view", &template_name);
|
||||
//intented for shared macros
|
||||
context.insert("format", &format.get_name());
|
||||
context.insert("mimetype", &mime_type.to_string());
|
||||
context.insert("data", &view);
|
||||
context.insert("extra", &self.template_config);
|
||||
settings.initalize_template_context(&mut context);
|
||||
|
||||
match self.tera.render(&(template_name.clone()+&format.get_file_extension()), &context) {
|
||||
Ok(text) => {
|
||||
let mut response = (
|
||||
[(
|
||||
header::CONTENT_TYPE,
|
||||
HeaderValue::from_str(mime_type.as_ref())
|
||||
.expect("MimeType should always be a valid header value.")
|
||||
)],
|
||||
Into::<Full<Bytes>>::into(text),
|
||||
).into_response();
|
||||
view.update_response(&mut response, &settings);
|
||||
response
|
||||
},
|
||||
Err(e) => {
|
||||
println!("There was an error while rendering template {}: {e:?}", template_name);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Template error in {}, contact owner or see logs.\n", template_name)
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
}
|
||||
MycFormatFamily::API => {
|
||||
view.get_api_response(&settings)
|
||||
}
|
||||
};
|
||||
|
||||
// Everything went well and nobody did the following work for us.
|
||||
if response.status() == StatusCode::OK {
|
||||
|
||||
// Set status code
|
||||
if status_code != StatusCode::OK {
|
||||
*response.status_mut() = status_code;
|
||||
}
|
||||
|
||||
// Set cookie header
|
||||
if let Some(cookie_string) = cookie_string {
|
||||
if let Ok(header_value) = HeaderValue::from_str(&cookie_string) {
|
||||
response.headers_mut().append(
|
||||
SET_COOKIE,
|
||||
header_value,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// return response
|
||||
response
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, S, F> From<MycProtoEngine> for MycEngine<V, S, F>
|
||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
||||
fn from(e: MycProtoEngine) -> Self {
|
||||
Self::new(e.tera, e.template_config)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, S, F> From<MycEngine<V, S, F>> for MycProtoEngine
|
||||
where V: MycView<S, F>, S: MycQuerySettings<F>, F: MycFormat {
|
||||
fn from(e: MycEngine<V, S, F>) -> Self {
|
||||
Self {
|
||||
tera: e.tera,
|
||||
template_config: e.template_config,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,132 +0,0 @@
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use mime::Mime;
|
||||
|
||||
/// Defines how the response should be rendered.
|
||||
pub enum MycFormatFamily {
|
||||
/// When rendering the templating engine will be invoked
|
||||
Template,
|
||||
/// When rendering the [View](./trait.View.html)
|
||||
/// is asked to generate an API response.
|
||||
API,
|
||||
}
|
||||
|
||||
/// Implement on a type that is able to describe a response format.
|
||||
///
|
||||
/// It is best implemented on an enum.
|
||||
pub trait MycFormat: ToString+Clone+Default+Send {
|
||||
|
||||
// Return the format family this
|
||||
fn get_family(&self) -> MycFormatFamily {
|
||||
match self.get_name().as_str() {
|
||||
"json" => MycFormatFamily::API,
|
||||
_ => MycFormatFamily::Template,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the file extnsion for the format.
|
||||
///
|
||||
/// Used for deriving the path for the template name.
|
||||
///
|
||||
/// Defaults to `.{self.get_name()}`
|
||||
/// with the exception of the name being `text`
|
||||
/// then it defaults to `.txt`.
|
||||
fn get_file_extension(&self) -> String {
|
||||
match self.get_name().as_str() {
|
||||
"text" => ".txt".to_string(),
|
||||
_ => ".".to_owned()+&self.get_name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the name of the format,
|
||||
/// by default taken from the ToString implementation.
|
||||
fn get_name(&self) -> String {
|
||||
self.to_string()
|
||||
}
|
||||
|
||||
/// Allows adding extra mimetypes quickly for prototyping
|
||||
///
|
||||
/// Implementing get_mime_type() properly is recommended
|
||||
/// for production use.
|
||||
fn get_less_well_known_mimetype(&self) -> Option<Mime> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns a textual representation of the Mimetype.
|
||||
///
|
||||
/// It is recommended to implement this when in production use.
|
||||
///
|
||||
/// For prototyping the default implementation makes assumptions
|
||||
/// based on the output of get_name(), falling back
|
||||
/// to get_less_well_known_mimetype() and the "application/octet-stream" type.
|
||||
///
|
||||
/// The default implementation knows the following associations:
|
||||
/// * `text`: `text/plain; charset=utf-8`
|
||||
/// * `html`: `text/html; charset=utf-8`
|
||||
/// * `json`: `application/json`
|
||||
/// * `xml`: `application/xml`
|
||||
/// * `rss`: `application/rss+xml`
|
||||
/// * `atom`: `application/atom+xml`
|
||||
///
|
||||
/// *Implementation Note:* It may be possible that two different views
|
||||
/// have the same MimeType (maybe two json representations for different consumers).
|
||||
///
|
||||
fn get_mime_type(&self) -> Mime {
|
||||
match self.get_name().as_str() {
|
||||
"text" => mime::TEXT_PLAIN_UTF_8,
|
||||
"html" => mime::TEXT_HTML_UTF_8,
|
||||
"json" => mime::APPLICATION_JSON,
|
||||
"xml" => "application/xml".parse()
|
||||
.expect("Parse static application/xml"),
|
||||
"rss" => "application/rss+xml".parse()
|
||||
.expect("Parse static application/rss+xml"),
|
||||
"atom" => "application/atom+xml".parse()
|
||||
.expect("Parse static application/atom+xml"),
|
||||
_ =>
|
||||
self.get_less_well_known_mimetype()
|
||||
.unwrap_or(mime::APPLICATION_OCTET_STREAM),
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a view from its name.
|
||||
fn from_name(name: &str) -> Option<Self>;
|
||||
}
|
||||
|
||||
// Some Sample implementations
|
||||
|
||||
#[derive(Clone,Serialize,Deserialize,Default)]
|
||||
#[serde(rename_all="lowercase")]
|
||||
pub enum HtmlTextJsonFormat {
|
||||
#[default]
|
||||
Html,
|
||||
Text,
|
||||
Json,
|
||||
}
|
||||
|
||||
impl ToString for HtmlTextJsonFormat {
|
||||
fn to_string(&self) -> String {
|
||||
match self {
|
||||
Self::Html => "html",
|
||||
Self::Text => "text",
|
||||
Self::Json => "json",
|
||||
}.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl MycFormat for HtmlTextJsonFormat {
|
||||
|
||||
//TODO: implement other methods to make it more performant
|
||||
|
||||
fn from_name(name: &str) -> Option<Self> {
|
||||
match name {
|
||||
"html" => Some(Self::Html),
|
||||
"text" => Some(Self::Text),
|
||||
"json" => Some(Self::Json),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,17 +0,0 @@
|
||||
mod engine;
|
||||
mod format;
|
||||
mod query_settings;
|
||||
mod template_loader;
|
||||
mod toml_helper;
|
||||
mod view;
|
||||
|
||||
pub use self::engine::MycEngine;
|
||||
pub use self::engine::MycProtoEngine;
|
||||
pub use self::format::HtmlTextJsonFormat;
|
||||
pub use self::format::MycFormat;
|
||||
pub use self::format::MycFormatFamily;
|
||||
pub use self::query_settings::MycQuerySettings;
|
||||
pub use self::template_loader::TemplateEngineLoader;
|
||||
pub use self::toml_helper::read_toml_from_file;
|
||||
pub use self::toml_helper::TomlError;
|
||||
pub use self::view::MycView;
|
@ -1,16 +0,0 @@
|
||||
|
||||
use tera::Context;
|
||||
|
||||
use crate::mycelium::MycFormat;
|
||||
|
||||
pub trait MycQuerySettings<F>
|
||||
where F: MycFormat {
|
||||
|
||||
/// Called before rendering a template to initalize it with
|
||||
/// values that come from the query itself.
|
||||
fn initalize_template_context(&self, context: &mut Context);
|
||||
|
||||
/// Returns the requested output format
|
||||
fn get_format(&self) -> F;
|
||||
|
||||
}
|
@ -1,114 +0,0 @@
|
||||
|
||||
use tera::Tera;
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use crate::mycelium::MycProtoEngine;
|
||||
use crate::mycelium::read_toml_from_file;
|
||||
use crate::mycelium::TomlError;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct TemplateEngineLoader {
|
||||
pub template_location: String,
|
||||
pub extra_config_location: Option<String>,
|
||||
}
|
||||
|
||||
impl TemplateEngineLoader {
|
||||
|
||||
pub fn new(
|
||||
template_location: String,
|
||||
extra_config_location: Option<String>
|
||||
) -> Self {
|
||||
Self {
|
||||
template_location: template_location,
|
||||
extra_config_location: extra_config_location,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cli_template_location(mut self, location: Option<String>) -> Self {
|
||||
if let Some(location) = location {
|
||||
self.template_location = location;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn cli_extra_config_location(mut self, location: Option<String>) -> Self {
|
||||
if let Some(location) = location {
|
||||
self.extra_config_location = Some(location);
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
// Returns the template base directory.
|
||||
//
|
||||
// Contructed by ensuring the template_location ends in a "/".
|
||||
pub fn base_dir(&self) -> String {
|
||||
if self.template_location.ends_with("/") {
|
||||
self.template_location.clone()
|
||||
} else {
|
||||
self.template_location.clone()+"/"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_templates(
|
||||
&self
|
||||
) -> Result<MycProtoEngine,TemplateEngineLoaderError> {
|
||||
let template_base_dir = self.base_dir();
|
||||
let template_extra_config_res = match &self.extra_config_location {
|
||||
Some(path) => read_toml_from_file(path),
|
||||
None => {
|
||||
read_toml_from_file(&(template_base_dir.clone()+"extra.toml"))
|
||||
}
|
||||
};
|
||||
let template_extra_config = match template_extra_config_res {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => match &e {
|
||||
TomlError::FileError{..} => {
|
||||
// Only fatal if the file was explicitly requested.
|
||||
// An implicit request could also mean that
|
||||
// the template doesn't need a config file.
|
||||
if self.extra_config_location.is_some() {
|
||||
return Err(TemplateEngineLoaderError::TomlError(e));
|
||||
}
|
||||
None
|
||||
},
|
||||
TomlError::ParseError{..} => {
|
||||
return Err(TemplateEngineLoaderError::TomlError(e));
|
||||
}
|
||||
},
|
||||
};
|
||||
let template_glob = template_base_dir.clone()+"*";
|
||||
println!("Parsing Templates from '{}' ...", &template_glob);
|
||||
let res = Tera::new((template_glob).as_str());
|
||||
let tera = match res {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Err(TemplateEngineLoaderError::TemplateParseError{
|
||||
path: template_glob,
|
||||
tera_error: e,
|
||||
});
|
||||
}
|
||||
};
|
||||
Ok(MycProtoEngine {
|
||||
tera: tera,
|
||||
template_config: template_extra_config,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TemplateEngineLoaderError {
|
||||
TomlError(TomlError),
|
||||
TemplateParseError{path: String, tera_error: tera::Error },
|
||||
}
|
||||
|
||||
impl fmt::Display for TemplateEngineLoaderError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::TomlError(e) =>
|
||||
write!(f,"Error with template extra configuration:\n(Depending on you template the file not being found might be okay)\n{e}"),
|
||||
Self::TemplateParseError{path, tera_error} =>
|
||||
write!(f,"Error parsing template '{path}':\n{tera_error}"),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
use serde::Deserialize;
|
||||
|
||||
use std::fmt;
|
||||
use std::fs;
|
||||
|
||||
pub fn read_toml_from_file<T>(path: &String) -> Result<T,TomlError>
|
||||
where T: for<'de> Deserialize<'de> {
|
||||
let text = match fs::read_to_string(path) {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return Err(TomlError::FileError{
|
||||
path: path.clone(),
|
||||
io_error: e
|
||||
});
|
||||
}
|
||||
};
|
||||
match toml::from_str(&text) {
|
||||
Ok(t) => Ok(t),
|
||||
Err(e) => {
|
||||
return Err(TomlError::ParseError{
|
||||
path: path.clone(),
|
||||
toml_error: e
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum TomlError {
|
||||
FileError{ path: String, io_error: std::io::Error },
|
||||
ParseError{ path: String, toml_error: toml::de::Error },
|
||||
}
|
||||
|
||||
impl fmt::Display for TomlError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::FileError{path, io_error} =>
|
||||
write!(f,"Error while reading file '{path}': {io_error}"),
|
||||
Self::ParseError{path, toml_error} =>
|
||||
write!(f,"Unable to parse file as toml '{path}':\n{toml_error}"),
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
use axum::response::IntoResponse;
|
||||
use axum::response::Json;
|
||||
use axum::response::Response;
|
||||
use axum::http::status::StatusCode;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::mycelium::MycQuerySettings;
|
||||
use crate::mycelium::MycFormat;
|
||||
|
||||
pub trait MycView<S,F>: Serialize + Sized
|
||||
where S: MycQuerySettings<F>, F: MycFormat {
|
||||
|
||||
/// Returns the template name that will be used to select
|
||||
/// the template file.
|
||||
///
|
||||
/// If the name is "404" for an html response the template
|
||||
/// file "404.html" will be used.
|
||||
///
|
||||
/// Also ends up as the `view` variable in the template.
|
||||
fn get_template_name(&self) -> String;
|
||||
|
||||
//TODO: Add query settings to these
|
||||
|
||||
/// Returns the reponse code for the view.
|
||||
///
|
||||
/// The numeric value will be useable as `http_status` in the template.
|
||||
fn get_status_code(&self, settings: &S) -> StatusCode;
|
||||
|
||||
/// If this returns a String it will be used as the cookie header.
|
||||
///
|
||||
/// See: [axum: Constructing a Cookie](https://docs.rs/axum-extra/0.8.0/axum_extra/extract/cookie/struct.Cookie.html#constructing-a-cookie)
|
||||
fn get_cookie_header(&self, _settings: &S) -> Option<String> { None }
|
||||
|
||||
/// Update non-API responses after they have been built.
|
||||
///
|
||||
/// Useful for setting extra headers. Does noting by default.
|
||||
fn update_response(&self, _response: &mut Response, _settings: &S) {}
|
||||
|
||||
/// Return an API-Response
|
||||
///
|
||||
/// By default causes the view to Serialize itself
|
||||
/// to a json response using serde.
|
||||
///
|
||||
/// Response code and cookie headers are queried in
|
||||
/// advance and set on the reulting response if it has a status code of 200.
|
||||
/// Otherwise it is assumed that the response genrating logic
|
||||
/// alredy took care of that.
|
||||
fn get_api_response(self, _settings: &S) -> Response {
|
||||
Json(self).into_response()
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
use serde::{Deserialize,Serialize};
|
||||
|
||||
use lib_humus::HtmlTextJsonFormat;
|
||||
use lib_humus::HumusQuerySettings;
|
||||
|
||||
use std::sync::Arc;
|
||||
use crate::mycelium::HtmlTextJsonFormat;
|
||||
use crate::mycelium::MycQuerySettings;
|
||||
|
||||
/* Response format */
|
||||
|
||||
@ -25,7 +26,7 @@ pub struct Selectable {
|
||||
pub weight: i32,
|
||||
}
|
||||
|
||||
impl MycQuerySettings<ResponseFormat> for QuerySettings {
|
||||
impl HumusQuerySettings<ResponseFormat> for QuerySettings {
|
||||
|
||||
fn initalize_template_context(&self, context: &mut tera::Context) {
|
||||
context.insert("language", &self.lang);
|
||||
|
@ -5,6 +5,7 @@ use axum::response::IntoResponse;
|
||||
use axum::response::Response;
|
||||
use axum_extra::extract::cookie::Cookie;
|
||||
use axum_extra::extract::cookie;
|
||||
use lib_humus::HumusView;
|
||||
|
||||
use crate::DigResult;
|
||||
use crate::IpResult;
|
||||
@ -12,7 +13,6 @@ use crate::config::DnsResolverConfig;
|
||||
use crate::settings::QuerySettings;
|
||||
use crate::settings::ResponseFormat;
|
||||
|
||||
use crate::mycelium::MycView;
|
||||
|
||||
#[derive(serde::Serialize, Clone)]
|
||||
#[serde(untagged)]
|
||||
@ -28,7 +28,7 @@ pub enum View {
|
||||
NotFound,
|
||||
}
|
||||
|
||||
impl MycView<QuerySettings, ResponseFormat> for View {
|
||||
impl HumusView<QuerySettings, ResponseFormat> for View {
|
||||
fn get_template_name(&self) -> String {
|
||||
match self {
|
||||
View::Asn{..} => "asn",
|
||||
|
Loading…
Reference in New Issue
Block a user