use bevy_brp_mcp_macros::ParamStruct;
use bevy_brp_mcp_macros::ResultStruct;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use serde::de::Error;
use serde_json::Value;
use crate::brp_tools::Port;
use crate::brp_tools::constants::COMPONENT_SELECTOR_ALL;
#[derive(Clone, Debug, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ComponentSelector {
All,
#[serde(untagged)]
Paths(Vec<String>),
}
impl<'de> Deserialize<'de> for ComponentSelector {
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let value = serde_json::Value::deserialize(deserializer)?;
match value {
Value::String(ref selector) if selector == COMPONENT_SELECTOR_ALL => Ok(Self::All),
Value::Array(arr) => {
let paths = arr
.into_iter()
.map(|v| {
v.as_str()
.ok_or_else(|| {
Error::custom(
"option array must contain only strings (component type paths)",
)
})
.map(String::from)
})
.collect::<core::result::Result<Vec<_>, _>>()?;
Ok(Self::Paths(paths))
},
_ => Err(Error::custom(
"option field must be either the string \"all\" or an array of component type \
paths like [\"bevy_transform::components::transform::Transform\"]",
)),
}
}
}
impl Default for ComponentSelector {
fn default() -> Self { Self::Paths(vec![]) }
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
pub struct BrpQuery {
#[serde(default)]
pub components: Vec<String>,
#[serde(default)]
pub option: ComponentSelector,
#[serde(default)]
pub has: Vec<String>,
}
#[derive(Clone, Debug, Default, Deserialize, Serialize, JsonSchema)]
pub struct BrpQueryFilter {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub with: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub without: Vec<String>,
}
#[derive(Clone, Deserialize, Serialize, JsonSchema, ParamStruct)]
pub struct QueryParams {
pub data: BrpQuery,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter: Option<BrpQueryFilter>,
#[serde(skip_serializing_if = "Option::is_none")]
pub strict: Option<bool>,
#[serde(default)]
pub port: Port,
}
#[derive(Serialize, ResultStruct)]
#[brp_result]
pub struct QueryResult {
#[serde(skip_serializing_if = "Option::is_none")]
#[to_result(skip_if_none)]
pub result: Option<Value>,
#[serde(rename = "entity_count")]
#[to_metadata(result_operation = "count")]
pub entities: usize,
#[serde(rename = "component_count")]
#[to_metadata(result_operation = "count_query_components")]
pub components: usize,
#[to_message(message_template = "Found {entity_count} entities")]
pub message_template: String,
}