How to turn the following macro into a variadic: - rust

I wrote this code:
//Amazing log function that can take up to three parameters!!!
macro_rules! log {
// todo variadic
() => {
let string = " ";
info!(" ");
};
($a:expr) => {
let string = format!("{}", format_args!("{}", $a));
info!("{}", string);
};
($a:expr,$b:expr) => {
let string = format!("{}", format_args!("{}{}", $a, $b));
info!("{}", string);
};
($a:expr,$b:expr,$c:expr) => {
let string = format!("{}", format_args!("{}{}{}", $a, $b, $c));
info!("{}", string);
};
}
Example usage:
let number = 163.19;
log!("Heeey fam is this number right: ", number, ". No, it's wrong");
//output: [TIME INFO project] Hey fam is this number correct: 163.19. No, it's wrong
I want it to be able to take any number of parameters is that possible in rust?

This is it.
macro_rules! log {
($($args:expr),*) => {
let mut result: String = String::from("");
$(
let tempstr: String = format!("{}", format_args!("{}", $args));
result.push_str(&tempstr[..]);
)*
info!("{}", result);
};
}

Related

How to handle String.parse<_> ParseIntError [rust]

I finished converting the Rust books guessing game example to an Iced GUI application and wanted to handle error handling for the input from the user.
I have a String trying to convert to an i32 and am not sure how to handle the error if the user puts a String in the text_input or just hits return. I figured out a simple solution of:
self.hidden_compare = self.user_guess_text.trim().parse::<i32>().unwrap_or(0);
Rather than having self.hidden_compare default to 0. I would rather have self.user_guess_text default to a String I have set to earlier in the application and am unsure of how to accomplish this still being fairly new.
Edit: Full function added for clarification.
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::BtnGuessNow => {
self.hidden_compare = self.user_guess_text.trim().parse::<i32>().unwrap_or(0);
if self.hidden_value == self.hidden_compare {
self.label_compare = String::from("A WINNER!");
self.number_ofguesses.push(self.hidden_compare.to_string() + ", A WINNER!");
}
else if self.hidden_value > self.hidden_compare {
self.label_compare = String::from("Too Low");
self.number_ofguesses.push(self.hidden_compare.to_string() + ", Too Low!");
}
else if self.hidden_value < self.hidden_compare {
self.label_compare = String::from("Too Big");
self.number_ofguesses.push(self.hidden_compare.to_string()+ ", Too Big!");
}
self.user_guess_text = "".to_string();
Command::none()
}
Message::UserInputValueUpdate(x) => {
self.user_guess_text = x;
Command::none()
}
}
and a relevant function that handles the Vec output:
fn guess_output_calc(&self) -> String {
let mut tempoutput = String::new();
for (i, x) in self.number_ofguesses.iter().enumerate().skip(1) {
let guessfmt = String::from(format!("Guess# {} Was: {}\n", i, x));
tempoutput.push_str(&guessfmt);
};
return
I would rather have self.user_guess_text default to a String
I'm not entirely sure what you mean with that, but I interpret this as "I want to set the self.user_guess_text variable to a specific value if it can't be converted to an integer". If this is wrong, then please update your question.
This is how I would approach this (simplified):
fn main() {
let mut user_guess_text = " a ";
match user_guess_text.trim().parse::<i32>() {
Ok(value) => {
println!("Parsed to value: {}", value);
}
Err(_) => {
println!("Unable to parse. Resetting variable.");
user_guess_text = "fallback text!";
}
}
println!("user_guess_text: {}", user_guess_text);
}
Unable to parse. Resetting variable.
user_guess_text: default text!

Match arms have incompatible types. Expected struc `NaiveDate`, found `()`

I'm new to Rust and am trying to wrap my head around error handling.
I'm trying to return error if parsing the date goes wrong, here is the function:
pub fn create_posts(contents: &Vec<String>) -> Result<Vec<Post>, CreatePostError> {
const TITLE_SEP: &str = "Title: ";
const DESC_SEP: &str = "Description: ";
const DATE_SEP: &str = "Date: ";
const TAGS_SEP: &str = "Tags: ";
let mut posts: Vec<Post> = Vec::new();
for entry in contents {
let lines = entry.lines().collect::<Vec<_>>();
let metadata = lines[0].contains(&TITLE_SEP)
&& lines[1].contains(&DESC_SEP)
&& lines[2].contains(&DATE_SEP)
&& lines[3].contains(&TAGS_SEP);
if metadata {
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
let tags: Vec<String> = lines[3][TAGS_SEP.len()..]
.split(", ")
.map(|s| s.to_string())
.collect();
let mut article_content = String::new();
for line in &lines[4..] {
article_content.push_str(line);
article_content.push_str("\n")
}
let post = Post {
title: lines[0][TITLE_SEP.len()..].to_string(),
description: lines[1][DESC_SEP.len()..].to_string(),
date: parsed_date,
tags,
content: article_content,
};
posts.push(post);
} else {
return Err(CreatePostError::MetadataError);
}
}
return Ok(posts);
}
You can see the full code here since i wrote custom errors: link
The problem I'm having is with this part:
let date = &lines[2][DATE_SEP.len()..];
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => parsed_date,
Err(e) => eprintln!("Error: {:?}", CreatePostError::ParseError { inner_err: e }),
};
I'm getting match arms have incompatible types. Expected struct NaiveDate, found ()
Here is my enum and impl for the error:
#[derive(Debug)]
pub enum CreatePostError {
ReadFileError { path: PathBuf },
MetadataError,
ParseError { inner_err: ParseError },
}
impl fmt::Display for CreatePostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::ReadFileError { path } => write!(f, "Error reading file {path:?}"),
Self::MetadataError => write!(f, "Some metadata is missing"),
Self::ParseError { inner_err } => {
write!(f, "Error parsing date: {inner_err}")
}
}
}
}
impl From<chrono::format::ParseError> for CreatePostError {
fn from(e: chrono::format::ParseError) -> Self {
CreatePostError::ParseError { inner_err: e }
}
}
You probably want to have a result here, here is a way to do it:
let parsed_date = match NaiveDate::parse_from_str(date, "%Y-%m-%d") {
Ok(parsed_date) => Ok(parsed_date),
Err(e) => {eprintln!("Error: {:?}", &e);
Err(CreatePostError::ParseError { inner_err: e }}),
}?;
The ? is saying: if the thing before is an error, return it, and if it is Ok, unwrap it.
This pattern is so common that rust's result gives a lot of utilities to make this kind of things easier. Here, the map_err function would make it more straightforward: map_err
see:
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d")
.map_err(|e| {
eprintln!("Error: {:?}", &e);
CreatePostError::ParseError { inner_err: e }})?;
But it is only a matter of preference and it might be a lot to digest if you are just beginning, so you can choose the way that you like the most.

