use super::{
Json, LuaSerdeExt, MetaMethod, Result, ToolId, UserData, UserDataFields, UserDataMethods,
Value, json,
};
pub(crate) trait ToolResolver: Send + Sync {
fn resolve(&self, description: &str) -> Result<ToolId>;
}
impl<F> ToolResolver for F
where
F: Fn(&str) -> Result<ToolId> + Send + Sync,
{
fn resolve(&self, description: &str) -> Result<ToolId> {
self(description)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ToolBinding {
pub(crate) alias: String,
pub(crate) description: String,
pub(crate) id: ToolId,
pub(crate) model_description: Option<String>,
}
impl ToolBinding {
#[cfg(test)]
pub(crate) fn for_test(alias: &str, description: &str, id: ToolId) -> Self {
Self {
alias: alias.to_owned(),
description: description.to_owned(),
id,
model_description: None,
}
}
#[must_use]
pub(crate) fn alias(&self) -> &str {
&self.alias
}
#[must_use]
pub(crate) fn description(&self) -> &str {
&self.description
}
#[must_use]
pub(crate) fn id(&self) -> &ToolId {
&self.id
}
#[must_use]
pub(crate) fn model_description(&self) -> Option<&str> {
self.model_description.as_deref()
}
}
#[derive(Debug, Clone, PartialEq)]
pub(crate) struct LuaToolHandle {
name: String,
description: String,
description_overridden: bool,
parameters: Json,
wire_name: String,
untrusted: bool,
}
impl LuaToolHandle {
#[must_use]
pub(crate) fn from_binding(
alias: impl Into<String>,
description: impl Into<String>,
id: &ToolId,
) -> Self {
Self {
name: alias.into(),
description: description.into(),
description_overridden: false,
parameters: json!({}),
wire_name: id.name().to_owned(),
untrusted: false,
}
}
pub(crate) fn from_live_binding(
alias: impl Into<String>,
description: impl Into<String>,
tool: &dyn crate::tools::Tool,
) -> Self {
Self {
name: alias.into(),
description: description.into(),
description_overridden: false,
parameters: tool.parameters_schema(),
wire_name: tool.wire_name().to_owned(),
untrusted: false,
}
}
#[must_use]
pub(crate) fn name(&self) -> &str {
&self.name
}
#[must_use]
pub(crate) fn model_description_override(&self) -> Option<&str> {
self.description_overridden
.then_some(self.description.as_str())
}
}
impl UserData for LuaToolHandle {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("name", |_, this| Ok(this.name.clone()));
fields.add_field_method_get("description", |_, this| Ok(this.description.clone()));
fields.add_field_method_set("description", |_, this, value: String| {
this.description = value;
this.description_overridden = true;
Ok(())
});
fields.add_field_method_get("parameters", |lua, this| lua.to_value(&this.parameters));
fields.add_field_method_get("wire_name", |_, this| Ok(this.wire_name.clone()));
fields.add_field_method_get("untrusted", |_, this| Ok(this.untrusted));
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LuaSectionHandle {
name: String,
heading: String,
has_prose: bool,
}
impl LuaSectionHandle {
#[must_use]
pub(crate) fn new(name: impl Into<String>, has_prose: bool) -> Self {
let name = name.into();
let heading = format!("## {name}");
Self {
name,
heading,
has_prose,
}
}
#[must_use]
pub(crate) fn heading(&self) -> &str {
&self.heading
}
}
impl UserData for LuaSectionHandle {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("name", |_, this| Ok(this.name.clone()));
fields.add_field_method_get("has_prose", |_, this| Ok(this.has_prose));
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LuaFanoutResult {
text: String,
ok: bool,
item: String,
exhausted: bool,
}
impl LuaFanoutResult {
#[must_use]
pub(crate) fn success(item: impl Into<String>, text: impl Into<String>) -> Self {
Self {
text: text.into(),
ok: true,
item: item.into(),
exhausted: false,
}
}
#[must_use]
pub(crate) fn exhausted_stub(item: impl Into<String>, text: impl Into<String>) -> Self {
Self {
text: text.into(),
ok: false,
item: item.into(),
exhausted: true,
}
}
}
impl UserData for LuaFanoutResult {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("text", |_, this| Ok(this.text.clone()));
fields.add_field_method_get("ok", |_, this| Ok(this.ok));
fields.add_field_method_get("item", |_, this| Ok(this.item.clone()));
fields.add_field_method_get("exhausted", |_, this| Ok(this.exhausted));
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::ToString, |_, this, ()| Ok(this.text.clone()));
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) enum LuaBlockResult {
Returned(Option<String>),
Jump(String),
}
pub(crate) fn resolve_section_target(value: Value) -> mlua::Result<String> {
match value {
Value::String(s) => Ok(s.to_str()?.to_owned()),
Value::UserData(ud) => {
let handle = ud.borrow::<LuaSectionHandle>()?;
Ok(handle.heading().to_owned())
}
other => Err(mlua::Error::external(format!(
"section target must be a string or Section object, got {}",
other.type_name()
))),
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub(crate) struct ToolBindings {
pub(crate) bindings: Vec<ToolBinding>,
pub(crate) always: Vec<String>,
}
impl ToolBindings {
#[cfg(test)]
pub(crate) fn for_test(bindings: Vec<ToolBinding>, always: Vec<String>) -> Self {
Self { bindings, always }
}
#[must_use]
pub(crate) fn bindings(&self) -> &[ToolBinding] {
&self.bindings
}
#[must_use]
pub(crate) fn always(&self) -> &[String] {
&self.always
}
pub(crate) fn binding(&self, alias: &str) -> Option<&ToolBinding> {
self.bindings.iter().find(|binding| binding.alias == alias)
}
}