Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: yeasts to be found with function #120

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions rustybeer-cli/src/commands/yeast.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use rustybeer::yeasts::YEASTS;
use rustybeer::conversions::TemperatureParser;
use rustybeer::measurements::Temperature;
use rustybeer::yeasts::{get_yeasts, Criteria, Yeast};
use structopt::StructOpt;

#[derive(Debug, StructOpt)]
Expand All @@ -8,19 +10,52 @@ pub struct YeastOptions {
#[structopt(short, long)]
/// Search by yeast name
name: Option<String>,
#[structopt(short, long)]
/// Search by yeast producer
company: Option<String>,
#[structopt(short, long)]
/// Search by yeast attenuation
attenuation: Option<u8>,
#[structopt(short, long, parse(try_from_str = TemperatureParser::parse))]
/// Search by yeast optimal temperature
temperature: Option<Temperature>,
}

pub fn search_and_print(opt: YeastOptions) {
if let Some(name) = opt.name {
let criteria = name.to_lowercase();
for yeast in YEASTS.iter() {
if yeast.name.to_lowercase().contains(&criteria) {
println!("{:?}", yeast);
}
}
} else {
for yeast in YEASTS.iter() {
println!("{:?}", yeast);
}
let criteria = Criteria {
name: opt.name,
company: opt.company,
attenuation: opt.attenuation,
temperature: opt.temperature,
};

let resp: Vec<&Yeast> = get_yeasts(Some(criteria));

if resp.is_empty() {
println!("Could not find any yeasts matching criteria");
return;
}

println!("Found the following yeasts with criteria:");
for x in &resp {
println!("---------------------");
println!("{}", x.name);
println!("{}\n", x.company);
println!(
"Attenuation: {}-{}",
x.min_attenuation.unwrap_or(0),
x.max_attenuation.unwrap_or(0)
);
println!(
"Temperature: {}-{}",
x.min_temp
.unwrap_or(Temperature::from_celsius(0.0))
.as_celsius(),
x.max_temp
.unwrap_or(Temperature::from_celsius(0.0))
.as_celsius()
);
println!("Alcohol tolerance: {}", x.alc_tolerance.unwrap_or(0));
}
println!("---------------------");
}
16 changes: 12 additions & 4 deletions rustybeer-server/src/handlers/yeasts.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use rustybeer::conversions::TemperatureParser;
use rustybeer::conversions::ToMap;
pub use rustybeer::yeasts::{Criteria, Yeast, YEASTS};
pub use rustybeer::yeasts::{get_yeasts, Criteria, Yeast};
use rweb::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
Expand Down Expand Up @@ -75,16 +76,23 @@ impl YeastResponse {
)]
pub fn search(q: Query<YeastQuery>) -> Json<Vec<YeastResponse>> {
let query = q.into_inner();
let mut temp = None;
if query.temperature.is_some() {
temp = match TemperatureParser::parse(&query.temperature.unwrap()) {
Ok(temperature) => Some(temperature),
Err(_) => None,
};
}

let criteria = Criteria {
name: query.name,
company: query.company,
attenuation: query.attenuation,
temperature: query.temperature,
temperature: temp,
};

let resp: Vec<YeastResponse> = YEASTS
let resp: Vec<YeastResponse> = get_yeasts(Some(criteria))
.iter()
.filter(|yeast| criteria.matches(yeast))
.map(|yeast| YeastResponse::from_yeast(&yeast))
.collect();

Expand Down
23 changes: 13 additions & 10 deletions rustybeer/src/yeasts.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::conversions::TemperatureParser;
use crate::strings::contains_case_insensitive;
/// Yeast list curated from https://www.brewersfriend.com/yeast/
use measurements::temperature::Temperature;
Expand Down Expand Up @@ -73,13 +72,17 @@ where
}

static YEASTS_JSON: &str = include_str!("json/yeasts.json");

/// All available yeasts.
///
/// Data will be loaded from JSON on the first use.
pub static YEASTS: Lazy<Vec<Yeast>> =
static YEASTS: Lazy<Vec<Yeast>> =
Lazy::new(|| serde_json::from_str(YEASTS_JSON).expect("yeasts data could not be deserialised"));

pub fn get_yeasts(criteria: Option<Criteria>) -> Vec<&'static Yeast> {
let crit = criteria.unwrap_or_default();
YEASTS.iter().filter(|yeast| crit.matches(yeast)).collect()
}

/// Criteria for selecting a yeast.
///
/// If an attribute is `None`, it is ignored.
Expand All @@ -88,7 +91,7 @@ pub struct Criteria {
pub company: Option<String>,
pub name: Option<String>,
pub attenuation: Option<u8>,
pub temperature: Option<String>,
pub temperature: Option<Temperature>,
}

impl Criteria {
Expand All @@ -114,11 +117,11 @@ impl Criteria {
}
}

if let Some(temperature) = &self.temperature {
if let Ok(temp) = TemperatureParser::parse(temperature) {
if temp < yeast.min_temp.unwrap_or(temp) || temp > yeast.max_temp.unwrap_or(temp) {
return false;
}
if let Some(temperature) = self.temperature {
if temperature < yeast.min_temp.unwrap_or(temperature)
|| temperature > yeast.max_temp.unwrap_or(temperature)
{
return false;
}
}

Expand Down Expand Up @@ -164,7 +167,7 @@ mod tests {
// Inclusive values match
criteria.attenuation = Some(7);
assert!(criteria.matches(&TEST_YEAST));
criteria.temperature = Some("20C".to_owned());
criteria.temperature = Some(Temperature::from_celsius(20.0).to_owned());
assert!(criteria.matches(&TEST_YEAST));
criteria.company = Some("yeast".to_owned());
assert!(criteria.matches(&TEST_YEAST));
Expand Down