Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
17 changes: 15 additions & 2 deletions raphael-cli/src/commands/search_mission.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use clap::Args;
use raphael_data::{
RECIPES, STELLAR_MISSIONS, StellarMission, get_job_name, get_stellar_mission_name,
RECIPES, STELLAR_MISSIONS, StellarMission, StellarSearchQuery, get_job_id, get_job_name,
get_stellar_mission_name,
};

use crate::commands::Language;
Expand All @@ -11,6 +12,10 @@ pub struct SearchArgs {
#[arg(short, long, required_unless_present_any(["recipe_id", "item_id", "mission_id"]), conflicts_with_all(["recipe_id", "item_id"]))]
pub pattern: Option<String>,

/// Job name to limit searches to
#[arg(short, long, requires("pattern"), conflicts_with_all(["recipe_id", "item_id"]))]
pub job: Option<String>,

/// Recipe ID to search for
#[arg(short, long, required_unless_present_any(["pattern", "item_id", "mission_id"]), conflicts_with = "item_id")]
pub recipe_id: Option<u32>,
Expand Down Expand Up @@ -44,7 +49,15 @@ pub fn execute(args: &SearchArgs) {
);
}
if let Some(pattern_arg) = &args.pattern {
matches.extend(raphael_data::find_stellar_missions(pattern_arg, locale));
let job_id = args
.job
.as_ref()
.and_then(|job_name| Some(get_job_id(job_name, locale).expect("Unknown job!")));
matches.extend(raphael_data::find_stellar_missions(StellarSearchQuery {
text: pattern_arg,
locale,
job_id,
}));
}
if let Some(recipe_id_arg) = args.recipe_id {
matches.extend(
Expand Down
19 changes: 17 additions & 2 deletions raphael-cli/src/commands/search_recipe.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use clap::Args;
use raphael_data::{RECIPES, Recipe, STELLAR_MISSIONS, get_job_name, get_raw_item_name};
use raphael_data::{
RECIPES, Recipe, RecipeSearchQuery, STELLAR_MISSIONS, get_job_id, get_job_name,
get_raw_item_name,
};

use crate::commands::Language;

Expand All @@ -9,6 +12,10 @@ pub struct SearchArgs {
#[arg(short, long, required_unless_present_any(["recipe_id", "item_id", "mission_id"]), conflicts_with_all(["recipe_id", "item_id"]))]
pub pattern: Option<String>,

/// Job name to limit searches to
#[arg(short, long, requires("pattern"), conflicts_with_all(["recipe_id", "item_id"]))]
pub job: Option<String>,

/// Recipe ID to search for
#[arg(short, long, required_unless_present_any(["pattern", "item_id", "mission_id"]), conflicts_with = "item_id")]
pub recipe_id: Option<u32>,
Expand Down Expand Up @@ -55,7 +62,15 @@ pub fn execute(args: &SearchArgs) {
}
}
if let Some(pattern_arg) = &args.pattern {
matches.extend(raphael_data::find_recipes(pattern_arg, locale));
let job_id = args
.job
.as_ref()
.and_then(|job_name| Some(get_job_id(job_name, locale).expect("Unknown job!")));
matches.extend(raphael_data::find_recipes(RecipeSearchQuery {
text: pattern_arg,
locale,
job_id,
}));
}
if let Some(recipe_id_arg) = args.recipe_id {
matches.extend(
Expand Down
16 changes: 14 additions & 2 deletions raphael-data/benches/bench_game_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,25 @@ fn bench_random_access(c: &mut Criterion) {

fn bench_find_recipes(c: &mut Criterion) {
c.bench_function("find_recipes", |b| {
b.iter(|| find_recipes("", Locale::EN));
b.iter(|| {
find_recipes(RecipeSearchQuery {
text: "",
locale: Locale::EN,
job_id: None,
})
});
});
}

fn bench_find_stellar_missions(c: &mut Criterion) {
c.bench_function("find_stellar_missions", |b| {
b.iter(|| find_stellar_missions("", Locale::EN));
b.iter(|| {
find_stellar_missions(StellarSearchQuery {
text: "",
locale: Locale::EN,
job_id: None,
})
});
});
}

Expand Down
16 changes: 16 additions & 0 deletions raphael-data/src/locales.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,22 @@ pub fn get_job_name(job_id: u8, locale: Locale) -> &'static str {
}
}

pub fn get_job_id(job_name: &str, locale: Locale) -> Option<u8> {
let job_names = match locale {
Locale::EN => JOB_NAMES_EN,
Locale::DE => JOB_NAMES_DE,
Locale::FR => JOB_NAMES_FR,
Locale::JP => JOB_NAMES_JP,
Locale::CN => JOB_NAMES_CN,
Locale::KR => JOB_NAMES_KR,
Locale::TW => JOB_NAMES_TW,
};
job_names
.iter()
.position(|&name| name == job_name)
.map(|i| i as u8)
}

pub const ITEM_NAMES_EN: NciArray<u32, &str> = include!("../data/item_names_en.rs");
pub const ITEM_NAMES_DE: NciArray<u32, &str> = include!("../data/item_names_de.rs");
pub const ITEM_NAMES_FR: NciArray<u32, &str> = include!("../data/item_names_fr.rs");
Expand Down
40 changes: 29 additions & 11 deletions raphael-data/src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ impl<T> AsRef<str> for MatcherCandidate<T> {
}
}

#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub struct RecipeSearchQuery<'a> {
pub text: &'a str,
pub locale: crate::Locale,
pub job_id: Option<u8>,
}

fn preprocess_pattern(pattern: &str) -> String {
pattern
.chars()
Expand All @@ -37,17 +44,17 @@ fn preprocess_pattern(pattern: &str) -> String {
}

pub type RecipeSearchEntry = (u32, &'static crate::Recipe);
pub fn find_recipes(
search_string: &str,
locale: crate::Locale,
) -> impl Iterator<Item = RecipeSearchEntry> {
pub fn find_recipes(query: RecipeSearchQuery) -> impl Iterator<Item = RecipeSearchEntry> {
let pattern = Pattern::parse(
&preprocess_pattern(search_string),
&preprocess_pattern(query.text),
CaseMatching::Ignore,
Normalization::Smart,
);
let entries = RECIPES.entries().filter_map(|(recipe_id, recipe)| {
let item_name = get_raw_item_name(recipe.item_id, locale)?;
let item_name = get_raw_item_name(recipe.item_id, query.locale)?;
if query.job_id.is_some_and(|job_id| job_id != recipe.job_id) {
return None;
}
Some(MatcherCandidate {
haystack: item_name,
associated_data: (recipe_id, recipe),
Expand All @@ -59,20 +66,28 @@ pub fn find_recipes(
.map(|(entry, _score)| entry.associated_data)
}

#[derive(Debug, Clone, Copy, PartialEq, Hash)]
pub struct StellarSearchQuery<'a> {
pub text: &'a str,
pub locale: crate::Locale,
pub job_id: Option<u8>,
}
pub type StellarMissionSearchEntry = (u32, &'static crate::StellarMission);
pub fn find_stellar_missions(
search_string: &str,
locale: crate::Locale,
query: StellarSearchQuery,
) -> impl Iterator<Item = StellarMissionSearchEntry> {
let pattern = Pattern::parse(
&preprocess_pattern(search_string),
&preprocess_pattern(query.text),
CaseMatching::Ignore,
Normalization::Smart,
);
let mission_entries = STELLAR_MISSIONS
.entries()
.filter_map(|(mission_id, mission)| {
let mission_name = get_stellar_mission_name(mission_id, locale)?;
let mission_name = get_stellar_mission_name(mission_id, query.locale)?;
if query.job_id.is_some_and(|job_id| job_id != mission.job_id) {
return None;
}
Some(MatcherCandidate {
haystack: mission_name,
associated_data: (mission_id, mission),
Expand All @@ -83,7 +98,10 @@ pub fn find_stellar_missions(
.flat_map(|(mission_id, mission)| {
mission.recipe_ids.iter().filter_map(move |&recipe_id| {
let recipe = RECIPES.get(recipe_id)?;
let item_name = get_raw_item_name(recipe.item_id, locale)?;
let item_name = get_raw_item_name(recipe.item_id, query.locale)?;
if query.job_id.is_some_and(|job_id| job_id != recipe.job_id) {
return None;
}
Some(MatcherCandidate {
haystack: item_name,
associated_data: (mission_id, mission),
Expand Down
73 changes: 68 additions & 5 deletions raphael-data/tests/test_recipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ fn all_recipe_items_exist() {
fn find_recipes_exact(
item_name: &str,
locale: raphael_data::Locale,
job_id: Option<u8>,
) -> impl Iterator<Item = &'static raphael_data::Recipe> {
raphael_data::find_recipes(item_name, locale).filter_map(move |(_recipe_id, recipe)| {
raphael_data::find_recipes(RecipeSearchQuery {
text: item_name,
locale,
job_id,
})
.filter_map(move |(_recipe_id, recipe)| {
let recipe_item_name = raphael_data::get_raw_item_name(recipe.item_id, locale).unwrap();
if item_name == recipe_item_name {
Some(recipe)
Expand All @@ -40,7 +46,8 @@ fn find_recipes_exact(

#[test]
fn medical_supplies() {
let matching_recipes = find_recipes_exact("Medical Supplies", Locale::EN).collect::<Vec<_>>();
let matching_recipes =
find_recipes_exact("Medical Supplies", Locale::EN, None).collect::<Vec<_>>();
let expected = expect![[r#"
[
Recipe {
Expand Down Expand Up @@ -362,7 +369,7 @@ fn medical_supplies() {

#[test]
fn ipe_lumber() {
let matching_recipes = find_recipes_exact("Ipe Lumber", Locale::EN).collect::<Vec<_>>();
let matching_recipes = find_recipes_exact("Ipe Lumber", Locale::EN, None).collect::<Vec<_>>();
let expected = expect![[r#"
[
Recipe {
Expand Down Expand Up @@ -412,7 +419,7 @@ fn ipe_lumber() {
#[test]
fn uncharted_course_resin() {
let matching_recipes =
find_recipes_exact("Uncharted Course Resin", Locale::EN).collect::<Vec<_>>();
find_recipes_exact("Uncharted Course Resin", Locale::EN, None).collect::<Vec<_>>();
let expected = expect![[r#"
[
Recipe {
Expand Down Expand Up @@ -500,7 +507,63 @@ fn uncharted_course_resin() {

#[test]
fn habitat_chair() {
let matching_recipes = find_recipes_exact("Habitat Chair", Locale::EN).collect::<Vec<_>>();
let matching_recipes =
find_recipes_exact("Habitat Chair", Locale::EN, None).collect::<Vec<_>>();
let expected = expect![[r#"
[
Recipe {
job_id: 0,
item_id: 48295,
max_level_scaling: 100,
recipe_level: 690,
progress_factor: 54,
quality_factor: 87,
durability_factor: 88,
material_factor: 0,
ingredients: [
Ingredient {
item_id: 0,
amount: 0,
},
Ingredient {
item_id: 0,
amount: 0,
},
Ingredient {
item_id: 0,
amount: 0,
},
Ingredient {
item_id: 0,
amount: 0,
},
Ingredient {
item_id: 0,
amount: 0,
},
Ingredient {
item_id: 0,
amount: 0,
},
],
is_expert: false,
req_craftsmanship: 0,
req_control: 0,
},
]
"#]];
expected.assert_debug_eq(&matching_recipes);
}

#[test]
fn habitat_chair_jobid() {
let results_with_wrong_job =
find_recipes_exact("Habitat Chair", Locale::EN, Some(5)).collect::<Vec<_>>();
assert!(results_with_wrong_job.is_empty());

let matching_recipes =
find_recipes_exact("Habitat Chair", Locale::EN, Some(0)).collect::<Vec<_>>();
assert_eq!(matching_recipes.len(), 1);
let expected = expect![[r#"
[
Recipe {
Expand Down
Loading
Loading