Replace all uses and implementations of new() with default()
This commit is contained in:
parent
315956e20d
commit
6b739d93c2
15 changed files with 100 additions and 84 deletions
|
|
@ -9,7 +9,7 @@ pub const SKIP_PATHS: &[&str] = &["en::types::Config::parse_text"];
|
|||
#[macro_export]
|
||||
macro_rules! log {
|
||||
($fmt:expr $(, $($arg:tt)+ )? ) => {{
|
||||
let mut display_path = String::new();
|
||||
let mut display_path = String::default();
|
||||
let mut path = std::any::type_name_of_val(&|| {})
|
||||
.to_string().replace("::{{closure}}", "");
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use en::{prelude::*, ONSET, syntax::serial::populate_graph, syntax};
|
|||
async fn main() -> io::Result<()> {
|
||||
print_debugging_state();
|
||||
|
||||
let args = syntax::command::Arguments::new().parse();
|
||||
let args = syntax::command::Arguments::default().parse();
|
||||
let address = args.make_address();
|
||||
|
||||
#[allow(clippy::print_stderr)]
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{syntax::serial::Format, types::Graph};
|
|||
mod handlers;
|
||||
|
||||
pub fn new(graph: &Graph) -> Router {
|
||||
let mut router = Router::new()
|
||||
let mut router = Router::default()
|
||||
.route(
|
||||
"/",
|
||||
get(|| handlers::navigation::page("index.html"))
|
||||
|
|
@ -91,7 +91,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn smoke() {
|
||||
let router = axum::Router::new();
|
||||
let router = axum::Router::default();
|
||||
let response = router
|
||||
.oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
|
||||
.await
|
||||
|
|
@ -121,7 +121,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn no_about_page() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.about = false;
|
||||
|
||||
let response = request("/about", Some(&config)).await;
|
||||
|
|
@ -130,7 +130,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn no_tree_page() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.tree = false;
|
||||
|
||||
let response = request("/tree", Some(&config)).await;
|
||||
|
|
@ -139,7 +139,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn no_toml_raw_graph() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.raw_toml = false;
|
||||
|
||||
let response = request("/graph/toml", Some(&config)).await;
|
||||
|
|
@ -148,7 +148,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn no_json_raw_graph() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.raw_json = false;
|
||||
|
||||
let response = request("/graph/json", Some(&config)).await;
|
||||
|
|
@ -157,7 +157,7 @@ mod tests {
|
|||
|
||||
#[tokio::test]
|
||||
async fn no_raw_graph() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.raw = false;
|
||||
|
||||
let toml_response = request("/graph/toml", Some(&config)).await;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub(in crate::router::handlers) fn by_code(
|
|||
}
|
||||
|
||||
fn make_body(code: Option<u16>, message: Option<&str>) -> String {
|
||||
let mut context = tera::Context::new();
|
||||
let mut context = tera::Context::default();
|
||||
|
||||
let out_code = code.unwrap_or(500);
|
||||
let out_message = &message.unwrap_or("Unknown error");
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ pub async fn node(Path(id): Path<String>) -> Response<Body> {
|
|||
.into_response();
|
||||
}
|
||||
|
||||
let mut context = tera::Context::new();
|
||||
let mut context = tera::Context::default();
|
||||
context.insert("node", &node);
|
||||
context.insert("text", &content::parse(&node.text, &graph.meta.config));
|
||||
context.insert("incoming", &graph.incoming.get(&id));
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::{syntax::serial::populate_graph, router::handlers, types::Node};
|
|||
|
||||
#[expect(clippy::unused_async)]
|
||||
pub async fn page(template: &str) -> Response<Body> {
|
||||
let mut context = tera::Context::new();
|
||||
let mut context = tera::Context::default();
|
||||
let graph = populate_graph();
|
||||
let root_node = graph.get_root().unwrap_or_default();
|
||||
let nodes: Vec<Node> = graph.nodes.into_values().collect();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ pub(in crate::router::handlers) fn render(
|
|||
match tera.render(name, context) {
|
||||
Ok(t) => (t, 200),
|
||||
Err(e) => {
|
||||
let mut error_context = tera::Context::new();
|
||||
let mut error_context = tera::Context::default();
|
||||
|
||||
let out_error_message = match error_message {
|
||||
Some(s) => &format!(
|
||||
|
|
@ -108,15 +108,25 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn by_filename_forced_error() {
|
||||
let response =
|
||||
by_filename("index.html", &tera::Context::new(), 418, None, true);
|
||||
let response = by_filename(
|
||||
"index.html",
|
||||
&tera::Context::default(),
|
||||
418,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
assert_eq!(response.status(), 418);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn by_filename_index() {
|
||||
let response =
|
||||
by_filename("index.html", &tera::Context::new(), 418, None, false);
|
||||
let response = by_filename(
|
||||
"index.html",
|
||||
&tera::Context::default(),
|
||||
418,
|
||||
None,
|
||||
false,
|
||||
);
|
||||
assert_eq!(response.status(), 200);
|
||||
}
|
||||
|
||||
|
|
@ -124,7 +134,7 @@ mod tests {
|
|||
fn by_filename_file_not_found() {
|
||||
let response = by_filename(
|
||||
"bwbl3BnWsluIgbO2NV9t3vtihwcjuF6t",
|
||||
&tera::Context::new(),
|
||||
&tera::Context::default(),
|
||||
418,
|
||||
None,
|
||||
false,
|
||||
|
|
@ -134,14 +144,15 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn by_filename_empty() {
|
||||
let response = by_filename("", &tera::Context::new(), 418, None, false);
|
||||
let response =
|
||||
by_filename("", &tera::Context::default(), 418, None, false);
|
||||
assert_eq!(response.status(), 500);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn render_with_context() {
|
||||
let payload = "dBgIw8DnNHxJojiXzu445qUC4UpxwZCy";
|
||||
let mut context = tera::Context::new();
|
||||
let mut context = tera::Context::default();
|
||||
let node = crate::types::Node::new(Some(payload.to_string()));
|
||||
let graph = crate::syntax::serial::populate_graph();
|
||||
context.insert("node", &node);
|
||||
|
|
@ -161,7 +172,7 @@ mod tests {
|
|||
let payload = "dBgIw8DnNHxJojiXzu445qUC4UpxwZCy";
|
||||
let (body, status) = render(
|
||||
"ObH9jYUl4wMhUNcXnuqwVVzHoqx4ufyN",
|
||||
&tera::Context::new(),
|
||||
&tera::Context::default(),
|
||||
Some(payload.to_string()),
|
||||
);
|
||||
assert_eq!(status, 500);
|
||||
|
|
@ -172,7 +183,7 @@ mod tests {
|
|||
fn render_empty() {
|
||||
let (body, status) = render(
|
||||
"R8D1pxwHZDxcH5SMjR7rZEnIzmpkiHkH",
|
||||
&tera::Context::new(),
|
||||
&tera::Context::default(),
|
||||
None,
|
||||
);
|
||||
assert_eq!(status, 500);
|
||||
|
|
@ -182,7 +193,7 @@ mod tests {
|
|||
#[test]
|
||||
fn render_not_found() {
|
||||
let payload = "OL6kb9qHe7Iwr7wFIRKUTeFhF34BRsQo";
|
||||
let (body, status) = render(payload, &tera::Context::new(), None);
|
||||
let (body, status) = render(payload, &tera::Context::default(), None);
|
||||
|
||||
assert!(body.matches("TemplateNotFound").count() > 0);
|
||||
assert!(body.matches(payload).count() > 0);
|
||||
|
|
@ -191,7 +202,8 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn render_bad_context() {
|
||||
let (body, status) = render("node.html", &tera::Context::new(), None);
|
||||
let (body, status) =
|
||||
render("node.html", &tera::Context::default(), None);
|
||||
assert!(body.matches("Template render failed.").count() > 0);
|
||||
assert_eq!(status, 500);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::path::PathBuf;
|
|||
|
||||
use crate::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Arguments {
|
||||
pub hostname: String,
|
||||
pub port: u16,
|
||||
|
|
@ -14,19 +14,21 @@ impl Arguments {
|
|||
format!("{}:{}", self.hostname, self.port)
|
||||
}
|
||||
|
||||
pub fn new() -> Arguments {
|
||||
#[must_use]
|
||||
pub fn parse(&self) -> Arguments {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse(self, &args)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Arguments {
|
||||
fn default() -> Arguments {
|
||||
Arguments {
|
||||
hostname: String::from("0.0.0.0"),
|
||||
port: 0,
|
||||
graph_path: PathBuf::from("./static/graph.toml"),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn parse(&self) -> Arguments {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
parse(self, &args)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(defaults: &Arguments, args: &[String]) -> Arguments {
|
||||
|
|
@ -67,7 +69,7 @@ mod tests {
|
|||
let args = Arguments {
|
||||
hostname: String::from("localhost"),
|
||||
port: 3007,
|
||||
graph_path: PathBuf::new(),
|
||||
graph_path: PathBuf::default(),
|
||||
};
|
||||
|
||||
assert_eq!(args.make_address(), "localhost:3007");
|
||||
|
|
@ -75,7 +77,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hostname() {
|
||||
let defaults = Arguments::new();
|
||||
let defaults = Arguments::default();
|
||||
|
||||
let payload = String::from("olUCu7vWcUAsumv2xpj2Z55EDheWLTEu");
|
||||
let args =
|
||||
|
|
@ -85,7 +87,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn port() {
|
||||
let defaults = Arguments::new();
|
||||
let defaults = Arguments::default();
|
||||
|
||||
let payload = 3901;
|
||||
let args = parse(&defaults, &[String::from("-p"), payload.to_string()]);
|
||||
|
|
@ -94,7 +96,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn graph_path() {
|
||||
let defaults = Arguments::new();
|
||||
let defaults = Arguments::default();
|
||||
|
||||
let payload = PathBuf::from("/tmp/");
|
||||
let args = parse(
|
||||
|
|
@ -106,7 +108,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn empty() {
|
||||
let defaults = Arguments::new();
|
||||
let defaults = Arguments::default();
|
||||
|
||||
let args = parse(&defaults, &[]);
|
||||
assert_eq!(defaults, args);
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const LEXMAP: LexMap = &[
|
|||
];
|
||||
|
||||
fn lex(text: &str, map: LexMap, config: &Config) -> Vec<Token> {
|
||||
let mut tokens: Vec<Token> = Vec::new();
|
||||
let mut tokens: Vec<Token> = Vec::default();
|
||||
let mut state = state::State::default();
|
||||
|
||||
let segments = segment::segment(text);
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ impl Lexeme {
|
|||
let mut iterator = raw_strings.iter().peekable();
|
||||
|
||||
while let Some(raw) = iterator.next() {
|
||||
let mut next = String::new();
|
||||
let mut next = String::default();
|
||||
let mut last = false;
|
||||
if let Some(peeked) = iterator.peek() {
|
||||
next.clone_from(*peeked);
|
||||
|
|
|
|||
|
|
@ -33,20 +33,20 @@ pub struct AnchorBuffer {
|
|||
impl AnchorBuffer {
|
||||
pub fn clear(&mut self) {
|
||||
self.candidate = Anchor::default();
|
||||
self.text = String::new();
|
||||
self.destination = String::new();
|
||||
self.text = String::default();
|
||||
self.destination = String::default();
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AnchorBuffer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let display_text = if self.text.is_empty() {
|
||||
String::new()
|
||||
String::default()
|
||||
} else {
|
||||
format!("text: {:?}", self.text)
|
||||
};
|
||||
let display_destination = if self.destination.is_empty() {
|
||||
String::new()
|
||||
String::default()
|
||||
} else {
|
||||
format!(", dest: {:?}", self.destination)
|
||||
};
|
||||
|
|
@ -69,13 +69,13 @@ impl Default for State {
|
|||
inline: Inline::None,
|
||||
block: Block::None,
|
||||
},
|
||||
dom_ids: HashMap::new(),
|
||||
dom_ids: HashMap::default(),
|
||||
switches: Switches { oblique: false },
|
||||
buffers: Buffers {
|
||||
anchor: AnchorBuffer {
|
||||
candidate: Anchor::default(),
|
||||
text: String::new(),
|
||||
destination: String::new(),
|
||||
text: String::default(),
|
||||
destination: String::default(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ impl std::fmt::Display for Anchor {
|
|||
None => "<unknown>",
|
||||
};
|
||||
|
||||
let mut tail = String::new();
|
||||
let mut tail = String::default();
|
||||
|
||||
if self.leading {
|
||||
tail.push_str(" [Leading]");
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ impl std::fmt::Display for Header {
|
|||
|
||||
let display_dom_id = match self.dom_id {
|
||||
Some(ref dom_id) => format!(" DOM ID {dom_id}"),
|
||||
None => String::new(),
|
||||
None => String::default(),
|
||||
};
|
||||
|
||||
write!(
|
||||
|
|
@ -193,7 +193,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn make_id() {
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::default();
|
||||
let id = Header::make_id(
|
||||
&Config::default(),
|
||||
&Lexeme::new("##", "Title"),
|
||||
|
|
@ -204,33 +204,33 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn ascii_ids_set() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.ascii_dom_ids = true;
|
||||
|
||||
let id = Header::make_id(
|
||||
&config,
|
||||
&Lexeme::new("##", "駄目!"),
|
||||
&mut HashMap::new(),
|
||||
&mut HashMap::default(),
|
||||
);
|
||||
assert_eq!(id, "h");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ascii_ids_unset() {
|
||||
let mut config = Config::new();
|
||||
let mut config = Config::default();
|
||||
config.ascii_dom_ids = false;
|
||||
|
||||
let id = Header::make_id(
|
||||
&config,
|
||||
&Lexeme::new("##", "駄目!"),
|
||||
&mut HashMap::new(),
|
||||
&mut HashMap::default(),
|
||||
);
|
||||
assert_eq!(id, "駄目!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn id_deduplication() {
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
let mut map: HashMap<String, Vec<String>> = HashMap::default();
|
||||
let config = Config::default();
|
||||
let id =
|
||||
Header::make_id(&config, &Lexeme::new("##", "UVrcCUjoQ"), &mut map);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
};
|
||||
|
||||
pub fn populate_graph() -> Graph {
|
||||
let args = Arguments::new().parse();
|
||||
let args = Arguments::default().parse();
|
||||
let toml_source = match std::fs::read_to_string(args.graph_path) {
|
||||
Ok(s) => s,
|
||||
Err(e) => format!("Error: {e}"),
|
||||
|
|
@ -32,7 +32,7 @@ fn modulate_graph(graph: &Graph) -> Graph {
|
|||
}
|
||||
|
||||
fn modulate_nodes(old_nodes: &HashMap<String, Node>) -> HashMap<String, Node> {
|
||||
let mut nodes: HashMap<String, Node> = HashMap::new();
|
||||
let mut nodes: HashMap<String, Node> = HashMap::default();
|
||||
|
||||
for (key, node) in old_nodes {
|
||||
let connections = node.connections.clone().unwrap_or_default();
|
||||
|
|
@ -61,7 +61,7 @@ fn modulate_nodes(old_nodes: &HashMap<String, Node>) -> HashMap<String, Node> {
|
|||
new_edges.push(Edge {
|
||||
from: key.clone(),
|
||||
to: link.clone(),
|
||||
anchor: String::new(),
|
||||
anchor: String::default(),
|
||||
detached: !old_nodes.contains_key(link),
|
||||
});
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ pub fn deserialize_graph(in_format: &Format, serial: &str) -> Graph {
|
|||
|
||||
// Construct a HashMap with incoming connections (reversed edges)
|
||||
fn make_incoming(nodes: &HashMap<String, Node>) -> HashMap<String, Vec<Edge>> {
|
||||
let mut incoming: HashMap<String, Vec<Edge>> = HashMap::new();
|
||||
let mut incoming: HashMap<String, Vec<Edge>> = HashMap::default();
|
||||
|
||||
for node in nodes.clone().into_values() {
|
||||
let empty_vec: Vec<Edge> = vec![];
|
||||
|
|
@ -137,7 +137,7 @@ fn make_incoming(nodes: &HashMap<String, Node>) -> HashMap<String, Vec<Edge>> {
|
|||
fn map_lowercase_keys(
|
||||
source_map: &HashMap<String, Node>,
|
||||
) -> HashMap<String, String> {
|
||||
let mut out_map: HashMap<String, String> = HashMap::new();
|
||||
let mut out_map: HashMap<String, String> = HashMap::default();
|
||||
let keys = source_map.keys();
|
||||
for key in keys {
|
||||
out_map.insert(key.clone().to_lowercase(), key.clone());
|
||||
|
|
@ -183,12 +183,12 @@ mod tests {
|
|||
let mut node = Node::new(None);
|
||||
node.connections = Some(vec![Edge {
|
||||
anchor: String::from("SomeAnchor"),
|
||||
from: String::new(),
|
||||
to: String::new(),
|
||||
from: String::default(),
|
||||
to: String::default(),
|
||||
detached: false,
|
||||
}]);
|
||||
|
||||
let mut map: HashMap<String, Node> = HashMap::new();
|
||||
let mut map: HashMap<String, Node> = HashMap::default();
|
||||
map.insert(String::from("SomeNode"), node);
|
||||
|
||||
let modulated_map = modulate_nodes(&map);
|
||||
|
|
|
|||
46
src/types.rs
46
src/types.rs
|
|
@ -60,7 +60,7 @@ fn mkversion() -> (u8, u8, u8) {
|
|||
(0, 0, 0)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq, Debug)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]
|
||||
pub struct Config {
|
||||
#[serde(default)]
|
||||
_private: bool,
|
||||
|
|
@ -118,12 +118,12 @@ fn mk8() -> u16 {
|
|||
impl Graph {
|
||||
pub fn new(message: Option<&str>) -> Graph {
|
||||
Graph {
|
||||
nodes: HashMap::new(),
|
||||
nodes: HashMap::default(),
|
||||
root_node: "VoidNode".to_string(),
|
||||
incoming: HashMap::new(),
|
||||
lowercase_keymap: HashMap::new(),
|
||||
incoming: HashMap::default(),
|
||||
lowercase_keymap: HashMap::default(),
|
||||
meta: Meta {
|
||||
config: Config::new(),
|
||||
config: Config::default(),
|
||||
version: (0, 1, 0),
|
||||
messages: message.map_or(vec![], |m| vec![m.to_string()]),
|
||||
},
|
||||
|
|
@ -155,24 +155,35 @@ impl Node {
|
|||
},
|
||||
connections: None,
|
||||
links: vec![],
|
||||
redirect: String::new(),
|
||||
redirect: String::default(),
|
||||
hidden: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn new() -> Config {
|
||||
#[must_use]
|
||||
pub fn parse_text(self) -> Config {
|
||||
Config {
|
||||
footer_text: content::parse(&self.footer_text, &self),
|
||||
about_text: content::parse(&self.about_text, &self),
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
_private: true,
|
||||
site_title: String::new(),
|
||||
site_description: String::new(),
|
||||
site_title: String::default(),
|
||||
site_description: String::default(),
|
||||
footer: true,
|
||||
footer_credits: true,
|
||||
footer_date: true,
|
||||
footer_text: String::new(),
|
||||
footer_text: String::default(),
|
||||
about: true,
|
||||
about_text: String::new(),
|
||||
about_text: String::default(),
|
||||
tree: true,
|
||||
raw: true,
|
||||
raw_toml: true,
|
||||
|
|
@ -183,16 +194,7 @@ impl Config {
|
|||
index_root_node: true,
|
||||
tree_node_text: false,
|
||||
ascii_dom_ids: false,
|
||||
content_language: String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn parse_text(self) -> Config {
|
||||
Config {
|
||||
footer_text: content::parse(&self.footer_text, &self),
|
||||
about_text: content::parse(&self.about_text, &self),
|
||||
..self
|
||||
content_language: String::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,7 +227,7 @@ mod tests {
|
|||
let default_graph = populate_graph();
|
||||
|
||||
let config = Config {
|
||||
footer_text: String::new(),
|
||||
footer_text: String::default(),
|
||||
..default_graph.meta.config
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue