Compare commits
2 Commits
ff061cb3bf
...
8fb51d12a7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fb51d12a7 | ||
|
|
374abf8c42 |
@@ -51,12 +51,52 @@ 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 (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(),
|
||||
),
|
||||
Err(_) => Err("❌ Invalid number. Please enter number of hours (1-720):".to_string()),
|
||||
let text = text.trim().to_lowercase();
|
||||
|
||||
// Try to parse as plain number first (backwards compatibility)
|
||||
if let Ok(hours) = text.parse::<i32>() {
|
||||
if (1..=720).contains(&hours) {
|
||||
return Ok(ListingDuration::hours(hours));
|
||||
} else {
|
||||
return Err("❌ Duration must be between 1 hour and 30 days (720 hours). Please enter a valid duration:".to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// Parse natural language duration
|
||||
let parts: Vec<&str> = text.split_whitespace().collect();
|
||||
if parts.len() != 2 {
|
||||
return Err(
|
||||
"❌ Please enter duration like '1 hour', '7 days', or just hours (1-720):".to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let number_str = parts[0];
|
||||
let unit = parts[1];
|
||||
|
||||
let number = match number_str.parse::<i32>() {
|
||||
Ok(n) if n > 0 => n,
|
||||
_ => {
|
||||
return Err(
|
||||
"❌ Duration number must be a positive integer. Please enter a valid duration:"
|
||||
.to_string(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let hours = match unit {
|
||||
"hour" | "hours" | "hr" | "hrs" => number,
|
||||
"day" | "days" => number * 24,
|
||||
_ => {
|
||||
return Err(
|
||||
"❌ Supported units: hour(s), day(s). Please enter a valid duration:".to_string(),
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
if (1..=720).contains(&hours) {
|
||||
Ok(ListingDuration::hours(hours))
|
||||
} else {
|
||||
Err("❌ Duration must be between 1 hour and 30 days (720 hours). Please enter a valid duration:".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,3 +112,34 @@ pub fn validate_start_time(text: &str) -> Result<ListingDuration, String> {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
#[rstest]
|
||||
#[case("24", ListingDuration::hours(24))] // Plain number
|
||||
#[case("1 hour", ListingDuration::hours(1))]
|
||||
#[case("2 hours", ListingDuration::hours(2))]
|
||||
#[case("1 day", ListingDuration::hours(24))]
|
||||
#[case("7 days", ListingDuration::hours(168))]
|
||||
#[case("30 days", ListingDuration::hours(720))] // Max 30 days
|
||||
fn test_validate_duration_valid(#[case] input: &str, #[case] expected: ListingDuration) {
|
||||
let result = validate_duration(input).unwrap();
|
||||
assert_eq!(result, expected);
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("0")]
|
||||
#[case("0 hours")]
|
||||
#[case("721")] // Over limit
|
||||
#[case("1 week")] // Unsupported unit
|
||||
#[case("1 month")] // Unsupported unit
|
||||
#[case("1 year")] // Unsupported unit
|
||||
#[case("abc")] // Invalid text
|
||||
#[case("-1 hour")] // Negative
|
||||
fn test_validate_duration_invalid(#[case] input: &str) {
|
||||
assert!(validate_duration(input).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,10 +79,11 @@ impl ListingDAO {
|
||||
|
||||
let query_str = format!(
|
||||
r#"
|
||||
UPDATE listings
|
||||
SET {}
|
||||
WHERE id = ? AND seller_id = ?
|
||||
RETURNING {}
|
||||
UPDATE listings
|
||||
SET {}
|
||||
WHERE id = ?
|
||||
AND seller_id = ?
|
||||
RETURNING {}
|
||||
"#,
|
||||
binds
|
||||
.bind_names()
|
||||
@@ -105,10 +106,13 @@ impl ListingDAO {
|
||||
pool: &SqlitePool,
|
||||
listing_id: ListingDbId,
|
||||
) -> Result<Option<PersistedListing>> {
|
||||
let result = sqlx::query_as("SELECT * FROM listings WHERE id = ?")
|
||||
.bind(listing_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
let result = sqlx::query_as(&format!(
|
||||
"SELECT {} FROM listings WHERE id = ?",
|
||||
LISTING_RETURN_FIELDS.join(", ")
|
||||
))
|
||||
.bind(listing_id)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@@ -118,11 +122,13 @@ impl ListingDAO {
|
||||
pool: &SqlitePool,
|
||||
seller_id: UserDbId,
|
||||
) -> Result<Vec<PersistedListing>> {
|
||||
let rows =
|
||||
sqlx::query_as("SELECT * FROM listings WHERE seller_id = ? ORDER BY created_at DESC")
|
||||
.bind(seller_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
let rows = sqlx::query_as(&format!(
|
||||
"SELECT {} FROM listings WHERE seller_id = ? ORDER BY created_at DESC",
|
||||
LISTING_RETURN_FIELDS.join(", ")
|
||||
))
|
||||
.bind(seller_id)
|
||||
.fetch_all(pool)
|
||||
.await?;
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
|
||||
@@ -170,11 +170,11 @@ impl FromRow<'_, SqliteRow> for PersistedUser {
|
||||
created_at: row.get("created_at"),
|
||||
updated_at: row.get("updated_at"),
|
||||
},
|
||||
telegram_id: row.get("telegram_id"),
|
||||
username: row.get("username"),
|
||||
first_name: row.get("first_name"),
|
||||
last_name: row.get("last_name"),
|
||||
is_banned: row.get("is_banned"),
|
||||
telegram_id: row.get("telegram_id"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user