Game sync and live services for independent game developers (targeting itch.io)
1use anyhow::{Context, Result};
2use rusqlite::Connection;
3use ulid::Ulid;
4
5pub async fn generate(count: usize, expires_days: u32, database: &std::path::Path) -> Result<()> {
6 let conn = Connection::open(database).context("Failed to open database")?;
7
8 let expires_at = chrono::Utc::now()
9 + chrono::Duration::days(expires_days as i64);
10 let expires_str = expires_at.to_rfc3339();
11
12 for _ in 0..count {
13 let code = generate_code();
14 conn.execute(
15 "INSERT INTO invites (code, created_by, expires_at) VALUES (?1, 'admin', ?2)",
16 [&code, &expires_str],
17 )
18 .context("Failed to insert invite")?;
19 println!("{}", code);
20 }
21
22 Ok(())
23}
24
25pub async fn list(database: &std::path::Path, unused_only: bool) -> Result<()> {
26 let conn = Connection::open(database).context("Failed to open database")?;
27
28 let query = if unused_only {
29 "SELECT code, expires_at FROM invites WHERE used_by IS NULL"
30 } else {
31 "SELECT code, used_by, expires_at FROM invites"
32 };
33
34 let mut stmt = conn.prepare(query)?;
35 let rows = stmt.query_map([], |row| {
36 let code: String = row.get(0)?;
37 let expires_at: String = row.get(1)?;
38 let used_by: Option<String> = row.get(2).ok();
39 Ok((code, used_by, expires_at))
40 })?;
41
42 println!("{:<20} {:<20} {}", "CODE", "USED_BY", "EXPIRES_AT");
43 println!("{}", "-".repeat(60));
44
45 for row in rows {
46 let (code, used_by, expires_at) = row?;
47 let used = used_by.map(|_| "yes").unwrap_or("no");
48 println!("{:<20} {:<20} {}", code, used, expires_at);
49 }
50
51 Ok(())
52}
53
54fn generate_code() -> String {
55 let ulid = Ulid::new();
56 ulid.to_string()
57}