format code

This commit is contained in:
user622628252416 2024-08-20 17:01:25 +02:00
parent 413f88d145
commit 92e6d264b4
9 changed files with 186 additions and 170 deletions

View File

@ -1,25 +1,25 @@
use crate::client::Client;
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
use crate::commands::tree::{ConsumedArgs, RawArgs};
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
/// todo: implement (so far only own name + @s/@p is implemented)
pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option<String> {
let s = args.pop()?;
match s {
match s {
"@s" if src.is_player() => Some(s.into()),
"@p" if src.is_player() => Some(s.into()),
"@r" => None, // todo: implement random player target selector
"@r" => None, // todo: implement random player target selector
"@a" | "@e" => None, // todo: implement all players target selector
_ => {
// todo: implement any other player than sender
if let Player(client) = src {
if let Some(profile) = &client.gameprofile {
if profile.name == s {
return Some(s.into())
return Some(s.into());
};
};
};
@ -29,8 +29,13 @@ pub fn consume_arg_player(src: &CommandSender, args: &mut RawArgs) -> Option<Str
}
/// todo: implement (so far only own name + @s/@p is implemented)
pub fn parse_arg_player<'a>(src: &'a mut CommandSender, arg_name: &str, consumed_args: &ConsumedArgs) -> Result<&'a mut Client, InvalidTreeError> {
let s = consumed_args.get(arg_name)
pub fn parse_arg_player<'a>(
src: &'a mut CommandSender,
arg_name: &str,
consumed_args: &ConsumedArgs,
) -> Result<&'a mut Client, InvalidTreeError> {
let s = consumed_args
.get(arg_name)
.ok_or(InvalidConsumptionError(None))?
.as_str();
@ -44,11 +49,11 @@ pub fn parse_arg_player<'a>(src: &'a mut CommandSender, arg_name: &str, consumed
if let Player(client) = src {
if let Some(profile) = &client.gameprofile {
if profile.name == s {
return Ok(client)
return Ok(client);
};
};
};
Err(InvalidConsumptionError(Some(s.into())))
}
}
}
}

View File

@ -5,12 +5,14 @@ use pumpkin_text::TextComponent;
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::dispatcher::InvalidTreeError::{InvalidConsumptionError, InvalidRequirementError};
use crate::commands::dispatcher::InvalidTreeError::{
InvalidConsumptionError, InvalidRequirementError,
};
use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
use crate::commands::tree_builder::{argument, require};
use crate::commands::CommandSender;
use crate::commands::CommandSender::Player;
use crate::entity::player::GameMode;
pub(crate) const NAME: &str = "gamemode";
@ -25,76 +27,70 @@ pub fn consume_arg_gamemode(_src: &CommandSender, args: &mut RawArgs) -> Option<
if let Ok(id) = s.parse::<u8>() {
match GameMode::from_u8(id) {
None | Some(GameMode::Undefined) => {},
Some(_) => return Some(s.into())
None | Some(GameMode::Undefined) => {}
Some(_) => return Some(s.into()),
};
};
match GameMode::from_str(s) {
Err(_) | Ok(GameMode::Undefined) => None,
Ok(_) => Some(s.into())
Ok(_) => Some(s.into()),
}
}
pub fn parse_arg_gamemode(consumed_args: &ConsumedArgs) -> Result<GameMode, InvalidTreeError> {
let s = consumed_args.get(ARG_GAMEMODE)
let s = consumed_args
.get(ARG_GAMEMODE)
.ok_or(InvalidConsumptionError(None))?;
if let Ok(id) = s.parse::<u8>() {
match GameMode::from_u8(id) {
None | Some(GameMode::Undefined) => {},
Some(gamemode) => return Ok(gamemode)
None | Some(GameMode::Undefined) => {}
Some(gamemode) => return Ok(gamemode),
};
};
match GameMode::from_str(s) {
Err(_) | Ok(GameMode::Undefined) => Err(InvalidConsumptionError(Some(s.into()))),
Ok(gamemode) => Ok(gamemode)
Ok(gamemode) => Ok(gamemode),
}
}
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(DESCRIPTION).with_child(
require(&|sender| {
sender.permission_lvl() >= 2
}).with_child(
argument(ARG_GAMEMODE, consume_arg_gamemode).with_child(
require(&|sender| sender.is_player())
.execute(&|sender, args| {
require(&|sender| sender.permission_lvl() >= 2).with_child(
argument(ARG_GAMEMODE, consume_arg_gamemode)
.with_child(
require(&|sender| sender.is_player()).execute(&|sender, args| {
let gamemode = parse_arg_gamemode(args)?;
return if let Player(target) = sender {
target.set_gamemode(gamemode);
target.send_system_message(TextComponent::text(
&format!("Game mode was set to {:?}", gamemode)
));
target.send_system_message(TextComponent::text(&format!(
"Game mode was set to {:?}",
gamemode
)));
Ok(())
} else {
Err(InvalidRequirementError)
}
})
).with_child(
argument(ARG_TARGET, consume_arg_player)
.execute(&|sender, args| {
};
}),
)
.with_child(
argument(ARG_TARGET, consume_arg_player).execute(&|sender, args| {
let gamemode = parse_arg_gamemode(args)?;
let target = parse_arg_player(sender, ARG_TARGET, args)?;
target.set_gamemode(gamemode);
target.send_system_message(TextComponent::text(
&format!("Set own game mode to {:?}", gamemode)
));
target.send_system_message(TextComponent::text(&format!(
"Set own game mode to {:?}",
gamemode
)));
Ok(())
})
)
)
}),
),
),
)
}

