Skip to content

Commit

Permalink
sqlite replaced with swc reading from cookbook.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
tylersayshi committed Oct 31, 2024
1 parent 5ae8da8 commit 82ab7db
Show file tree
Hide file tree
Showing 9 changed files with 938 additions and 130 deletions.
768 changes: 713 additions & 55 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
indicatif = "0.17"
rusqlite = { version = "0.31.0", features = ["bundled"] }
swc_common = "2.0.1" # Replace with the latest version
swc_ecma_parser = "3.0.1"
swc_ecma_codegen = "2.0.0"
swc_ecma_ast = "2.0.0"
chrono = "0.4.38"
Binary file removed cookbook.db
Binary file not shown.
46 changes: 46 additions & 0 deletions cookbook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
type Recipe = {
created_at: string;
name: string;
ingredients: string;
instructions: string;
};

export default [
{
created_at: "2024-10-01",
name: "Pesto and sundried tomato stuffed zucchini boats",
ingredients: `- 3 medium zucchinis
- 1/2 cup of pesto
- 1/4 cup of sundried tomatoes, chopped
- 1/4 cup of grated parmesan cheese
- Salt and pepper to taste
`,
instructions: `1. Preheat your oven to 375°F (190°C).
2. Cut the zucchinis in half lengthwise and scoop out the seeds and flesh from the center to create a hollow "boat".
3. In a mixing bowl, combine the pesto, chopped sundried tomatoes, and grated parmesan cheese. Season with salt and pepper to taste.
4. Spoon the pesto mixture into the hollowed-out zucchini boats, filling them generously.
5. Place the filled zucchini boats on a baking sheet lined with parchment paper.
6. Bake in the preheated oven for about 20-25 minutes, or until the zucchinis are tender and the filling is heated through.
7. Serve the pesto and sundried tomato stuffed zucchini boats hot as a delicious and satisfying meal or side dish. Enjoy!
`,
},
{
created_at: "2024-10-01",
name: "Chocolate Chip Cookies",
ingredients: `- 310 g flour
- 1/2 tsp baking soda
- 1/2 tsp salt
- 170 g butter (melted)
- 1 cup brown sugar
- 1/2 cup sugar
- 1 tbsp vanilla
- 1 egg & 1 egg yolk
- 2 cups chocolate chips
`,
instructions: `1. preheat 325 & grease cookie sheets
2. sift flour, baking soda, & salt
3. mix butter & sugar till well blended. beat in vanilla & eggs. mix in dry ingredients. add chocolate chips with spoon, then make big cookies on the sheets.
4. bake 15-17 mins
`,
},
] satisfies Recipe[];
25 changes: 4 additions & 21 deletions src/cookbook.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,13 @@
use crate::read_cookbook::read_cookbook;
use inquire::Select;
use rusqlite::Connection;

use crate::utils::DBRecipe;

pub fn cookbook() {
let conn = Connection::open("cookbook.db").unwrap();

let recipes: Vec<DBRecipe> = conn
.prepare("SELECT * FROM recipes ORDER BY id ASC")
.unwrap()
.query_map([], |row| {
Ok(DBRecipe {
id: row.get(0).unwrap(),
created_at: row.get(1).unwrap(),
name: row.get(2).unwrap(),
instructions: row.get(3).unwrap(),
ingredients: row.get(4).unwrap(),
})
})
.unwrap()
.map(|x| x.unwrap())
.collect();
let recipes = read_cookbook();

let options = recipes
.iter()
.map(|x| format!("{}. {}", x.id.clone(), x.name.clone()))
.enumerate()
.map(|(i, x)| format!("{}. {}", i.clone() + 1, x.name.clone()))
.collect();

let choice = Select::new("Would you like to cook one of these recipes?", options)
Expand Down
29 changes: 5 additions & 24 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ use inquire::Select;
use std::fmt;
mod chatbot;
mod cookbook;
mod recipes;
mod read_cookbook;
mod utils;
mod write_recipe;
use ctrlc;
use rusqlite::Connection;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

Expand Down Expand Up @@ -37,27 +37,6 @@ async fn main() {
.expect("Error setting Ctrl+C handler");

while running.load(Ordering::SeqCst) {
let cookbook_exists = utils::file_exists("cookbook.db");

if !cookbook_exists {
println!("Creating cookbook.db");
let conn = Connection::open("cookbook.db").unwrap();

conn.execute(
"CREATE TABLE recipes (
id INTEGER PRIMARY KEY,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
name TEXT NOT NULL,
instructions TEXT NOT NULL,
ingredients TEXT NOT NULL
)",
[],
)
.unwrap();

conn.close().unwrap();
}

let options = vec![Choice::Cookbook, Choice::Chatbot, Choice::NewRecipe];

let choice = Select::new("What would you like to cook up today?", options).prompt();
Expand All @@ -71,7 +50,7 @@ async fn main() {
chatbot::chatbot().await;
}
Choice::NewRecipe => {
recipes::recipes();
write_recipe::recipes();
}
},
Err(inquire::error::InquireError::OperationInterrupted) => {
Expand All @@ -83,6 +62,8 @@ async fn main() {
break;
}
}

break;
}

println!("Goodbye!");
Expand Down
130 changes: 130 additions & 0 deletions src/read_cookbook.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::fs;
use swc_common::{sync::Lrc, FileName, SourceMap};
use swc_ecma_ast::{Expr, Module, ModuleDecl, TsSatisfiesExpr};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax, TsSyntax};

use crate::utils::SavedRecipe;

