Skip to content

Commit

Permalink
fix: make the library more ergonomic
Browse files Browse the repository at this point in the history
  • Loading branch information
suchapalaver committed Dec 31, 2023
1 parent 50ea3a3 commit fd8ea90
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 166 deletions.
14 changes: 7 additions & 7 deletions crates/api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ mod tests {
let api = Api::init(StoreType::SqliteInMem).await.unwrap();

let response = api
.dispatch(ApiCommand::Add(Add::Recipe { recipe: Recipe::new("fluffy american pancakes").unwrap(), ingredients: Ingredients::from_input_string("135g/4¾oz plain flour, 1 tsp baking powder, ½ tsp salt, 2 tbsp caster sugar, 130ml/4½fl oz milk, 1 large egg, lightly beaten, 2 tbsp melted butter (allowed to cool slightly), plus extra for cooking") }))
.dispatch(ApiCommand::Add(Add::Recipe { recipe: Recipe::new("fluffy american pancakes"), ingredients: Ingredients::from_input_string("135g/4¾oz plain flour, 1 tsp baking powder, ½ tsp salt, 2 tbsp caster sugar, 130ml/4½fl oz milk, 1 large egg, lightly beaten, 2 tbsp melted butter (allowed to cool slightly), plus extra for cooking") }))
.await
.unwrap();

Expand All @@ -252,9 +252,9 @@ mod tests {
insta::assert_display_snapshot!(response.to_string().trim(), @"fluffy american pancakes");

let response = api
.dispatch(ApiCommand::Read(Read::Recipe(
Recipe::new("fluffy american pancakes").unwrap(),
)))
.dispatch(ApiCommand::Read(Read::Recipe(Recipe::new(
"fluffy american pancakes",
))))
.await
.unwrap();

Expand Down Expand Up @@ -286,9 +286,9 @@ mod tests {
"###);

let response = api
.dispatch(ApiCommand::Delete(Delete::Recipe(
Recipe::new("fluffy american pancakes").unwrap(),
)))
.dispatch(ApiCommand::Delete(Delete::Recipe(Recipe::new(
"fluffy american pancakes",
))))
.await
.unwrap();

Expand Down
16 changes: 14 additions & 2 deletions crates/common/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,16 @@ impl Item {
self.recipes.as_ref()
}

pub fn recipes_mut(&mut self) -> Option<&mut [Recipe]> {
self.recipes.as_deref_mut()
pub fn add_recipe(&mut self, recipe: &str) {
self.recipes
.get_or_insert_with(Vec::new)
.push(recipe.into());
}

pub fn delete_recipe(&mut self, name: &str) {
if let Some(vec) = self.recipes.as_mut() {
vec.retain(|x| x.as_str() != name)
}
}

pub fn with_section(mut self, section: impl Into<String>) -> Self {
Expand Down Expand Up @@ -108,4 +116,8 @@ impl Section {
pub fn as_str(&self) -> &str {
&self.0
}

pub fn contains(&self, s: &Section) -> bool {
self.0.contains(s.as_str())
}
}
84 changes: 26 additions & 58 deletions crates/common/src/items.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::str::FromStr;

use serde::{Deserialize, Serialize};

use crate::{
item::{Item, Name, Section},
item::{Item, Section},
recipes::{Ingredients, Recipe},
Load, ReadError,
Load,
};

#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
Expand Down Expand Up @@ -51,27 +49,21 @@ impl Items {
self.collection.push(item);
}

pub fn delete_item(&mut self, name: &str) -> Result<(), ReadError> {
if let Ok(i) = self
pub fn delete_item(&mut self, name: &str) {
self.collection = self
.collection
.iter()
.position(|x| x.name() == &Name::from(name))
.ok_or(ReadError::ItemNotFound)
{
self.collection.remove(i);
}
Ok(())
.drain(..)
.filter(|item| item.name().as_str() != name)
.collect();
}

pub fn items(&self) -> impl Iterator<Item = &Item> {
self.sections
.iter()
.flat_map(|section| {
self.collection.iter().filter(|item| {
let Some(item_section) = item.section() else {
return false;
};
item_section.as_str().contains(section.as_str())
item.section()
.map_or(false, |item_section| item_section.contains(section))
})
})
.collect::<Vec<_>>()
Expand All @@ -82,59 +74,35 @@ impl Items {
self.recipes.iter()
}

pub fn add_recipe(&mut self, name: &str, ingredients: &str) -> Result<(), ReadError> {
let recipe = Recipe::from_str(name)?;

let ingredients = Ingredients::from_input_string(ingredients);

pub fn add_recipe(&mut self, name: &str, ingredients: &str) {
self.collection
.iter_mut()
.filter(|x| ingredients.contains(x.name()))
.for_each(|x| match x.recipes_mut() {
Some(recipes) => recipes.to_vec().push(recipe.clone()),
None => {
x.recipes_mut().replace(&mut [recipe.clone()]);
}
});

self.recipes.push(recipe);
Ok(())
.filter(|item| Ingredients::from_input_string(ingredients).contains(item.name()))
.for_each(|item| item.add_recipe(name));

self.recipes.push(name.into());
}

pub fn delete_recipe(&mut self, name: &str) -> Result<(), ReadError> {
if let Ok(i) = self
pub fn delete_recipe(&mut self, name: &str) {
self.recipes = self
.recipes
.iter()
.position(|recipe| recipe.as_str() == name)
.ok_or(ReadError::ItemNotFound)
{
self.recipes.remove(i);
}
.drain(..)
.filter(|recipe| recipe.as_str() != name)
.collect();

for item in &mut self.collection {
if let Some(recipes) = item.recipes_mut() {
if let Some(i) = recipes.iter().position(|recipe| recipe.as_str() == name) {
recipes.to_vec().remove(i);
}
}
item.delete_recipe(name);
}
Ok(())
}

pub fn recipe_ingredients(
&self,
recipe: &str,
) -> Result<impl Iterator<Item = &Item>, ReadError> {
let recipe = Recipe::from_str(recipe)?;
Ok(self
.collection
pub fn recipe_ingredients(&self, recipe: &Recipe) -> impl Iterator<Item = &Item> {
self.collection
.iter()
.filter(|item| {
let Some(recipes) = &item.recipes() else {
return false;
};
recipes.contains(&recipe)
item.recipes()
.map_or(false, |recipes| recipes.contains(recipe))
})
.collect::<Vec<_>>()
.into_iter())
.into_iter()
}
}
13 changes: 0 additions & 13 deletions crates/common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,8 @@ use std::{
};

use serde::Deserialize;
use serde_json::Value;
use thiserror::Error;

#[derive(Error, Debug)]
pub enum ReadError {
#[error("Invalid JSON: {0}")]
Json(Value),

#[error("Item not found")]
ItemNotFound,

#[error("No groceries library found")]
LibraryNotFound,
}

#[derive(Error, Debug)]
pub enum LoadError {
#[error("load error: {0}")]
Expand Down
65 changes: 22 additions & 43 deletions crates/common/src/list.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
use std::str::FromStr;

use crate::{
input::user_wants_to_add_item_to_list,
item::{Item, Name, SECTIONS},
item::{Item, SECTIONS},
items::Items,
recipes::Recipe,
Load, ReadError,
Load,
};
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -87,7 +85,7 @@ impl List {
}
}

pub fn add_groceries(&mut self, groceries: &Items) -> Result<(), ReadError> {
pub fn add_groceries(&mut self, groceries: &Items) {
// move everything off list to temp list
let list_items: Vec<Item> = self.items.drain(..).collect();
let sections = SECTIONS;
Expand All @@ -99,23 +97,18 @@ impl List {
.iter()
.filter(|item| item.section().is_some())
.filter(|item| {
if let Some(item_sec) = item.section() {
item_sec.as_str() == section
} else {
false
}
item.section()
.map_or(false, |item_sec| item_sec.as_str() == section)
})
.cloned()
.collect();

let b: Vec<Item> = groceries
.collection()
.filter(|item| {
if let Some(item_sec) = item.section() {
item.section().map_or(false, |item_sec| {
item_sec.as_str() == section && !a.contains(item)
} else {
false
}
})
})
.cloned()
.collect();
Expand Down Expand Up @@ -152,55 +145,41 @@ impl List {
}
}
}
Ok(())
}

pub fn add_item(&mut self, item: Item) {
self.items.push(item);
}

pub fn delete_groceries_item(&mut self, name: &str) -> Result<(), ReadError> {
if let Ok(i) = self
pub fn delete_groceries_item(&mut self, name: &str) {
self.items = self
.items
.iter()
.position(|x| x.name() == &Name::from(name))
.ok_or(ReadError::ItemNotFound)
{
self.items.remove(i);
}
Ok(())
.drain(..)
.filter(|item| item.name().as_str() != name)
.collect();
}

pub fn add_checklist_item(&mut self, item: Item) {
self.checklist.push(item);
}

pub fn delete_checklist_item(&mut self, name: &str) -> Result<(), ReadError> {
if let Ok(i) = self
pub fn delete_checklist_item(&mut self, name: &str) {
self.checklist = self
.checklist
.iter()
.position(|x| x.name() == &Name::from(name))
.ok_or(ReadError::ItemNotFound)
{
self.checklist.remove(i);
}
Ok(())
.drain(..)
.filter(|item| item.name().as_str() != name)
.collect();
}

pub fn add_recipe(&mut self, recipe: Recipe) {
self.recipes.push(recipe);
}

pub fn delete_recipe(&mut self, name: &str) -> Result<(), ReadError> {
let recipe = Recipe::from_str(name)?;
if let Ok(index) = self
pub fn delete_recipe(&mut self, name: &str) {
self.recipes = self
.recipes
.iter()
.position(|x| x == &recipe)
.ok_or(ReadError::ItemNotFound)
{
self.recipes.remove(index);
}
Ok(())
.drain(..)
.filter(|recipe| recipe.as_str() != name)
.collect();
}
}
18 changes: 5 additions & 13 deletions crates/common/src/recipes.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::{fmt, ops::Deref, str::FromStr};
use std::{fmt, ops::Deref};

use serde::{Deserialize, Serialize};

use crate::{item::Name, ReadError};
use crate::item::Name;

#[derive(Serialize, Deserialize, Clone, Debug, Default, Hash, Eq, PartialEq)]
pub struct Recipe(String);
Expand All @@ -14,8 +14,8 @@ impl fmt::Display for Recipe {
}

impl Recipe {
pub fn new(s: &str) -> Result<Self, ReadError> {
Self::from_str(s)
pub fn new(s: &str) -> Self {
s.into()
}

pub fn new_unchecked(s: impl Into<String>) -> Self {
Expand All @@ -29,15 +29,7 @@ impl Recipe {

impl From<&str> for Recipe {
fn from(s: &str) -> Self {
Self(s.to_string())
}
}

impl FromStr for Recipe {
type Err = ReadError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.trim().to_lowercase()))
Self(s.trim().to_lowercase())
}
}

Expand Down
4 changes: 0 additions & 4 deletions crates/gust/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use api::ApiError;
use clap::{builder::NonEmptyStringValueParser, Arg, Command, ValueHint};
use common::ReadError;
use thiserror::Error;

#[derive(Error, Debug)]
Expand All @@ -11,9 +10,6 @@ pub enum CliError {
#[error("invalid input: {0}")]
ParseInputError(String),

#[error("read error: {0}")]
ReadError(#[from] ReadError),

#[error("URL parse error: {0}")]
UrlParseError(#[from] url::ParseError),
}
Expand Down
Loading

0 comments on commit fd8ea90

Please sign in to comment.