View File

@ -1,9 +1,9 @@
use pumpkin_text::TextComponent;
use crate::commands::{CommandSender, DISPATCHER, dispatcher_init};
use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
use crate::commands::dispatcher::InvalidTreeError::InvalidConsumptionError;
use crate::commands::dispatcher::{CommandDispatcher, InvalidTreeError};
use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
use crate::commands::tree_builder::argument;
use crate::commands::{dispatcher_init, CommandSender, DISPATCHER};
use pumpkin_text::TextComponent;
pub(crate) const NAME: &str = "help";
pub(crate) const ALIAS: &str = "?";
@ -17,12 +17,19 @@ fn consume_arg_command(_src: &CommandSender, args: &mut RawArgs) -> Option<Strin
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
if dispatcher.commands.contains_key(s) { Some(s.into()) }
else { None }
if dispatcher.commands.contains_key(s) {
Some(s.into())
} else {
None
}
}
fn parse_arg_command<'a>(consumed_args: &'a ConsumedArgs, dispatcher: &'a CommandDispatcher) -> Result<(&'a str, &'a CommandTree<'a>), InvalidTreeError> {
let command_name = consumed_args.get(ARG_COMMAND)
fn parse_arg_command<'a>(
consumed_args: &'a ConsumedArgs,
dispatcher: &'a CommandDispatcher,
) -> Result<(&'a str, &'a CommandTree<'a>), InvalidTreeError> {
let command_name = consumed_args
.get(ARG_COMMAND)
.ok_or(InvalidConsumptionError(None))?;
if let Some(tree) = dispatcher.commands.get::<&str>(&command_name.as_str()) {
@ -33,31 +40,35 @@ fn parse_arg_command<'a>(consumed_args: &'a ConsumedArgs, dispatcher: &'a Comman
}
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(DESCRIPTION).with_child(
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, args| {
CommandTree::new(DESCRIPTION)
.with_child(
argument(ARG_COMMAND, consume_arg_command).execute(&|sender, args| {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
let (name, tree) = parse_arg_command(args, dispatcher)?;
sender.send_message(TextComponent::text(&format!(
"{} - {} Usage:{}",
name,
tree.description,
tree.paths_formatted(name)
)));
Ok(())
}),
)
.execute(&|sender, _args| {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
let (name, tree) = parse_arg_command(args, dispatcher)?;
sender.send_message(
TextComponent::text(
&format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name))
)
);
for (name, tree) in &dispatcher.commands {
sender.send_message(TextComponent::text(&format!(
"{} - {} Usage:{}",
name,
tree.description,
tree.paths_formatted(name)
)));
}
Ok(())
})
).execute(&|sender, _args| {
let dispatcher = DISPATCHER.get_or_init(dispatcher_init);
for (name, tree) in &dispatcher.commands {
sender.send_message(
TextComponent::text(
&format!("{} - {} Usage:{}", name, tree.description, tree.paths_formatted(name))
)
);
};
Ok(())
})
}
}

