Skip to content

Commit aa327da

Browse files
feat: seed db
1 parent 98c6695 commit aa327da

4 files changed

Lines changed: 150 additions & 1 deletion

File tree

src-tauri/Cargo.lock

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ rusqlite = { version = "0.38", features = ["bundled"] }
2323
rand = { version = "0.9", features = ["thread_rng"], default-features = false }
2424
chrono = { version = "0.4", features = ["now"], default-features = false }
2525
dirs = "6"
26+
csv = "1"
2627

2728
[profile.dev]
2829
debug = 0

src-tauri/lunch_list.csv

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
restaurant,option
2+
Arbys,cheap
3+
"Bubba's",Normal
4+
Charlestons,Normal
5+
Firehouse,Normal
6+
Freddies,Normal
7+
Frosted Mug,Normal
8+
Hideaway,Normal
9+
"Jersy Mike's",Normal
10+
Johnnies,Normal
11+
Mcalisters,Normal
12+
Mcneilies,Normal
13+
Olive Garden,Normal
14+
On The Border,Normal
15+
Qdoba,Normal
16+
Tamashii Ramen,Normal
17+
Teds,Normal
18+
The Mule,Normal
19+
Zios,Normal

src-tauri/src/db.rs

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,23 @@ impl Database {
2626
}
2727

2828
pub fn with_connection(conn: Connection) -> Result<Self> {
29+
Self::with_connection_internal(conn, true)
30+
}
31+
32+
fn with_connection_internal(conn: Connection, should_seed: bool) -> Result<Self> {
2933
let db = Database {
3034
conn: Mutex::new(conn),
3135
};
3236
db.init_tables()?;
37+
if should_seed {
38+
db.seed_database()?;
39+
}
3340
Ok(db)
3441
}
3542

3643
pub fn in_memory() -> Result<Self> {
3744
let conn = Connection::open_in_memory()?;
38-
Self::with_connection(conn)
45+
Self::with_connection_internal(conn, false)
3946
}
4047

4148
fn init_tables(&self) -> Result<()> {
@@ -55,6 +62,50 @@ impl Database {
5562
Ok(())
5663
}
5764

65+
fn seed_database(&self) -> Result<()> {
66+
let conn = self.conn.lock().unwrap();
67+
68+
// Check if database is empty (first launch)
69+
let count: i64 = conn.query_row(
70+
"SELECT COUNT(*) FROM lunch_list",
71+
[],
72+
|row| row.get(0),
73+
)?;
74+
75+
if count > 0 {
76+
// Database already has data, skip seeding
77+
return Ok(());
78+
}
79+
80+
// Parse embedded CSV and seed database
81+
let csv_data = include_str!("../lunch_list.csv");
82+
let mut reader = csv::Reader::from_reader(csv_data.as_bytes());
83+
84+
for result in reader.records() {
85+
let record = result.map_err(|e| {
86+
rusqlite::Error::FromSqlConversionFailure(
87+
0,
88+
rusqlite::types::Type::Text,
89+
Box::new(e),
90+
)
91+
})?;
92+
93+
if record.len() >= 2 {
94+
let name = record.get(0).unwrap_or("");
95+
let category = record.get(1).unwrap_or("");
96+
97+
if !name.is_empty() && !category.is_empty() {
98+
conn.execute(
99+
"INSERT INTO lunch_list (restaurants, option) VALUES (?, ?)",
100+
[name, category],
101+
)?;
102+
}
103+
}
104+
}
105+
106+
Ok(())
107+
}
108+
58109
pub fn list_all(&self) -> Result<Vec<Restaurant>> {
59110
let conn = self.conn.lock().unwrap();
60111
let mut stmt = conn.prepare("SELECT restaurants, option FROM lunch_list ORDER BY restaurants")?;
@@ -164,3 +215,59 @@ fn get_db_path() -> PathBuf {
164215
.join("lunch.db")
165216
}
166217
}
218+
219+
#[cfg(test)]
220+
mod tests {
221+
use super::*;
222+
223+
#[test]
224+
fn test_seed_database_on_first_launch() {
225+
let db = Database::in_memory().unwrap();
226+
227+
// Explicitly seed the database
228+
db.seed_database().unwrap();
229+
230+
// Verify restaurants were seeded
231+
let restaurants = db.list_all().unwrap();
232+
assert!(restaurants.len() > 0, "Database should be seeded with restaurants");
233+
234+
// Verify both cheap and normal categories exist
235+
let cheap = db.list_by_category("cheap").unwrap();
236+
let normal = db.list_by_category("normal").unwrap();
237+
assert!(cheap.len() > 0, "Should have cheap restaurants");
238+
assert!(normal.len() > 0, "Should have normal restaurants");
239+
240+
// Verify specific restaurants from CSV
241+
assert!(
242+
restaurants.iter().any(|r| r.name == "Arbys"),
243+
"Should contain Arbys from seed data"
244+
);
245+
assert!(
246+
restaurants.iter().any(|r| r.name == "Bubba's"),
247+
"Should contain Bubba's from seed data"
248+
);
249+
}
250+
251+
#[test]
252+
fn test_seed_database_does_not_overwrite() {
253+
let db = Database::in_memory().unwrap();
254+
255+
// Add a custom restaurant before seeding
256+
db.add("Custom Restaurant", "normal").unwrap();
257+
258+
// Try seeding (should not add anything because database is not empty)
259+
db.seed_database().unwrap();
260+
261+
// Verify count is only 1 (our custom restaurant)
262+
let final_restaurants = db.list_all().unwrap();
263+
assert_eq!(
264+
final_restaurants.len(),
265+
1,
266+
"Seeding should not run on non-empty database"
267+
);
268+
assert!(
269+
final_restaurants.iter().any(|r| r.name == "Custom Restaurant"),
270+
"Custom restaurant should be preserved"
271+
);
272+
}
273+
}

0 commit comments

Comments
 (0)