Rust: why can't I pattern match a mut String Option inside a loop?

I have this code here, where I try to extract some text based on a delimiter:
//not working
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8e4e0e8d7b2271b8f8ebd126896236ea
But I get these errors from the compiler
^^ value moved here, in previous iteration of loop
note: these 2 reinitializations might get skipped
What's going on here?
Why can't I pattern match on text? Is the problem that my text variable gets consumed? I can't really understand where and how?
Changing the code to use as_ref() on text fixes the error but I don't understand why this is necessary:
//working
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text.as_ref() {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
If you do not use as_ref, you are moving the object hence consuming it, so it is not available in the next iteration.
You can also match a reference:
match &text {}
Playground
Or you can take the inner value, leaving a None behind, that way you do not drop it. And since you re-assignate it afterwards keeps the same functionality:
const DELIMITERS: [&'static str; 2] = [
"example",
"not-found",
];
fn main() {
let full_text = String::from("This is an example test");
let mut text: Option<String> = None;
for d in DELIMITERS {
let split_res = full_text.split_once(d);
if let Some((_, t0)) = split_res {
let t = t0.to_string();
match text.take() {
None => text = Some(t),
Some(t2) => if t.len() < t2.len() {
text = Some(t);
}
}
}
}
if let Some(t) = text {
println!("{}", t);
}
}
Playground

How to use multiple macros inside macros in Rust?

I’d like to have a code that supposed to achieve this in end.
counters!(NAME1, “unit1”, NAME2, “unit2”)
// generates
const NAME1 = 0;
const NAME2 = 1;
static mut counters = [AtomicUsize::new(0), AtomicUsize::new(0)];
static units = [“unit1”, “unit2”];
So far I’ve been able to create indices names and the array of arbitrary values, but I have troubles combining this things together.
playgound link
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:ident, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($name),+,);
};
}
fn main() {
let arr = counters!(ONE, TWO);
dbg!(arr);
}
This seems to do what you want.
use std::sync::atomic::AtomicUsize;
macro_rules! gen_array {
($out:expr; ) => { $out };
([$($out:tt)*]; $name:literal, $($names:tt)*) => {
gen_array!([$($out)* $name,]; $($names)*)
};
}
macro_rules! gen_vars {
($cnt:expr; ) => {};
($cnt:expr; $name:ident, $($names:tt)*) => {
const $name: usize = $cnt;
gen_vars!($cnt + 1; $($names)*)
};
}
macro_rules! counters {
($($name:ident,$value:literal),+) => {
gen_vars!(0; $($name),+,);
gen_array!([]; $($value),+,);
};
}
fn main() {
counters!(ONE, "unit1", TWO, "unit2");
}
Playground

Reduce code duplication with dynamic if let matching

I have this code below that is repeated in many places in my application.
The only thing that differs on the per-command basis is the if let Role { whatever } = role line
#[command]
pub async fn protect(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let target_tag: String = args.single()?;
let (user_id, guild_id) = msg.get_ids();
let (target_id, _) = LupusCtxHelper::parse_tag_to_target_id(ctx, Tag(target_tag))
.await
.ok_or(MyError)?;
let player = {
let dt = ctx.data.read().await;
dt.get_player(&guild_id, &user_id).await
};
if let Some(p) = player {
// this line is the problem
if let LupusRole::BODYGUARD { .. } = *p.current_role() {
LupusCtxHelper::send_lupus_command(ctx, msg, LupusAction::Protect(target_id)).await?
} else {
msg.channel_id
.say(&ctx.http, "check your role dude")
.await?;
}
}
Ok(())
}
How would you guys suggest I go about cleaning up this code/refactoring in an external function? It seems like I can't pattern match dynamically on the left.
ps: beware that the enum LupusRole has struct values
Example of another file:
#[command]
pub async fn frame(ctx: &Context, msg: &Message, mut args: Args) -> CommandResult {
let target_tag: String = args.single()?;
let (user_id, guild_id) = msg.get_ids();
let (target_id, _) = LupusCtxHelper::parse_tag_to_target_id(ctx, Tag(target_tag))
.await
.ok_or(MyError)?;
let player = {
let dt = ctx.data.read().await;
dt.get_player(&guild_id, &user_id).await
};
if let Some(p) = player {
if let LupusRole::GUFO = *p.current_role() {
LupusCtxHelper::send_lupus_command(ctx, msg, LupusAction::Frame(target_id)).await?
} else {
msg.channel_id
.say(&ctx.http, "fra... ruolo sbagliato")
.await?;
}
}
Ok(())
}

Resources