View File

@ -1,6 +1,6 @@
use pumpkin_text::{color::NamedColor, TextComponent};
use pumpkin_protocol::CURRENT_MC_PROTOCOL;
use crate::server::CURRENT_MC_VERSION;
use pumpkin_protocol::CURRENT_MC_PROTOCOL;
use pumpkin_text::{color::NamedColor, TextComponent};
use crate::commands::tree::CommandTree;
@ -12,7 +12,7 @@ pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(DESCRIPTION).execute(&|sender, _| {
let version = env!("CARGO_PKG_VERSION");
let description = env!("CARGO_PKG_DESCRIPTION");
sender.send_message(TextComponent::text(
&format!("Pumpkin {version}, {description} (Minecraft {CURRENT_MC_VERSION}, Protocol {CURRENT_MC_PROTOCOL})")
).color_named(NamedColor::Green));

View File

@ -7,9 +7,7 @@ const DESCRIPTION: &str = "Stop the server.";
pub(crate) fn init_command_tree<'a>() -> CommandTree<'a> {
CommandTree::new(DESCRIPTION).with_child(
require(&|sender| { sender.permission_lvl() >= 4 })
.execute(&|_sender, _args| {
std::process::exit(0)
})
require(&|sender| sender.permission_lvl() >= 4)
.execute(&|_sender, _args| std::process::exit(0)),
)
}
}

View File

@ -1,12 +1,14 @@
use std::collections::HashMap;
use crate::commands::dispatcher::InvalidTreeError::{
InvalidConsumptionError, InvalidRequirementError,
};
use crate::commands::tree::{CommandTree, ConsumedArgs, NodeType, RawArgs};
use crate::commands::CommandSender;
use crate::commands::dispatcher::InvalidTreeError::{InvalidConsumptionError, InvalidRequirementError};
use crate::commands::tree::{ConsumedArgs, NodeType, RawArgs, CommandTree};
use std::collections::HashMap;
#[derive(Debug)]
pub(crate) enum InvalidTreeError {
/// This error means that there was an error while parsing a previously consumed argument.
/// That only happens when consumption is wrongly implemented, as it should ensure parsing may
/// That only happens when consumption is wrongly implemented, as it should ensure parsing may
/// never fail.
InvalidConsumptionError(Option<String>),
@ -15,15 +17,13 @@ pub(crate) enum InvalidTreeError {
}
pub(crate) struct CommandDispatcher<'a> {
pub(crate) commands: HashMap<&'a str, CommandTree<'a>>
pub(crate) commands: HashMap<&'a str, CommandTree<'a>>,
}
/// Stores registered [CommandTree]s and dispatches commands to them.
impl <'a> CommandDispatcher<'a> {
impl<'a> CommandDispatcher<'a> {
/// Execute a command using its corresponding [CommandTree].
pub(crate) fn dispatch(&'a self, src: &mut CommandSender, cmd: &str) -> Result<(), String> {
let mut parts = cmd.split_ascii_whitespace();
let key = parts.next().ok_or("Empty Command")?;
let raw_args: Vec<&str> = parts.rev().collect();
@ -35,23 +35,32 @@ impl <'a> CommandDispatcher<'a> {
match Self::try_is_fitting_path(src, path, tree, raw_args.clone()) {
Err(InvalidConsumptionError(s)) => {
println!("Error while parsing command \"{cmd}\": {s:?} was consumed, but couldn't be parsed");
return Err("Internal Error (See logs for details)".into())
},
return Err("Internal Error (See logs for details)".into());
}
Err(InvalidRequirementError) => {
println!("Error while parsing command \"{cmd}\": a requirement that was expected was not met.");
return Err("Internal Error (See logs for details)".into())
},
return Err("Internal Error (See logs for details)".into());
}
Ok(is_fitting_path) => {
if is_fitting_path { return Ok(()) }
if is_fitting_path {
return Ok(());
}
}
}
}
Err(format!("Invalid Syntax. Usage:{}", tree.paths_formatted(key)))
Err(format!(
"Invalid Syntax. Usage:{}",
tree.paths_formatted(key)
))
}
fn try_is_fitting_path(src: &mut CommandSender, path: Vec<usize>, tree: &CommandTree, mut raw_args: RawArgs) -> Result<bool, InvalidTreeError> {
fn try_is_fitting_path(
src: &mut CommandSender,
path: Vec<usize>,
tree: &CommandTree,
mut raw_args: RawArgs,
) -> Result<bool, InvalidTreeError> {
let mut parsed_args: ConsumedArgs = HashMap::new();
for node in path.iter().map(|&i| &tree.nodes[i]) {
@ -69,7 +78,11 @@ impl <'a> CommandDispatcher<'a> {
return Ok(false);
}
}
NodeType::Argument { consumer: consume, name, .. } => {
NodeType::Argument {
consumer: consume,
name,
..
} => {
if let Some(consumed) = consume(src, &mut raw_args) {
parsed_args.insert(name, consumed);
} else {
@ -82,8 +95,8 @@ impl <'a> CommandDispatcher<'a> {
}
}
}
};
}
Ok(false)
}
}
}

