use rig::{completion::ToolDefinition, tool::Tool};
use serde::{Deserialize, Serialize};
use serde_json::json;
use crate::{
error::SenseiError,
lua,
rcon_ext::{execute_lua_json, SharedRcon},
};
pub struct GetNearbyEntities {
pub(crate) rcon: SharedRcon,
}
impl GetNearbyEntities {
pub const fn new(rcon: SharedRcon) -> Self {
Self { rcon }
}
}
#[derive(Debug, Deserialize)]
pub struct GetNearbyEntitiesArgs {
pub radius: Option<f64>,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct NearbyEntity {
pub name: String,
#[serde(rename = "type")]
pub kind: String,
pub x: f64,
pub y: f64,
}
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct NearbyEntities {
pub entities: Vec<NearbyEntity>,
}
impl Tool for GetNearbyEntities {
const NAME: &'static str = "get_nearby_entities";
type Error = SenseiError;
type Args = GetNearbyEntitiesArgs;
type Output = NearbyEntities;
async fn definition(&self, _prompt: String) -> ToolDefinition {
ToolDefinition {
name: "get_nearby_entities".to_string(),
description: "Get buildings and structures near the player (excludes resources, trees, and decoratives). Returns up to 50 entities.".to_string(),
parameters: json!({
"type": "object",
"properties": {
"radius": {
"type": "number",
"description": "Search radius in tiles (default: 20)"
}
}
}),
}
}
async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
let radius = args.radius.unwrap_or(20.0);
let lua = lua::nearby_entities(radius);
let json = execute_lua_json(&self.rcon, &lua).await?;
Ok(serde_json::from_str(&json)?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_entities() {
let json = r#"{"entities":[
{"name":"stone-furnace","type":"furnace","x":1.5,"y":2.5},
{"name":"transport-belt","type":"transport-belt","x":3.0,"y":4.0}
]}"#;
let result: NearbyEntities = serde_json::from_str(json).unwrap();
assert_eq!(result.entities.len(), 2);
assert_eq!(result.entities[0].name, "stone-furnace");
assert_eq!(result.entities[0].kind, "furnace");
}
#[test]
fn test_parse_empty_entities() {
let json = r#"{"entities":[]}"#;
let result: NearbyEntities = serde_json::from_str(json).unwrap();
assert!(result.entities.is_empty());
}
}