cargo clippy
This commit is contained in:
@@ -2,9 +2,7 @@ use crate::{
|
||||
case,
|
||||
db::{Listing, ListingDAO, ListingId, User, UserDAO},
|
||||
keyboard_buttons,
|
||||
message_utils::{
|
||||
extract_callback_data, pluralize_with_count, send_message, HandleAndId, MessageTarget,
|
||||
},
|
||||
message_utils::{extract_callback_data, pluralize_with_count, send_message, MessageTarget},
|
||||
Command, DialogueRootState, HandlerResult, RootDialogue,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -12,7 +10,7 @@ use sqlx::SqlitePool;
|
||||
use teloxide::{
|
||||
dispatching::{DpHandlerDescription, UpdateFilterExt},
|
||||
prelude::*,
|
||||
types::{InlineKeyboardButton, Message, MessageId, ParseMode},
|
||||
types::{InlineKeyboardButton, Message},
|
||||
Bot,
|
||||
};
|
||||
|
||||
@@ -183,7 +181,7 @@ async fn show_listing_details(
|
||||
);
|
||||
|
||||
send_message(
|
||||
&bot,
|
||||
bot,
|
||||
target,
|
||||
response,
|
||||
Some(ManageListingButtons::to_keyboard()),
|
||||
@@ -233,7 +231,7 @@ async fn get_user_and_listing(
|
||||
listing_id: ListingId,
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> 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,
|
||||
None => {
|
||||
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,
|
||||
None => {
|
||||
send_message(bot, target, "❌ Listing not found.", None).await?;
|
||||
|
||||
@@ -9,7 +9,6 @@ use crate::{
|
||||
models::new_listing::{NewListing, NewListingBase, NewListingFields},
|
||||
ListingDuration, NewUser, UserDAO,
|
||||
},
|
||||
keyboard_buttons,
|
||||
message_utils::*,
|
||||
DialogueRootState, HandlerResult, RootDialogue,
|
||||
};
|
||||
@@ -35,8 +34,8 @@ fn create_back_button_keyboard_with_clear(field: &str) -> InlineKeyboardMarkup {
|
||||
create_single_row_keyboard(&[
|
||||
("🔙 Back", "edit_back"),
|
||||
(
|
||||
&format!("🧹 Clear {}", field),
|
||||
&format!("edit_clear_{}", field),
|
||||
&format!("🧹 Clear {field}"),
|
||||
&format!("edit_clear_{field}"),
|
||||
),
|
||||
])
|
||||
}
|
||||
@@ -190,7 +189,7 @@ async fn handle_description_callback(
|
||||
send_message(&bot, target, response, None).await?;
|
||||
}
|
||||
_ => {
|
||||
error!("Unknown callback data: {}", data);
|
||||
error!("Unknown callback data: {data}");
|
||||
dialogue.exit().await?;
|
||||
}
|
||||
}
|
||||
@@ -205,7 +204,7 @@ async fn handle_awaiting_draft_field_callback(
|
||||
callback_query: CallbackQuery,
|
||||
) -> HandlerResult {
|
||||
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);
|
||||
|
||||
if data == "cancel" {
|
||||
@@ -214,17 +213,17 @@ async fn handle_awaiting_draft_field_callback(
|
||||
|
||||
match field {
|
||||
ListingField::Title => {
|
||||
error!("Unknown callback data: {}", data);
|
||||
error!("Unknown callback data: {data}");
|
||||
dialogue.exit().await?;
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
ListingField::Description => {
|
||||
handle_description_callback(bot, dialogue, draft, data.as_str(), target).await
|
||||
}
|
||||
ListingField::Price => {
|
||||
error!("Unknown callback data: {}", data);
|
||||
error!("Unknown callback data: {data}");
|
||||
dialogue.exit().await?;
|
||||
return Ok(());
|
||||
Ok(())
|
||||
}
|
||||
ListingField::Slots => {
|
||||
handle_slots_callback(bot, dialogue, draft, data.as_str(), target).await
|
||||
@@ -303,7 +302,7 @@ async fn process_slots_and_respond(
|
||||
);
|
||||
|
||||
send_message(
|
||||
&bot,
|
||||
bot,
|
||||
target,
|
||||
&response,
|
||||
Some(StartTimeKeyboardButtons::to_keyboard()),
|
||||
@@ -328,12 +327,12 @@ async fn handle_viewing_draft_callback(
|
||||
|
||||
match button {
|
||||
ConfirmationKeyboardButtons::Create => {
|
||||
info!("User {:?} confirmed listing creation", target);
|
||||
info!("User {target:?} confirmed listing creation");
|
||||
dialogue.exit().await?;
|
||||
create_listing(db_pool, bot, dialogue, from, message_id, draft.clone()).await?;
|
||||
}
|
||||
ConfirmationKeyboardButtons::Discard => {
|
||||
info!("User {:?} discarded listing creation", from);
|
||||
info!("User {from:?} discarded listing creation");
|
||||
|
||||
// Exit dialogue and send cancellation message
|
||||
dialogue.exit().await?;
|
||||
@@ -345,7 +344,7 @@ async fn handle_viewing_draft_callback(
|
||||
send_message(&bot, target, &response, None).await?;
|
||||
}
|
||||
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
|
||||
dialogue
|
||||
@@ -379,18 +378,17 @@ async fn process_start_time_and_respond(
|
||||
.await?;
|
||||
|
||||
// Generate response message
|
||||
let start_msg = format!("in {}", duration);
|
||||
let start_msg = format!("in {duration}");
|
||||
|
||||
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\
|
||||
How long should your listing run?\n\
|
||||
Enter duration in hours (minimum 1 hour, maximum 720 hours / 30 days):",
|
||||
start_msg
|
||||
Enter duration in hours (minimum 1 hour, maximum 720 hours / 30 days):"
|
||||
);
|
||||
|
||||
send_message(
|
||||
&bot,
|
||||
bot,
|
||||
target,
|
||||
&response,
|
||||
Some(DurationKeyboardButtons::to_keyboard()),
|
||||
@@ -606,7 +604,7 @@ async fn show_edit_screen(
|
||||
);
|
||||
|
||||
if let Some(flash_message) = flash_message {
|
||||
response = format!("{}\n\n{}", flash_message, response);
|
||||
response = format!("{flash_message}\n\n{response}");
|
||||
}
|
||||
|
||||
send_message(
|
||||
@@ -629,7 +627,7 @@ async fn handle_editing_field_input(
|
||||
let chat = msg.chat.clone();
|
||||
let text = msg.text().unwrap_or("").trim();
|
||||
|
||||
info!("User {:?} editing field {:?}", chat, field);
|
||||
info!("User {chat:?} editing field {field:?}");
|
||||
|
||||
match field {
|
||||
ListingField::Title => {
|
||||
@@ -728,10 +726,9 @@ async fn handle_editing_draft_callback(
|
||||
|
||||
// update the message to show the edit screen
|
||||
let response = format!(
|
||||
"Editing {:?}\n\n\
|
||||
Previous value: {}\
|
||||
",
|
||||
field, value
|
||||
"Editing {field:?}\n\n\
|
||||
Previous value: {value}\
|
||||
"
|
||||
);
|
||||
|
||||
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 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,
|
||||
None => {
|
||||
UserDAO::insert_user(
|
||||
@@ -803,7 +800,7 @@ async fn create_listing(
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to create listing for user {:?}: {}", from, e);
|
||||
log::error!("Failed to create listing for user {from:?}: {e}");
|
||||
send_message(
|
||||
&bot,
|
||||
(from, message_id),
|
||||
@@ -823,7 +820,7 @@ async fn cancel_wizard(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("{:?} cancelled new listing wizard", target);
|
||||
info!("{target:?} cancelled new listing wizard");
|
||||
dialogue.exit().await?;
|
||||
send_message(&bot, target, "❌ Listing creation cancelled.", None).await?;
|
||||
Ok(())
|
||||
@@ -838,7 +835,7 @@ async fn handle_edit_title(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("User {:?} editing title: '{}'", target, text);
|
||||
info!("User {target:?} editing title: '{text}'");
|
||||
|
||||
draft.title = match validate_title(text) {
|
||||
Ok(title) => title,
|
||||
@@ -873,7 +870,7 @@ async fn handle_edit_description(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("User {:?} editing description: '{}'", target, text);
|
||||
info!("User {target:?} editing description: '{text}'");
|
||||
|
||||
state.description = match validate_description(text) {
|
||||
Ok(description) => Some(description),
|
||||
@@ -903,7 +900,7 @@ async fn handle_edit_price(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("User {:?} editing price: '{}'", target, text);
|
||||
info!("User {target:?} editing price: '{text}'");
|
||||
|
||||
state.buy_now_price = match validate_price(text) {
|
||||
Ok(price) => price,
|
||||
@@ -932,7 +929,7 @@ async fn handle_edit_slots(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("User {:?} editing slots: '{}'", target, text);
|
||||
info!("User {target:?} editing slots: '{text}'");
|
||||
|
||||
state.slots_available = match validate_slots(text) {
|
||||
Ok(s) => s,
|
||||
@@ -962,7 +959,7 @@ async fn handle_edit_start_time(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
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) {
|
||||
Ok(h) => h,
|
||||
@@ -998,7 +995,7 @@ async fn handle_edit_duration(
|
||||
target: impl Into<MessageTarget>,
|
||||
) -> HandlerResult {
|
||||
let target = target.into();
|
||||
info!("User {:?} editing duration: '{}'", target, text);
|
||||
info!("User {target:?} editing duration: '{text}'");
|
||||
|
||||
state.duration = match validate_duration(text) {
|
||||
Ok(d) => d,
|
||||
|
||||
@@ -41,7 +41,7 @@ pub fn validate_price(text: &str) -> Result<MoneyAmount, String> {
|
||||
|
||||
pub fn validate_slots(text: &str) -> Result<i32, String> {
|
||||
match text.parse::<i32>() {
|
||||
Ok(slots) if slots >= 1 && slots <= 1000 => Ok(slots),
|
||||
Ok(slots) if (1..=1000).contains(&slots) => Ok(slots),
|
||||
Ok(_) => Err(
|
||||
"❌ Number of slots must be between 1 and 1000. Please enter a valid number:"
|
||||
.to_string(),
|
||||
@@ -52,7 +52,7 @@ pub fn validate_slots(text: &str) -> Result<i32, String> {
|
||||
|
||||
pub fn validate_duration(text: &str) -> Result<ListingDuration, String> {
|
||||
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(
|
||||
"❌ 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> {
|
||||
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(
|
||||
"❌ Start time must be between 0 and 168 hours. Please enter a valid number:"
|
||||
.to_string(),
|
||||
|
||||
@@ -76,7 +76,7 @@ impl Config {
|
||||
log::info!(" Web Port: {}", self.web_port);
|
||||
|
||||
if let Some(admin_id) = self.admin_user_id {
|
||||
log::info!(" Admin User ID: {}", admin_id);
|
||||
log::info!(" Admin User ID: {admin_id}");
|
||||
} else {
|
||||
log::info!(" Admin User ID: Not set");
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ impl ListingDAO {
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
Ok(result.map(Self::row_to_listing).transpose()?)
|
||||
result.map(Self::row_to_listing).transpose()
|
||||
}
|
||||
|
||||
/// Find all listings by a seller
|
||||
@@ -125,10 +125,10 @@ impl ListingDAO {
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
|
||||
Ok(rows
|
||||
rows
|
||||
.into_iter()
|
||||
.map(Self::row_to_listing)
|
||||
.collect::<Result<Vec<_>>>()?)
|
||||
.collect::<Result<Vec<_>>>()
|
||||
}
|
||||
|
||||
/// Delete a listing
|
||||
|
||||
@@ -13,6 +13,7 @@ use crate::db::{
|
||||
/// Data Access Object for User operations
|
||||
pub struct UserDAO;
|
||||
|
||||
#[allow(unused)]
|
||||
impl UserDAO {
|
||||
/// Insert a new user into the database
|
||||
pub async fn insert_user(pool: &SqlitePool, new_user: &NewUser) -> Result<User> {
|
||||
@@ -131,7 +132,7 @@ impl UserDAO {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::db::models::user::{NewUser, User};
|
||||
use crate::db::models::user::NewUser;
|
||||
use rstest::rstest;
|
||||
use sqlx::SqlitePool;
|
||||
use teloxide::types::UserId;
|
||||
|
||||
@@ -15,6 +15,7 @@ use chrono::{DateTime, Utc};
|
||||
|
||||
/// Main listing/auction entity
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub struct Listing {
|
||||
pub base: ListingBase,
|
||||
pub fields: ListingFields,
|
||||
@@ -22,6 +23,7 @@ pub struct Listing {
|
||||
|
||||
/// Common fields shared by all listing types
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub struct ListingBase {
|
||||
pub id: ListingId,
|
||||
pub seller_id: UserRowId,
|
||||
@@ -34,6 +36,7 @@ pub struct ListingBase {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub enum ListingFields {
|
||||
BasicAuction {
|
||||
starting_bid: MoneyAmount,
|
||||
@@ -57,6 +60,7 @@ pub enum ListingFields {
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl Listing {
|
||||
/// Get the listing type as an enum value
|
||||
pub fn listing_type(&self) -> ListingType {
|
||||
|
||||
@@ -8,10 +8,6 @@ pub mod user;
|
||||
pub mod user_settings;
|
||||
|
||||
// Re-export all types for easy access
|
||||
pub use bid::*;
|
||||
pub use listing::*;
|
||||
pub use listing_media::*;
|
||||
pub use listing_type::*;
|
||||
pub use proxy_bid::*;
|
||||
pub use user::*;
|
||||
pub use user_settings::*;
|
||||
|
||||
@@ -29,6 +29,7 @@ pub struct NewListingBase {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub enum NewListingFields {
|
||||
BasicAuction {
|
||||
starting_bid: MoneyAmount,
|
||||
@@ -52,6 +53,7 @@ pub enum NewListingFields {
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl NewListingBase {
|
||||
pub fn new(
|
||||
seller_id: UserRowId,
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::db::MoneyAmount;
|
||||
|
||||
/// Proxy bid strategies (automatic bidding settings)
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[allow(unused)]
|
||||
pub struct ProxyBid {
|
||||
pub id: i64,
|
||||
pub listing_id: i64,
|
||||
@@ -17,6 +18,7 @@ pub struct ProxyBid {
|
||||
|
||||
/// New proxy bid data for insertion
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(unused)]
|
||||
pub struct NewProxyBid {
|
||||
pub listing_id: i64,
|
||||
pub buyer_id: i64,
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::db::{TelegramUserId, UserRowId};
|
||||
|
||||
/// Core user information
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[allow(unused)]
|
||||
pub struct User {
|
||||
pub id: UserRowId,
|
||||
pub telegram_id: TelegramUserId,
|
||||
|
||||
@@ -3,41 +3,38 @@ use sqlx::{
|
||||
};
|
||||
|
||||
/// Currency types supported by the platform
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum CurrencyType {
|
||||
USD,
|
||||
#[default]
|
||||
Usd,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
impl CurrencyType {
|
||||
/// Get the currency code as a string
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
CurrencyType::USD => "USD",
|
||||
CurrencyType::Usd => "USD",
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the currency symbol
|
||||
pub fn symbol(&self) -> &'static str {
|
||||
match self {
|
||||
CurrencyType::USD => "$",
|
||||
CurrencyType::Usd => "$",
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse currency from string
|
||||
pub fn from_str(s: &str) -> Result<Self, String> {
|
||||
match s.to_uppercase().as_str() {
|
||||
"USD" => Ok(CurrencyType::USD),
|
||||
_ => Err(format!("Unsupported currency: {}", s)),
|
||||
"USD" => Ok(CurrencyType::Usd),
|
||||
_ => Err(format!("Unsupported currency: {s}")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Default for CurrencyType (defaults to USD)
|
||||
impl Default for CurrencyType {
|
||||
fn default() -> Self {
|
||||
CurrencyType::USD
|
||||
}
|
||||
}
|
||||
|
||||
// Implement Display for CurrencyType
|
||||
impl std::fmt::Display for CurrencyType {
|
||||
@@ -83,7 +80,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_currency_type_display() {
|
||||
let usd = CurrencyType::USD;
|
||||
let usd = CurrencyType::Usd;
|
||||
assert_eq!(usd.to_string(), "USD");
|
||||
assert_eq!(usd.symbol(), "$");
|
||||
assert_eq!(usd.as_str(), "USD");
|
||||
@@ -92,16 +89,16 @@ mod tests {
|
||||
#[test]
|
||||
fn test_currency_type_default() {
|
||||
let default_currency = CurrencyType::default();
|
||||
assert_eq!(default_currency, CurrencyType::USD);
|
||||
assert_eq!(default_currency, CurrencyType::Usd);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_currency_type_parsing() {
|
||||
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();
|
||||
assert_eq!(parsed_upper, CurrencyType::USD);
|
||||
assert_eq!(parsed_upper, CurrencyType::Usd);
|
||||
|
||||
let invalid = CurrencyType::from_str("EUR");
|
||||
assert!(invalid.is_err());
|
||||
|
||||
@@ -11,6 +11,7 @@ use std::fmt::{self, Display};
|
||||
use crate::message_utils::pluralize_with_count;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Default)]
|
||||
pub struct ListingDuration(i32);
|
||||
impl ListingDuration {
|
||||
pub fn hours(hours: i32) -> Self {
|
||||
@@ -23,11 +24,6 @@ impl ListingDuration {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
impl Default for ListingDuration {
|
||||
fn default() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
}
|
||||
impl From<ListingDuration> for Duration {
|
||||
fn from(duration: ListingDuration) -> Self {
|
||||
Duration::hours(duration.0 as i64)
|
||||
|
||||
@@ -6,6 +6,7 @@ mod telegram_user_id;
|
||||
mod user_row_id;
|
||||
|
||||
// Re-export all types for easy access
|
||||
#[allow(unused)]
|
||||
pub use currency_type::*;
|
||||
pub use listing_duration::*;
|
||||
pub use listing_id::*;
|
||||
|
||||
@@ -35,12 +35,12 @@ impl MoneyAmount {
|
||||
}
|
||||
|
||||
/// Get the value in cents
|
||||
pub fn cents(&self) -> i64 {
|
||||
pub fn cents(self) -> i64 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// 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
|
||||
}
|
||||
}
|
||||
@@ -204,7 +204,7 @@ mod tests {
|
||||
// Insert test data
|
||||
sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)")
|
||||
.bind(&amount)
|
||||
.bind(CurrencyType::USD)
|
||||
.bind(CurrencyType::Usd)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("Failed to insert test data");
|
||||
@@ -219,7 +219,7 @@ mod tests {
|
||||
let retrieved_currency: CurrencyType = row.get("currency");
|
||||
|
||||
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)
|
||||
assert_eq!(retrieved_amount.to_string(), expected_str);
|
||||
@@ -234,7 +234,7 @@ mod tests {
|
||||
// Test with NULL (None) value
|
||||
sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)")
|
||||
.bind(MoneyAmount::from_str("50.00").unwrap())
|
||||
.bind(CurrencyType::USD)
|
||||
.bind(CurrencyType::Usd)
|
||||
.bind(None::<MoneyAmount>)
|
||||
.execute(&pool)
|
||||
.await
|
||||
@@ -244,7 +244,7 @@ mod tests {
|
||||
let optional_amount = Some(MoneyAmount::from_str("25.75").unwrap());
|
||||
sqlx::query("INSERT INTO test_money (amount, currency, optional_amount) VALUES (?, ?, ?)")
|
||||
.bind(MoneyAmount::from_str("100.00").unwrap())
|
||||
.bind(CurrencyType::USD)
|
||||
.bind(CurrencyType::Usd)
|
||||
.bind(&optional_amount)
|
||||
.execute(&pool)
|
||||
.await
|
||||
@@ -291,7 +291,7 @@ mod tests {
|
||||
// Insert into database
|
||||
sqlx::query("INSERT INTO test_money (amount, currency) VALUES (?, ?)")
|
||||
.bind(&amount)
|
||||
.bind(CurrencyType::USD)
|
||||
.bind(CurrencyType::Usd)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.expect("Failed to insert precision test data");
|
||||
|
||||
@@ -7,7 +7,6 @@ use sqlx::{
|
||||
encode::IsNull, error::BoxDynError, sqlite::SqliteTypeInfo, Decode, Encode, Sqlite, Type,
|
||||
};
|
||||
use std::fmt;
|
||||
use teloxide::types::ChatId;
|
||||
|
||||
/// Type-safe wrapper for user IDs
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
|
||||
@@ -35,10 +35,10 @@ macro_rules! keyboard_buttons {
|
||||
markup
|
||||
}
|
||||
}
|
||||
impl Into<teloxide::types::InlineKeyboardButton> for $name {
|
||||
fn into(self) -> teloxide::types::InlineKeyboardButton {
|
||||
match self {
|
||||
$($(Self::$variant => teloxide::types::InlineKeyboardButton::callback($text, $callback_data)),*),*
|
||||
impl From<$name> for teloxide::types::InlineKeyboardButton {
|
||||
fn from(value: $name) -> Self {
|
||||
match value {
|
||||
$($($name::$variant => teloxide::types::InlineKeyboardButton::callback($text, $callback_data)),*),*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@ use teloxide::{
|
||||
payloads::{EditMessageTextSetters as _, SendMessageSetters as _},
|
||||
prelude::Requester as _,
|
||||
types::{
|
||||
CallbackQuery, Chat, ChatId, InlineKeyboardButton, InlineKeyboardMarkup, Message,
|
||||
MessageId, ParseMode, User,
|
||||
CallbackQuery, Chat, ChatId, InlineKeyboardButton, InlineKeyboardMarkup, MessageId,
|
||||
ParseMode, User,
|
||||
},
|
||||
Bot,
|
||||
};
|
||||
@@ -39,14 +39,14 @@ impl<'s> HandleAndId<'s> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> Into<HandleAndId<'s>> for &'s User {
|
||||
fn into(self) -> HandleAndId<'s> {
|
||||
HandleAndId::from_user(self)
|
||||
impl<'s> From<&'s User> for HandleAndId<'s> {
|
||||
fn from(val: &'s User) -> Self {
|
||||
HandleAndId::from_user(val)
|
||||
}
|
||||
}
|
||||
impl<'s> Into<HandleAndId<'s>> for &'s Chat {
|
||||
fn into(self) -> HandleAndId<'s> {
|
||||
HandleAndId::from_chat(self)
|
||||
impl<'s> From<&'s Chat> for HandleAndId<'s> {
|
||||
fn from(val: &'s Chat) -> Self {
|
||||
HandleAndId::from_chat(val)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,47 +60,47 @@ pub struct MessageTarget {
|
||||
pub message_id: Option<MessageId>,
|
||||
}
|
||||
|
||||
impl Into<MessageTarget> for ChatId {
|
||||
fn into(self) -> MessageTarget {
|
||||
impl From<ChatId> for MessageTarget {
|
||||
fn from(val: ChatId) -> Self {
|
||||
MessageTarget {
|
||||
chat_id: self,
|
||||
chat_id: val,
|
||||
message_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<MessageTarget> for Chat {
|
||||
fn into(self) -> MessageTarget {
|
||||
impl From<Chat> for MessageTarget {
|
||||
fn from(val: Chat) -> Self {
|
||||
MessageTarget {
|
||||
chat_id: self.id,
|
||||
chat_id: val.id,
|
||||
message_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<MessageTarget> for User {
|
||||
fn into(self) -> MessageTarget {
|
||||
impl From<User> for MessageTarget {
|
||||
fn from(val: User) -> Self {
|
||||
MessageTarget {
|
||||
chat_id: self.id.into(),
|
||||
chat_id: val.id.into(),
|
||||
message_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<MessageTarget> for (User, MessageId) {
|
||||
fn into(self) -> MessageTarget {
|
||||
impl From<(User, MessageId)> for MessageTarget {
|
||||
fn from(val: (User, MessageId)) -> Self {
|
||||
MessageTarget {
|
||||
chat_id: self.0.id.into(),
|
||||
message_id: Some(self.1),
|
||||
chat_id: val.0.id.into(),
|
||||
message_id: Some(val.1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<MessageTarget> for (Chat, MessageId) {
|
||||
fn into(self) -> MessageTarget {
|
||||
impl From<(Chat, MessageId)> for MessageTarget {
|
||||
fn from(val: (Chat, MessageId)) -> Self {
|
||||
MessageTarget {
|
||||
chat_id: self.0.id.into(),
|
||||
message_id: Some(self.1),
|
||||
chat_id: val.0.id,
|
||||
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
|
||||
pub async fn extract_callback_data<'c>(
|
||||
pub async fn extract_callback_data(
|
||||
bot: &Bot,
|
||||
callback_query: CallbackQuery,
|
||||
) -> HandlerResult<(String, User, MessageId)> {
|
||||
@@ -184,7 +184,7 @@ pub async fn extract_callback_data<'c>(
|
||||
|
||||
// Answer the callback query to remove loading state
|
||||
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))
|
||||
@@ -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,
|
||||
singular: &str,
|
||||
plural: &str,
|
||||
|
||||
Reference in New Issue
Block a user