cargo clippy

This commit is contained in:
Dylan Knutson
2025-08-29 10:21:39 -07:00
parent 34de9b6d59
commit d8e6c4ef3e
18 changed files with 106 additions and 112 deletions

View File

@@ -2,9 +2,7 @@ use crate::{
case, case,
db::{Listing, ListingDAO, ListingId, User, UserDAO}, db::{Listing, ListingDAO, ListingId, User, UserDAO},
keyboard_buttons, keyboard_buttons,
message_utils::{ message_utils::{extract_callback_data, pluralize_with_count, send_message, MessageTarget},
extract_callback_data, pluralize_with_count, send_message, HandleAndId, MessageTarget,
},
Command, DialogueRootState, HandlerResult, RootDialogue, Command, DialogueRootState, HandlerResult, RootDialogue,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -12,7 +10,7 @@ use sqlx::SqlitePool;
use teloxide::{ use teloxide::{
dispatching::{DpHandlerDescription, UpdateFilterExt}, dispatching::{DpHandlerDescription, UpdateFilterExt},
prelude::*, prelude::*,
types::{InlineKeyboardButton, Message, MessageId, ParseMode}, types::{InlineKeyboardButton, Message},
Bot, Bot,
}; };
@@ -183,7 +181,7 @@ async fn show_listing_details(
); );
send_message( send_message(
&bot, bot,
target, target,
response, response,
Some(ManageListingButtons::to_keyboard()), Some(ManageListingButtons::to_keyboard()),
@@ -233,7 +231,7 @@ async fn get_user_and_listing(
listing_id: ListingId, listing_id: ListingId,
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult<(User, Listing)> { ) -> HandlerResult<(User, Listing)> {
let user = match UserDAO::find_by_telegram_id(&db_pool, user_id).await? { let user = match UserDAO::find_by_telegram_id(db_pool, user_id).await? {
Some(user) => user, Some(user) => user,
None => { None => {
send_message( send_message(
@@ -247,7 +245,7 @@ async fn get_user_and_listing(
} }
}; };
let listing = match ListingDAO::find_by_id(&db_pool, listing_id).await? { let listing = match ListingDAO::find_by_id(db_pool, listing_id).await? {
Some(listing) => listing, Some(listing) => listing,
None => { None => {
send_message(bot, target, "❌ Listing not found.", None).await?; send_message(bot, target, "❌ Listing not found.", None).await?;

View File

@@ -9,7 +9,6 @@ use crate::{
models::new_listing::{NewListing, NewListingBase, NewListingFields}, models::new_listing::{NewListing, NewListingBase, NewListingFields},
ListingDuration, NewUser, UserDAO, ListingDuration, NewUser, UserDAO,
}, },
keyboard_buttons,
message_utils::*, message_utils::*,
DialogueRootState, HandlerResult, RootDialogue, DialogueRootState, HandlerResult, RootDialogue,
}; };
@@ -35,8 +34,8 @@ fn create_back_button_keyboard_with_clear(field: &str) -> InlineKeyboardMarkup {
create_single_row_keyboard(&[ create_single_row_keyboard(&[
("🔙 Back", "edit_back"), ("🔙 Back", "edit_back"),
( (
&format!("🧹 Clear {}", field), &format!("🧹 Clear {field}"),
&format!("edit_clear_{}", field), &format!("edit_clear_{field}"),
), ),
]) ])
} }
@@ -190,7 +189,7 @@ async fn handle_description_callback(
send_message(&bot, target, response, None).await?; send_message(&bot, target, response, None).await?;
} }
_ => { _ => {
error!("Unknown callback data: {}", data); error!("Unknown callback data: {data}");
dialogue.exit().await?; dialogue.exit().await?;
} }
} }
@@ -205,7 +204,7 @@ async fn handle_awaiting_draft_field_callback(
callback_query: CallbackQuery, callback_query: CallbackQuery,
) -> HandlerResult { ) -> HandlerResult {
let (data, from, message_id) = extract_callback_data(&bot, callback_query).await?; let (data, from, message_id) = extract_callback_data(&bot, callback_query).await?;
info!("User {:?} selected callback: {:?}", from, data); info!("User {from:?} selected callback: {data:?}");
let target = (from, message_id); let target = (from, message_id);
if data == "cancel" { if data == "cancel" {
@@ -214,17 +213,17 @@ async fn handle_awaiting_draft_field_callback(
match field { match field {
ListingField::Title => { ListingField::Title => {
error!("Unknown callback data: {}", data); error!("Unknown callback data: {data}");
dialogue.exit().await?; dialogue.exit().await?;
return Ok(()); Ok(())
} }
ListingField::Description => { ListingField::Description => {
handle_description_callback(bot, dialogue, draft, data.as_str(), target).await handle_description_callback(bot, dialogue, draft, data.as_str(), target).await
} }
ListingField::Price => { ListingField::Price => {
error!("Unknown callback data: {}", data); error!("Unknown callback data: {data}");
dialogue.exit().await?; dialogue.exit().await?;
return Ok(()); Ok(())
} }
ListingField::Slots => { ListingField::Slots => {
handle_slots_callback(bot, dialogue, draft, data.as_str(), target).await handle_slots_callback(bot, dialogue, draft, data.as_str(), target).await
@@ -303,7 +302,7 @@ async fn process_slots_and_respond(
); );
send_message( send_message(
&bot, bot,
target, target,
&response, &response,
Some(StartTimeKeyboardButtons::to_keyboard()), Some(StartTimeKeyboardButtons::to_keyboard()),
@@ -328,12 +327,12 @@ async fn handle_viewing_draft_callback(
match button { match button {
ConfirmationKeyboardButtons::Create => { ConfirmationKeyboardButtons::Create => {
info!("User {:?} confirmed listing creation", target); info!("User {target:?} confirmed listing creation");
dialogue.exit().await?; dialogue.exit().await?;
create_listing(db_pool, bot, dialogue, from, message_id, draft.clone()).await?; create_listing(db_pool, bot, dialogue, from, message_id, draft.clone()).await?;
} }
ConfirmationKeyboardButtons::Discard => { ConfirmationKeyboardButtons::Discard => {
info!("User {:?} discarded listing creation", from); info!("User {from:?} discarded listing creation");
// Exit dialogue and send cancellation message // Exit dialogue and send cancellation message
dialogue.exit().await?; dialogue.exit().await?;
@@ -345,7 +344,7 @@ async fn handle_viewing_draft_callback(
send_message(&bot, target, &response, None).await?; send_message(&bot, target, &response, None).await?;
} }
ConfirmationKeyboardButtons::Edit => { ConfirmationKeyboardButtons::Edit => {
info!("User {:?} chose to edit listing", from); info!("User {from:?} chose to edit listing");
// Go to editing state to allow user to modify specific fields // Go to editing state to allow user to modify specific fields
dialogue dialogue
@@ -379,18 +378,17 @@ async fn process_start_time_and_respond(
.await?; .await?;
// Generate response message // Generate response message
let start_msg = format!("in {}", duration); let start_msg = format!("in {duration}");
let response = format!( let response = format!(
"✅ Listing will start: <b>{}</b>\n\n\ "✅ Listing will start: <b>{start_msg}</b>\n\n\
<i>Step 6 of 6: Duration</i>\n\ <i>Step 6 of 6: Duration</i>\n\
How long should your listing run?\n\ How long should your listing run?\n\
Enter duration in hours (minimum 1 hour, maximum 720 hours / 30 days):", Enter duration in hours (minimum 1 hour, maximum 720 hours / 30 days):"
start_msg
); );
send_message( send_message(
&bot, bot,
target, target,
&response, &response,
Some(DurationKeyboardButtons::to_keyboard()), Some(DurationKeyboardButtons::to_keyboard()),
@@ -606,7 +604,7 @@ async fn show_edit_screen(
); );
if let Some(flash_message) = flash_message { if let Some(flash_message) = flash_message {
response = format!("{}\n\n{}", flash_message, response); response = format!("{flash_message}\n\n{response}");
} }
send_message( send_message(
@@ -629,7 +627,7 @@ async fn handle_editing_field_input(
let chat = msg.chat.clone(); let chat = msg.chat.clone();
let text = msg.text().unwrap_or("").trim(); let text = msg.text().unwrap_or("").trim();
info!("User {:?} editing field {:?}", chat, field); info!("User {chat:?} editing field {field:?}");
match field { match field {
ListingField::Title => { ListingField::Title => {
@@ -728,10 +726,9 @@ async fn handle_editing_draft_callback(
// update the message to show the edit screen // update the message to show the edit screen
let response = format!( let response = format!(
"Editing {:?}\n\n\ "Editing {field:?}\n\n\
Previous value: {}\ Previous value: {value}\
", "
field, value
); );
send_message(&bot, target, response, Some(keyboard)).await?; send_message(&bot, target, response, Some(keyboard)).await?;
@@ -751,7 +748,7 @@ async fn create_listing(
let starts_at = now + Into::<Duration>::into(draft.start_delay); let starts_at = now + Into::<Duration>::into(draft.start_delay);
let ends_at = starts_at + Into::<Duration>::into(draft.duration); let ends_at = starts_at + Into::<Duration>::into(draft.duration);
let user = match UserDAO::find_by_telegram_id(&db_pool, from.id.clone()).await? { let user = match UserDAO::find_by_telegram_id(&db_pool, from.id).await? {
Some(user) => user, Some(user) => user,
None => { None => {
UserDAO::insert_user( UserDAO::insert_user(
@@ -803,7 +800,7 @@ async fn create_listing(
); );
} }
Err(e) => { Err(e) => {
log::error!("Failed to create listing for user {:?}: {}", from, e); log::error!("Failed to create listing for user {from:?}: {e}");
send_message( send_message(
&bot, &bot,
(from, message_id), (from, message_id),
@@ -823,7 +820,7 @@ async fn cancel_wizard(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("{:?} cancelled new listing wizard", target); info!("{target:?} cancelled new listing wizard");
dialogue.exit().await?; dialogue.exit().await?;
send_message(&bot, target, "❌ Listing creation cancelled.", None).await?; send_message(&bot, target, "❌ Listing creation cancelled.", None).await?;
Ok(()) Ok(())
@@ -838,7 +835,7 @@ async fn handle_edit_title(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing title: '{}'", target, text); info!("User {target:?} editing title: '{text}'");
draft.title = match validate_title(text) { draft.title = match validate_title(text) {
Ok(title) => title, Ok(title) => title,
@@ -873,7 +870,7 @@ async fn handle_edit_description(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing description: '{}'", target, text); info!("User {target:?} editing description: '{text}'");
state.description = match validate_description(text) { state.description = match validate_description(text) {
Ok(description) => Some(description), Ok(description) => Some(description),
@@ -903,7 +900,7 @@ async fn handle_edit_price(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing price: '{}'", target, text); info!("User {target:?} editing price: '{text}'");
state.buy_now_price = match validate_price(text) { state.buy_now_price = match validate_price(text) {
Ok(price) => price, Ok(price) => price,
@@ -932,7 +929,7 @@ async fn handle_edit_slots(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing slots: '{}'", target, text); info!("User {target:?} editing slots: '{text}'");
state.slots_available = match validate_slots(text) { state.slots_available = match validate_slots(text) {
Ok(s) => s, Ok(s) => s,
@@ -962,7 +959,7 @@ async fn handle_edit_start_time(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing start time: '{}'", target, text); info!("User {target:?} editing start time: '{text}'");
state.start_delay = match validate_start_time(text) { state.start_delay = match validate_start_time(text) {
Ok(h) => h, Ok(h) => h,
@@ -998,7 +995,7 @@ async fn handle_edit_duration(
target: impl Into<MessageTarget>, target: impl Into<MessageTarget>,
) -> HandlerResult { ) -> HandlerResult {
let target = target.into(); let target = target.into();
info!("User {:?} editing duration: '{}'", target, text); info!("User {target:?} editing duration: '{text}'");
state.duration = match validate_duration(text) { state.duration = match validate_duration(text) {
Ok(d) => d, Ok(d) => d,

View File

@@ -41,7 +41,7 @@ pub fn validate_price(text: &str) -> Result<MoneyAmount, String> {
pub fn validate_slots(text: &str) -> Result<i32, String> { pub fn validate_slots(text: &str) -> Result<i32, String> {
match text.parse::<i32>() { match text.parse::<i32>() {
Ok(slots) if slots >= 1 && slots <= 1000 => Ok(slots), Ok(slots) if (1..=1000).contains(&slots) => Ok(slots),
Ok(_) => Err( Ok(_) => Err(
"❌ Number of slots must be between 1 and 1000. Please enter a valid number:" "❌ Number of slots must be between 1 and 1000. Please enter a valid number:"
.to_string(), .to_string(),
@@ -52,7 +52,7 @@ pub fn validate_slots(text: &str) -> Result<i32, String> {
pub fn validate_duration(text: &str) -> Result<ListingDuration, String> { pub fn validate_duration(text: &str) -> Result<ListingDuration, String> {
match text.parse::<i32>() { match text.parse::<i32>() {
Ok(hours) if hours >= 1 && hours <= 720 => Ok(ListingDuration::hours(hours)), // 1 hour to 30 days Ok(hours) if (1..=720).contains(&hours) => Ok(ListingDuration::hours(hours)), // 1 hour to 30 days
Ok(_) => Err( Ok(_) => Err(
"❌ Duration must be between 1 and 720 hours. Please enter a valid number:".to_string(), "❌ Duration must be between 1 and 720 hours. Please enter a valid number:".to_string(),
), ),
@@ -62,7 +62,7 @@ pub fn validate_duration(text: &str) -> Result<ListingDuration, String> {
pub fn validate_start_time(text: &str) -> Result<ListingDuration, String> { pub fn validate_start_time(text: &str) -> Result<ListingDuration, String> {
match text.parse::<i32>() { match text.parse::<i32>() {
Ok(hours) if hours >= 0 && hours <= 168 => Ok(ListingDuration::hours(hours)), // Max 1 week delay Ok(hours) if (0..=168).contains(&hours) => Ok(ListingDuration::hours(hours)), // Max 1 week delay
Ok(_) => Err( Ok(_) => Err(
"❌ Start time must be between 0 and 168 hours. Please enter a valid number:" "❌ Start time must be between 0 and 168 hours. Please enter a valid number:"
.to_string(), .to_string(),

View File

@@ -76,7 +76,7 @@ impl Config {
log::info!(" Web Port: {}", self.web_port); log::info!(" Web Port: {}", self.web_port);
if let Some(admin_id) = self.admin_user_id { if let Some(admin_id) = self.admin_user_id {
log::info!(" Admin User ID: {}", admin_id); log::info!(" Admin User ID: {admin_id}");
} else { } else {
log::info!(" Admin User ID: Not set"); log::info!(" Admin User ID: Not set");
} }

View File

@@ -114,7 +114,7 @@ impl ListingDAO {
.fetch_optional(pool) .fetch_optional(pool)
.await?; .await?;
Ok(result.map(Self::row_to_listing).transpose()?) result.map(Self::row_to_listing).transpose()
} }
/// Find all listings by a seller /// Find all listings by a seller
@@ -125,10 +125,10 @@ impl ListingDAO {
.fetch_all(pool) .fetch_all(pool)
.await?; .await?;
Ok(rows rows
.into_iter() .into_iter()
.map(Self::row_to_listing) .map(Self::row_to_listing)
.collect::<Result<Vec<_>>>()?) .collect::<Result<Vec<_>>>()
} }
/// Delete a listing /// Delete a listing

View File

@@ -13,6 +13,7 @@ use crate::db::{
/// Data Access Object for User operations /// Data Access Object for User operations
pub struct UserDAO; pub struct UserDAO;
#[allow(unused)]
impl UserDAO { impl UserDAO {
/// Insert a new user into the database /// Insert a new user into the database
pub async fn insert_user(pool: &SqlitePool, new_user: &NewUser) -> Result<User> { pub async fn insert_user(pool: &SqlitePool, new_user: &NewUser) -> Result<User> {
@@ -131,7 +132,7 @@ impl UserDAO {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::db::models::user::{NewUser, User}; use crate::db::models::user::NewUser;
use rstest::rstest; use rstest::rstest;
use sqlx::SqlitePool; use sqlx::SqlitePool;
use teloxide::types::UserId; use teloxide::types::UserId;

View File

@@ -15,6 +15,7 @@ use chrono::{DateTime, Utc};
/// Main listing/auction entity /// Main listing/auction entity
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)]
pub struct Listing { pub struct Listing {
pub base: ListingBase, pub base: ListingBase,
pub fields: ListingFields, pub fields: ListingFields,
@@ -22,6 +23,7 @@ pub struct Listing {
/// Common fields shared by all listing types /// Common fields shared by all listing types
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)]
pub struct ListingBase { pub struct ListingBase {
pub id: ListingId, pub id: ListingId,
pub seller_id: UserRowId, pub seller_id: UserRowId,
@@ -34,6 +36,7 @@ pub struct ListingBase {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)]
pub enum ListingFields { pub enum ListingFields {
BasicAuction { BasicAuction {
starting_bid: MoneyAmount, starting_bid: MoneyAmount,
@@ -57,6 +60,7 @@ pub enum ListingFields {
}, },
} }
#[allow(unused)]
impl Listing { impl Listing {
/// Get the listing type as an enum value /// Get the listing type as an enum value
pub fn listing_type(&self) -> ListingType { pub fn listing_type(&self) -> ListingType {

View File

@@ -8,10 +8,6 @@ pub mod user;
pub mod user_settings; pub mod user_settings;
// Re-export all types for easy access // Re-export all types for easy access
pub use bid::*;
pub use listing::*; pub use listing::*;
pub use listing_media::*;
pub use listing_type::*; pub use listing_type::*;
pub use proxy_bid::*;
pub use user::*; pub use user::*;
pub use user_settings::*;

View File

@@ -29,6 +29,7 @@ pub struct NewListingBase {
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)]
pub enum NewListingFields { pub enum NewListingFields {
BasicAuction { BasicAuction {
starting_bid: MoneyAmount, starting_bid: MoneyAmount,
@@ -52,6 +53,7 @@ pub enum NewListingFields {
}, },
} }
#[allow(unused)]
impl NewListingBase { impl NewListingBase {
pub fn new( pub fn new(
seller_id: UserRowId, seller_id: UserRowId,

View File

@@ -5,6 +5,7 @@ use crate::db::MoneyAmount;
/// Proxy bid strategies (automatic bidding settings) /// Proxy bid strategies (automatic bidding settings)
#[derive(Debug, Clone, FromRow)] #[derive(Debug, Clone, FromRow)]
#[allow(unused)]
pub struct ProxyBid { pub struct ProxyBid {
pub id: i64, pub id: i64,
pub listing_id: i64, pub listing_id: i64,
@@ -17,6 +18,7 @@ pub struct ProxyBid {
/// New proxy bid data for insertion /// New proxy bid data for insertion
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(unused)]
pub struct NewProxyBid { pub struct NewProxyBid {
pub listing_id: i64, pub listing_id: i64,
pub buyer_id: i64, pub buyer_id: i64,

View File

@@ -5,6 +5,7 @@ use crate::db::{TelegramUserId, UserRowId};
/// Core user information /// Core user information
#[derive(Debug, Clone, FromRow)] #[derive(Debug, Clone, FromRow)]
#[allow(unused)]
pub struct User { pub struct User {
pub id: UserRowId, pub id: UserRowId,
pub telegram_id: TelegramUserId, pub telegram_id: TelegramUserId,

View File

@@ -3,41 +3,38 @@ use sqlx::{
}; };
/// Currency types supported by the platform /// Currency types supported by the platform
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum CurrencyType { pub enum CurrencyType {
USD, #[default]
Usd,
} }
#[allow(unused)]
impl CurrencyType { impl CurrencyType {
/// Get the currency code as a string /// Get the currency code as a string
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
match self { match self {
CurrencyType::USD => "USD", CurrencyType::Usd => "USD",
} }
} }
/// Get the currency symbol /// Get the currency symbol
pub fn symbol(&self) -> &'static str { pub fn symbol(&self) -> &'static str {
match self { match self {
CurrencyType::USD => "$", CurrencyType::Usd => "$",
} }
} }
/// Parse currency from string /// Parse currency from string
pub fn from_str(s: &str) -> Result<Self, String> { pub fn from_str(s: &str) -> Result<Self, String> {
match s.to_uppercase().as_str() { match s.to_uppercase().as_str() {
"USD" => Ok(CurrencyType::USD), "USD" => Ok(CurrencyType::Usd),
_ => Err(format!("Unsupported currency: {}", s)), _ => Err(format!("Unsupported currency: {s}")),
} }
} }
} }
// Implement Default for CurrencyType (defaults to USD) // Implement Default for CurrencyType (defaults to USD)
impl Default for CurrencyType {
fn default() -> Self {
CurrencyType::USD
}
}
// Implement Display for CurrencyType // Implement Display for CurrencyType
impl std::fmt::Display for CurrencyType { impl std::fmt::Display for CurrencyType {
@@ -83,7 +80,7 @@ mod tests {
#[test] #[test]
fn test_currency_type_display() { fn test_currency_type_display() {
let usd = CurrencyType::USD; let usd = CurrencyType::Usd;
assert_eq!(usd.to_string(), "USD"); assert_eq!(usd.to_string(), "USD");
assert_eq!(usd.symbol(), "$"); assert_eq!(usd.symbol(), "$");
assert_eq!(usd.as_str(), "USD"); assert_eq!(usd.as_str(), "USD");
@@ -92,16 +89,16 @@ mod tests {
#[test] #[test]
fn test_currency_type_default() { fn test_currency_type_default() {
let default_currency = CurrencyType::default(); let default_currency = CurrencyType::default();
assert_eq!(default_currency, CurrencyType::USD); assert_eq!(default_currency, CurrencyType::Usd);
} }
#[test] #[test]
fn test_currency_type_parsing() { fn test_currency_type_parsing() {
let parsed_currency = CurrencyType::from_str("usd").unwrap(); // Case insensitive let parsed_currency = CurrencyType::from_str("usd").unwrap(); // Case insensitive
assert_eq!(parsed_currency, CurrencyType::USD); assert_eq!(parsed_currency, CurrencyType::Usd);
let parsed_upper = CurrencyType::from_str("USD").unwrap(); let parsed_upper = CurrencyType::from_str("USD").unwrap();
assert_eq!(parsed_upper, CurrencyType::USD); assert_eq!(parsed_upper, CurrencyType::Usd);
let invalid = CurrencyType::from_str("EUR"); let invalid = CurrencyType::from_str("EUR");
assert!(invalid.is_err()); assert!(invalid.is_err());

View File

@@ -11,6 +11,7 @@ use std::fmt::{self, Display};
use crate::message_utils::pluralize_with_count; use crate::message_utils::pluralize_with_count;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Default)]
pub struct ListingDuration(i32); pub struct ListingDuration(i32);
impl ListingDuration { impl ListingDuration {
pub fn hours(hours: i32) -> Self { pub fn hours(hours: i32) -> Self {
@@ -23,11 +24,6 @@ impl ListingDuration {
Default::default() Default::default()
} }
} }
impl Default for ListingDuration {
fn default() -> Self {
Self(0)
}
}
impl From<ListingDuration> for Duration { impl From<ListingDuration> for Duration {
fn from(duration: ListingDuration) -> Self { fn from(duration: ListingDuration) -> Self {
Duration::hours(duration.0 as i64) Duration::hours(duration.0 as i64)

View File

@@ -6,6 +6,7 @@ mod telegram_user_id;
mod user_row_id; mod user_row_id;
// Re-export all types for easy access // Re-export all types for easy access
#[allow(unused)]
pub use currency_type::*; pub use currency_type::*;
pub use listing_duration::*; pub use listing_duration::*;
pub use listing_id::*; pub use listing_id::*;

View File

@@ -35,12 +35,12 @@ impl MoneyAmount {
} }
/// Get the value in cents /// Get the value in cents
pub fn cents(&self) -> i64 { pub fn cents(self) -> i64 {
self.0 self.0
} }
/// Get the value as a Decimal (for display/calculation purposes) /// Get the value as a Decimal (for display/calculation purposes)
pub fn to_decimal(&self) -> Decimal { pub fn to_decimal(self) -> Decimal {
Decimal::new(self.0, 2) // 2 decimal places for cents Decimal::new(self.0, 2) // 2 decimal places for cents
} }
} }
@@ -204,7 +204,7 @@ mod tests {
// Insert test data // Insert test data
sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)") sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)")
.bind(&amount) .bind(&amount)
.bind(CurrencyType::USD) .bind(CurrencyType::Usd)
.execute(&pool) .execute(&pool)
.await .await
.expect("Failed to insert test data"); .expect("Failed to insert test data");
@@ -219,7 +219,7 @@ mod tests {
let retrieved_currency: CurrencyType = row.get("currency"); let retrieved_currency: CurrencyType = row.get("currency");
assert_eq!(retrieved_amount, amount); assert_eq!(retrieved_amount, amount);
assert_eq!(retrieved_currency, CurrencyType::USD); assert_eq!(retrieved_currency, CurrencyType::Usd);
// Verify string representation matches expected format (cent-based precision) // Verify string representation matches expected format (cent-based precision)
assert_eq!(retrieved_amount.to_string(), expected_str); assert_eq!(retrieved_amount.to_string(), expected_str);
@@ -234,7 +234,7 @@ mod tests {
// Test with NULL (None) value // Test with NULL (None) value
sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)") sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)")
.bind(MoneyAmount::from_str("50.00").unwrap()) .bind(MoneyAmount::from_str("50.00").unwrap())
.bind(CurrencyType::USD) .bind(CurrencyType::Usd)
.bind(None::<MoneyAmount>) .bind(None::<MoneyAmount>)
.execute(&pool) .execute(&pool)
.await .await
@@ -244,7 +244,7 @@ mod tests {
let optional_amount = Some(MoneyAmount::from_str("25.75").unwrap()); let optional_amount = Some(MoneyAmount::from_str("25.75").unwrap());
sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)") sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)")
.bind(MoneyAmount::from_str("100.00").unwrap()) .bind(MoneyAmount::from_str("100.00").unwrap())
.bind(CurrencyType::USD) .bind(CurrencyType::Usd)
.bind(&optional_amount) .bind(&optional_amount)
.execute(&pool) .execute(&pool)
.await .await
@@ -291,7 +291,7 @@ mod tests {
// Insert into database // Insert into database
sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)") sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)")
.bind(&amount) .bind(&amount)
.bind(CurrencyType::USD) .bind(CurrencyType::Usd)
.execute(&pool) .execute(&pool)
.await .await
.expect("Failed to insert precision test data"); .expect("Failed to insert precision test data");

View File

@@ -7,7 +7,6 @@ use sqlx::{
encode::IsNull, error::BoxDynError, sqlite::SqliteTypeInfo, Decode, Encode, Sqlite, Type, encode::IsNull, error::BoxDynError, sqlite::SqliteTypeInfo, Decode, Encode, Sqlite, Type,
}; };
use std::fmt; use std::fmt;
use teloxide::types::ChatId;
/// Type-safe wrapper for user IDs /// Type-safe wrapper for user IDs
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]

View File

@@ -35,10 +35,10 @@ macro_rules! keyboard_buttons {
markup markup
} }
} }
impl Into<teloxide::types::InlineKeyboardButton> for $name { impl From<$name> for teloxide::types::InlineKeyboardButton {
fn into(self) -> teloxide::types::InlineKeyboardButton { fn from(value: $name) -> Self {
match self { match value {
$($(Self::$variant => teloxide::types::InlineKeyboardButton::callback($text, $callback_data)),*),* $($($name::$variant => teloxide::types::InlineKeyboardButton::callback($text, $callback_data)),*),*
} }
} }
} }

View File

@@ -6,8 +6,8 @@ use teloxide::{
payloads::{EditMessageTextSetters as _, SendMessageSetters as _}, payloads::{EditMessageTextSetters as _, SendMessageSetters as _},
prelude::Requester as _, prelude::Requester as _,
types::{ types::{
CallbackQuery, Chat, ChatId, InlineKeyboardButton, InlineKeyboardMarkup, Message, CallbackQuery, Chat, ChatId, InlineKeyboardButton, InlineKeyboardMarkup, MessageId,
MessageId, ParseMode, User, ParseMode, User,
}, },
Bot, Bot,
}; };
@@ -39,14 +39,14 @@ impl<'s> HandleAndId<'s> {
} }
} }
impl<'s> Into<HandleAndId<'s>> for &'s User { impl<'s> From<&'s User> for HandleAndId<'s> {
fn into(self) -> HandleAndId<'s> { fn from(val: &'s User) -> Self {
HandleAndId::from_user(self) HandleAndId::from_user(val)
} }
} }
impl<'s> Into<HandleAndId<'s>> for &'s Chat { impl<'s> From<&'s Chat> for HandleAndId<'s> {
fn into(self) -> HandleAndId<'s> { fn from(val: &'s Chat) -> Self {
HandleAndId::from_chat(self) HandleAndId::from_chat(val)
} }
} }
@@ -60,47 +60,47 @@ pub struct MessageTarget {
pub message_id: Option<MessageId>, pub message_id: Option<MessageId>,
} }
impl Into<MessageTarget> for ChatId { impl From<ChatId> for MessageTarget {
fn into(self) -> MessageTarget { fn from(val: ChatId) -> Self {
MessageTarget { MessageTarget {
chat_id: self, chat_id: val,
message_id: None, message_id: None,
} }
} }
} }
impl Into<MessageTarget> for Chat { impl From<Chat> for MessageTarget {
fn into(self) -> MessageTarget { fn from(val: Chat) -> Self {
MessageTarget { MessageTarget {
chat_id: self.id, chat_id: val.id,
message_id: None, message_id: None,
} }
} }
} }
impl Into<MessageTarget> for User { impl From<User> for MessageTarget {
fn into(self) -> MessageTarget { fn from(val: User) -> Self {
MessageTarget { MessageTarget {
chat_id: self.id.into(), chat_id: val.id.into(),
message_id: None, message_id: None,
} }
} }
} }
impl Into<MessageTarget> for (User, MessageId) { impl From<(User, MessageId)> for MessageTarget {
fn into(self) -> MessageTarget { fn from(val: (User, MessageId)) -> Self {
MessageTarget { MessageTarget {
chat_id: self.0.id.into(), chat_id: val.0.id.into(),
message_id: Some(self.1), message_id: Some(val.1),
} }
} }
} }
impl Into<MessageTarget> for (Chat, MessageId) { impl From<(Chat, MessageId)> for MessageTarget {
fn into(self) -> MessageTarget { fn from(val: (Chat, MessageId)) -> Self {
MessageTarget { MessageTarget {
chat_id: self.0.id.into(), chat_id: val.0.id,
message_id: Some(self.1), message_id: Some(val.1),
} }
} }
} }
@@ -165,7 +165,7 @@ pub fn create_multi_row_keyboard(rows: &[&[(&str, &str)]]) -> InlineKeyboardMark
} }
// Extract callback data and answer callback query // Extract callback data and answer callback query
pub async fn extract_callback_data<'c>( pub async fn extract_callback_data(
bot: &Bot, bot: &Bot,
callback_query: CallbackQuery, callback_query: CallbackQuery,
) -> HandlerResult<(String, User, MessageId)> { ) -> HandlerResult<(String, User, MessageId)> {
@@ -184,7 +184,7 @@ pub async fn extract_callback_data<'c>(
// Answer the callback query to remove loading state // Answer the callback query to remove loading state
if let Err(e) = bot.answer_callback_query(callback_query.id.clone()).await { if let Err(e) = bot.answer_callback_query(callback_query.id.clone()).await {
log::warn!("Failed to answer callback query: {}", e); log::warn!("Failed to answer callback query: {e}");
} }
Ok((data, from, message_id)) Ok((data, from, message_id))
@@ -202,7 +202,7 @@ pub fn pluralize<'a, N: One + PartialEq<N>>(
} }
} }
pub fn pluralize_with_count<'a, N: One + PartialEq<N> + Display + Copy>( pub fn pluralize_with_count<N: One + PartialEq<N> + Display + Copy>(
count: N, count: N,
singular: &str, singular: &str,
plural: &str, plural: &str,