fn extract_default_export_list(module: &Module) -> Option<Vec<SavedRecipe>> {
for item in &module.body {
if let swc_ecma_ast::ModuleItem::ModuleDecl(ModuleDecl::ExportDefaultExpr(expr)) = item {
// Check if the export is a "satisfies" expression (type assertion)
let expr = match &*expr.expr {
Expr::TsSatisfies(TsSatisfiesExpr { expr, .. }) => &**expr, // Unwrap `satisfies` type assertion
other => other,
};

// Now check if it is an array literal
if let Expr::Array(arr) = expr {
let list = arr
.elems
.iter()
.filter_map(|elem| {
if let Some(Expr::Object(obj)) = elem.as_ref().map(|e| &*e.expr) {
let mut name = String::new();
let mut created_at = String::new();
let mut ingredients = String::new();
let mut instructions = String::new();

for prop in &obj.props {
if let swc_ecma_ast::PropOrSpread::Prop(boxed_prop) = prop {
if let swc_ecma_ast::Prop::KeyValue(kv) = &**boxed_prop {
if let Some(key) = kv.key.as_ident() {
match &*kv.value {
Expr::Lit(swc_ecma_ast::Lit::Str(lit)) => {
// Handle regular string
match &*key.sym {
"name" => name = lit.value.to_string(),
"created_at" => {
created_at = lit.value.to_string()
}
"ingredients" => {
ingredients = lit.value.to_string()
}
"instructions" => {
instructions = lit.value.to_string()
}
_ => {}
}
}
Expr::Tpl(tpl) => {
// Handle template literals
let combined_string = tpl
.quasis
.iter()
.map(|quasi| {
quasi
.cooked
.as_ref()
.map(|c| c.to_string())
.unwrap_or_default()
})
.collect::<String>();

match &*key.sym {
"ingredients" => {
ingredients = combined_string
}
"instructions" => {
instructions = combined_string
}
_ => {}
}
}
_ => {}
}
}
}
}
}

if !name.is_empty()
&& !ingredients.is_empty()
&& !instructions.is_empty()
&& !created_at.is_empty()
{
Some(SavedRecipe {
name,
created_at,
ingredients,
instructions,
})
} else {
None
}
} else {
None
}
})
.collect();

return Some(list);
}
}
}
None
}

pub fn read_cookbook() -> Vec<SavedRecipe> {
let cm: Lrc<SourceMap> = Default::default();

let file_path = "./cookbook.ts";
let code = fs::read_to_string(file_path).expect("Failed to read file");

let fm = cm.new_source_file(FileName::Real(file_path.into()).into(), code);

let lexer = Lexer::new(
Syntax::Typescript(TsSyntax {
tsx: false,
..Default::default()
}),
Default::default(),
StringInput::from(&*fm),
None,
);
let mut parser = Parser::new_from(lexer);

let module = parser.parse_module().expect("Failed to parse module");

extract_default_export_list(&module).unwrap_or_default()
}
60 changes: 33 additions & 27 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
use chrono::Utc;
use std::fs;

use rusqlite::{params, Connection};
use std::error::Error;

pub fn file_exists(path: &str) -> bool {
fs::metadata(path).is_ok()
}

#[derive(Debug)]
pub struct Recipe {
pub name: String,
Expand All @@ -15,31 +9,43 @@ pub struct Recipe {
}

#[derive(Debug)]
pub struct DBRecipe {
pub id: i32,
pub struct SavedRecipe {
pub created_at: String,
pub name: String,
pub instructions: String,
pub ingredients: String,
}

pub fn save_recipe(recipe: &Recipe) -> Result<String, String> {
let res: Result<String, Box<dyn Error>> = {
let conn = Connection::open("cookbook.db").unwrap();

conn.execute(
"INSERT INTO recipes (name, instructions, ingredients) VALUES (?1, ?2, ?3)",
params![recipe.name, recipe.instructions, recipe.ingredients],
)
.unwrap();

conn.close().unwrap();
const FILE_PATH: &str = "./cookbook.ts";

Ok(format!("Saved {}", recipe.name))
};

match res {
Ok(res) => Ok(res),
Err(_err) => Err("Failed to save recipe".to_string()),
}
pub fn save_recipe(recipe: &Recipe) -> Result<String, String> {
// Read the existing TypeScript code
let mut source_code = fs::read_to_string(FILE_PATH).expect("Failed to read file");

let created_at_iso = Utc::now().to_rfc3339()[0..10].to_string();

// Create a new entry
let new_entry = format!(
r#" {{
name: "{name}",
ingredients: "{ingredients}",
instructions: "{instructions}",
created_at: "{created_at_iso}",
}},"#,
name = recipe.name,
ingredients = recipe.ingredients,
instructions = recipe.instructions,
created_at_iso = created_at_iso,
);

// Insert the new entry into the default export array
source_code = source_code.replace(
"] satisfies Recipe[];",
&format!("{}\n] satisfies Recipe[];", new_entry),
);

// Write the updated source code back to the original file
fs::write(FILE_PATH, source_code).expect("Failed to write updated code");

Ok("Saved recipe".to_string())
}
4 changes: 2 additions & 2 deletions src/recipes.rs → src/write_recipe.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use inquire::{Editor, Text};

use crate::utils::Recipe;
use crate::utils::{save_recipe, Recipe};

pub fn recipes() {
let answer = Text::new("Name of the recipe: ").prompt().unwrap();
Expand All @@ -18,5 +18,5 @@ pub fn recipes() {
ingredients,
instructions,
};
crate::utils::save_recipe(&recipe).unwrap();
let _ = save_recipe(&recipe);
}

0 comments on commit 82ab7db

Please sign in to comment.