View File

@ -1,17 +1,17 @@
use pumpkin_text::TextComponent;
use std::collections::HashMap;
use std::sync::OnceLock;
use pumpkin_text::TextComponent;
use crate::client::Client;
use crate::commands::dispatcher::CommandDispatcher;
mod arg_player;
mod cmd_gamemode;
mod cmd_help;
mod cmd_pumpkin;
mod cmd_stop;
mod dispatcher;
mod tree;
mod tree_builder;
mod dispatcher;
mod arg_player;
mod cmd_help;
pub enum CommandSender<'a> {
Rcon(&'a mut Vec<String>),
@ -78,9 +78,7 @@ fn dispatcher_init<'a>() -> CommandDispatcher<'a> {
map.insert(cmd_help::NAME, cmd_help::init_command_tree());
map.insert(cmd_help::ALIAS, cmd_help::init_command_tree());
CommandDispatcher {
commands: map,
}
CommandDispatcher { commands: map }
}
pub fn handle_command(sender: &mut CommandSender, cmd: &str) {
@ -88,8 +86,7 @@ pub fn handle_command(sender: &mut CommandSender, cmd: &str) {
if let Err(err) = dispatcher.dispatch(sender, cmd) {
sender.send_message(
TextComponent::text(&err)
.color_named(pumpkin_text::color::NamedColor::Red),
TextComponent::text(&err).color_named(pumpkin_text::color::NamedColor::Red),
)
}
}

View File

@ -1,7 +1,7 @@
use std::collections::{HashMap, VecDeque};
use crate::commands::CommandSender;
use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::CommandSender;
/// see [crate::commands::tree_builder::argument]
pub(crate) type RawArgs<'a> = Vec<&'a str>;
@ -21,16 +21,14 @@ pub(crate) enum NodeType<'a> {
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
},
#[allow(dead_code)] // todo: remove (so far no commands requiring this are implemented)
Literal {
string: &'a str,
},
Literal { string: &'a str },
Argument {
name: &'a str,
consumer: ArgumentConsumer<'a>,
},
Require {
predicate: &'a (dyn Fn(&CommandSender) -> bool + Sync),
}
},
}
pub(crate) struct CommandTree<'a> {
@ -39,7 +37,7 @@ pub(crate) struct CommandTree<'a> {
pub(crate) description: &'a str,
}
impl <'a> CommandTree<'a> {
impl<'a> CommandTree<'a> {
/// iterate over all possible paths that end in a [NodeType::ExecuteLeaf]
pub(crate) fn iter_paths(&'a self) -> impl Iterator<Item = Vec<usize>> + 'a {
let mut todo = VecDeque::<(usize, usize)>::new();
@ -54,25 +52,29 @@ impl <'a> CommandTree<'a> {
}
}
/// format possible paths as [String], using ```name``` as the command name
pub(crate) fn paths_formatted(&'a self, name: &str) -> String {
let paths: Vec<Vec<&NodeType>> = self.iter_paths()
let paths: Vec<Vec<&NodeType>> = self
.iter_paths()
.map(|path| path.iter().map(|&i| &self.nodes[i].node_type).collect())
.collect();
let len = paths.iter()
.map(|path| path.iter()
.map(|node| match node {
NodeType::ExecuteLeaf { .. } => 0,
NodeType::Literal { string } => string.len() + 1,
NodeType::Argument { name, .. } => name.len() + 3,
NodeType::Require { .. } => 0,
})
.sum::<usize>() + name.len() + 2
)
let len = paths
.iter()
.map(|path| {
path.iter()
.map(|node| match node {
NodeType::ExecuteLeaf { .. } => 0,
NodeType::Literal { string } => string.len() + 1,
NodeType::Argument { name, .. } => name.len() + 3,
NodeType::Require { .. } => 0,
})
.sum::<usize>()
+ name.len()
+ 2
})
.sum::<usize>();
let mut s = String::with_capacity(len);
for path in paths.iter() {
@ -95,7 +97,7 @@ impl <'a> CommandTree<'a> {
}
}
}
s
}
}
@ -107,8 +109,7 @@ struct TraverseAllPathsIter<'a> {
todo: VecDeque<(usize, usize)>,
}
impl <'a>Iterator for TraverseAllPathsIter<'a> {
impl<'a> Iterator for TraverseAllPathsIter<'a> {
type Item = Vec<usize>;
fn next(&mut self) -> Option<Self::Item> {
@ -118,12 +119,15 @@ impl <'a>Iterator for TraverseAllPathsIter<'a> {
// add new children to front
self.todo.reserve(node.children.len());
node.children.iter().rev().for_each(|&c| self.todo.push_front((depth + 1, c)));
node.children
.iter()
.rev()
.for_each(|&c| self.todo.push_front((depth + 1, c)));
// update path
while self.path.len() > depth {
self.path.pop();
};
}
self.path.push(i);
if let NodeType::ExecuteLeaf { .. } = node.node_type {
@ -132,4 +136,3 @@ impl <'a>Iterator for TraverseAllPathsIter<'a> {
}
}
}

View File

@ -1,9 +1,8 @@
use crate::commands::CommandSender;
use crate::commands::dispatcher::InvalidTreeError;
use crate::commands::tree::{ArgumentConsumer, ConsumedArgs, Node, NodeType, CommandTree};
impl <'a> CommandTree<'a> {
use crate::commands::tree::{ArgumentConsumer, CommandTree, ConsumedArgs, Node, NodeType};
use crate::commands::CommandSender;
impl<'a> CommandTree<'a> {
/// Add a child [Node] to the root of this [CommandTree].
pub fn with_child(mut self, child: impl NodeBuilder<'a>) -> Self {
let node = child.build(&mut self);
@ -11,7 +10,7 @@ impl <'a> CommandTree<'a> {
self.nodes.push(node);
self
}
pub fn new(description: &'a str) -> Self {
Self {
nodes: Vec::new(),
@ -27,11 +26,12 @@ impl <'a> CommandTree<'a> {
/// desired type.
///
/// Also see [NonLeafNodeBuilder::execute].
pub fn execute(mut self, run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync)) -> Self {
pub fn execute(
mut self,
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
) -> Self {
let node = Node {
node_type: NodeType::ExecuteLeaf {
run,
},
node_type: NodeType::ExecuteLeaf { run },
children: Vec::new(),
};
@ -50,7 +50,7 @@ struct LeafNodeBuilder<'a> {
node_type: NodeType<'a>,
}
impl <'a>NodeBuilder<'a> for LeafNodeBuilder<'a> {
impl<'a> NodeBuilder<'a> for LeafNodeBuilder<'a> {
fn build(self, _tree: &mut CommandTree<'a>) -> Node<'a> {
Node {
children: Vec::new(),
@ -62,10 +62,10 @@ impl <'a>NodeBuilder<'a> for LeafNodeBuilder<'a> {
pub struct NonLeafNodeBuilder<'a> {
node_type: NodeType<'a>,
child_nodes: Vec<NonLeafNodeBuilder<'a>>,
leaf_nodes: Vec<LeafNodeBuilder<'a>>
leaf_nodes: Vec<LeafNodeBuilder<'a>>,
}
impl <'a>NodeBuilder<'a> for NonLeafNodeBuilder<'a> {
impl<'a> NodeBuilder<'a> for NonLeafNodeBuilder<'a> {
fn build(self, tree: &mut CommandTree<'a>) -> Node<'a> {
let mut child_indices = Vec::new();
@ -88,8 +88,7 @@ impl <'a>NodeBuilder<'a> for NonLeafNodeBuilder<'a> {
}
}
impl <'a>NonLeafNodeBuilder<'a> {
impl<'a> NonLeafNodeBuilder<'a> {
/// Add a child [Node] to this one.
pub fn with_child(mut self, child: NonLeafNodeBuilder<'a>) -> Self {
self.child_nodes.push(child);
@ -103,13 +102,14 @@ impl <'a>NonLeafNodeBuilder<'a> {
/// desired type.
///
/// Also see [CommandTree::execute].
pub fn execute(mut self, run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync)) -> Self {
pub fn execute(
mut self,
run: &'a (dyn Fn(&mut CommandSender, &ConsumedArgs) -> Result<(), InvalidTreeError> + Sync),
) -> Self {
self.leaf_nodes.push(LeafNodeBuilder {
node_type: NodeType::ExecuteLeaf {
run
},
node_type: NodeType::ExecuteLeaf { run },
});
self
}
}
@ -118,9 +118,7 @@ impl <'a>NonLeafNodeBuilder<'a> {
#[allow(dead_code)] // todo: remove (so far no commands requiring this are implemented)
pub fn literal(string: &str) -> NonLeafNodeBuilder {
NonLeafNodeBuilder {
node_type: NodeType::Literal {
string
},
node_type: NodeType::Literal { string },
child_nodes: Vec::new(),
leaf_nodes: Vec::new(),
}
@ -136,10 +134,7 @@ pub fn literal(string: &str) -> NonLeafNodeBuilder {
/// reversed, so [Vec::pop] can be used to obtain args in ltr order.
pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBuilder<'a> {
NonLeafNodeBuilder {
node_type: NodeType::Argument {
name,
consumer,
},
node_type: NodeType::Argument { name, consumer },
child_nodes: Vec::new(),
leaf_nodes: Vec::new(),
}
@ -149,9 +144,7 @@ pub fn argument<'a>(name: &'a str, consumer: ArgumentConsumer) -> NonLeafNodeBui
/// met.
pub fn require(predicate: &(dyn Fn(&CommandSender) -> bool + Sync)) -> NonLeafNodeBuilder {
NonLeafNodeBuilder {
node_type: NodeType::Require {
predicate
},
node_type: NodeType::Require { predicate },
child_nodes: Vec::new(),
leaf_nodes: Vec::new(),
}