use super::SearchError;
use mlua::{IntoLuaMulti, MultiValue, UserData, UserDataMethods};
use pasta_core::registry::{
DefaultRandomSelector, MockRandomSelector, RandomSelector, SceneRegistry, SceneTable,
WordDefRegistry, WordTable,
};
use std::collections::HashMap;
pub struct SearchContext {
scene_table: SceneTable,
word_table: WordTable,
}
impl SearchContext {
pub fn new(
scene_registry: SceneRegistry,
word_registry: WordDefRegistry,
) -> Result<Self, SearchError> {
let scene_table = SceneTable::from_scene_registry(
scene_registry,
Box::new(DefaultRandomSelector::new()),
)?;
let word_table = WordTable::from_word_def_registry(
word_registry,
Box::new(DefaultRandomSelector::new()),
);
Ok(Self {
scene_table,
word_table,
})
}
pub fn search_scene(
&mut self,
name: &str,
global_scene_name: Option<&str>,
) -> Result<Option<(String, String)>, SearchError> {
let filters = HashMap::new();
if let Some(parent) = global_scene_name {
match self
.scene_table
.resolve_scene_id_unified(parent, name, &filters)
{
Ok(scene_id) => {
let scene = self.scene_table.get_scene(scene_id).ok_or_else(|| {
SearchError::InvalidArgument("Scene ID not found".to_string())
})?;
let (global_name, local_name) = Self::parse_fn_name(&scene.fn_name);
Ok(Some((global_name, local_name)))
}
Err(pasta_core::SceneTableError::SceneNotFound { .. }) => Ok(None),
Err(pasta_core::SceneTableError::NoMatchingScene { .. }) => Ok(None),
Err(pasta_core::SceneTableError::NoMoreScenes { .. }) => Ok(None),
Err(e) => Err(SearchError::SceneTableError(e)),
}
} else {
match self.scene_table.resolve_scene_id(name, &filters) {
Ok(scene_id) => {
let scene = self.scene_table.get_scene(scene_id).ok_or_else(|| {
SearchError::InvalidArgument("Scene ID not found".to_string())
})?;
let (global_name, _) = Self::parse_fn_name(&scene.fn_name);
Ok(Some((global_name, "__start__".to_string())))
}
Err(pasta_core::SceneTableError::SceneNotFound { .. }) => Ok(None),
Err(pasta_core::SceneTableError::NoMatchingScene { .. }) => Ok(None),
Err(pasta_core::SceneTableError::NoMoreScenes { .. }) => Ok(None),
Err(e) => Err(SearchError::SceneTableError(e)),
}
}
}
fn parse_fn_name(fn_name: &str) -> (String, String) {
if let Some((global_part, local_part)) = fn_name.split_once("::") {
let local_name = if local_part == "__start__" {
"__start__".to_string()
} else {
local_part.to_string()
};
(global_part.to_string(), local_name)
} else {
(fn_name.to_string(), "__start__".to_string())
}
}
pub fn search_word(
&mut self,
name: &str,
global_scene_name: Option<&str>,
) -> Result<Option<String>, SearchError> {
let module_name = global_scene_name.unwrap_or("");
match self.word_table.search_word(module_name, name, &[]) {
Ok(word) => Ok(Some(word)),
Err(pasta_core::WordTableError::WordNotFound { .. }) => Ok(None),
}
}
pub fn set_scene_selector(&mut self, sequence: Option<Vec<usize>>) -> Result<(), SearchError> {
let selector: Box<dyn RandomSelector> = match sequence {
Some(seq) => Box::new(MockRandomSelector::new(seq)),
None => Box::new(DefaultRandomSelector::new()),
};
self.scene_table.replace_selector(selector);
Ok(())
}
pub fn set_word_selector(&mut self, sequence: Option<Vec<usize>>) -> Result<(), SearchError> {
let selector: Box<dyn RandomSelector> = match sequence {
Some(seq) => Box::new(MockRandomSelector::new(seq)),
None => Box::new(DefaultRandomSelector::new()),
};
self.word_table.replace_selector(selector);
Ok(())
}
}
impl UserData for SearchContext {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut(
"search_scene",
|lua, this, (name, global_scene_name): (String, Option<String>)| match this
.search_scene(&name, global_scene_name.as_deref())
{
Ok(Some((global, local))) => (global, local).into_lua_multi(lua),
Ok(None) => Ok(MultiValue::new()),
Err(e) => Err(mlua::Error::from(e)),
},
);
methods.add_method_mut(
"search_word",
|lua, this, (name, global_scene_name): (String, Option<String>)| match this
.search_word(&name, global_scene_name.as_deref())
{
Ok(Some(word)) => word.into_lua_multi(lua),
Ok(None) => Ok(MultiValue::new()),
Err(e) => Err(mlua::Error::from(e)),
},
);
methods.add_method_mut("set_scene_selector", |_lua, this, args: MultiValue| {
if args.is_empty() {
this.set_scene_selector(None).map_err(mlua::Error::from)?;
} else {
let sequence: Result<Vec<usize>, _> = args
.iter()
.map(|v| {
v.as_integer()
.ok_or_else(|| {
mlua::Error::RuntimeError("expected integer argument".into())
})
.map(|i| i as usize)
})
.collect();
this.set_scene_selector(Some(sequence?))
.map_err(mlua::Error::from)?;
}
Ok(())
});
methods.add_method_mut("set_word_selector", |_lua, this, args: MultiValue| {
if args.is_empty() {
this.set_word_selector(None).map_err(mlua::Error::from)?;
} else {
let sequence: Result<Vec<usize>, _> = args
.iter()
.map(|v| {
v.as_integer()
.ok_or_else(|| {
mlua::Error::RuntimeError("expected integer argument".into())
})
.map(|i| i as usize)
})
.collect();
this.set_word_selector(Some(sequence?))
.map_err(mlua::Error::from)?;
}
Ok(())
});
}
}