use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ActionType {
Delete,
Replace,
Expand,
Query,
Read,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Confidence {
High,
Medium,
Low,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SuggestedAction {
pub action_type: ActionType,
pub confidence: Confidence,
pub reason: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub params: Option<HashMap<String, serde_json::Value>>,
}
impl Confidence {
pub fn calculate(is_unique: bool, has_file: bool, has_kind: bool, is_ambiguous: bool) -> Self {
if is_unique && has_file && has_kind && !is_ambiguous {
return Confidence::High;
}
if is_ambiguous || (!has_file && !is_unique) {
return Confidence::Low;
}
if (has_file || has_kind) && !is_ambiguous {
return Confidence::Medium;
}
Confidence::Low
}
}
pub fn suggest_action(
action_type: ActionType,
symbol_name: &str,
symbol_kind: &str,
file_path: &str,
confidence: Confidence,
) -> SuggestedAction {
let reason = generate_reason(action_type, symbol_name, symbol_kind, file_path);
let params = generate_params(action_type);
SuggestedAction {
action_type,
confidence,
reason,
params,
}
}
fn generate_reason(
action_type: ActionType,
symbol_name: &str,
symbol_kind: &str,
file_path: &str,
) -> String {
match action_type {
ActionType::Delete => {
format!(
"Delete symbol '{}' ({}) at {}",
symbol_name, symbol_kind, file_path
)
}
ActionType::Replace => {
format!("Replace symbol '{}' with provided content", symbol_name)
}
ActionType::Expand => {
format!(
"Expand symbol '{}' to include full context (2 levels)",
symbol_name
)
}
ActionType::Query => {
format!("Query for symbols matching specified labels")
}
ActionType::Read => {
format!(
"Read symbol '{}' ({}) at {}",
symbol_name, symbol_kind, file_path
)
}
}
}
fn generate_params(action_type: ActionType) -> Option<HashMap<String, serde_json::Value>> {
let mut params = HashMap::new();
match action_type {
ActionType::Delete => {
params.insert(
"remove_references".to_string(),
serde_json::Value::Bool(true),
);
}
ActionType::Replace => {
params.insert(
"preserve_signature".to_string(),
serde_json::Value::Bool(true),
);
}
ActionType::Expand => {
params.insert("levels".to_string(), serde_json::Value::Number(2.into()));
}
ActionType::Query => {
params.insert(
"include_context".to_string(),
serde_json::Value::Bool(false),
);
}
ActionType::Read => {
params.insert(
"include_metadata".to_string(),
serde_json::Value::Bool(true),
);
}
}
Some(params)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_confidence_high() {
let confidence = Confidence::calculate(true, true, true, false);
assert_eq!(confidence, Confidence::High);
}
#[test]
fn test_confidence_medium() {
let confidence = Confidence::calculate(false, true, true, false);
assert_eq!(confidence, Confidence::Medium);
let confidence = Confidence::calculate(true, false, true, false);
assert_eq!(confidence, Confidence::Medium);
}
#[test]
fn test_confidence_low() {
let confidence = Confidence::calculate(false, false, false, true);
assert_eq!(confidence, Confidence::Low);
let confidence = Confidence::calculate(false, false, false, false);
assert_eq!(confidence, Confidence::Low);
}
#[test]
fn test_action_type_serialization() {
let delete = serde_json::to_string(&ActionType::Delete).unwrap();
assert_eq!(delete, "\"delete\"");
let replace = serde_json::to_string(&ActionType::Replace).unwrap();
assert_eq!(replace, "\"replace\"");
let expand = serde_json::to_string(&ActionType::Expand).unwrap();
assert_eq!(expand, "\"expand\"");
}
#[test]
fn test_confidence_serialization() {
let high = serde_json::to_string(&Confidence::High).unwrap();
assert_eq!(high, "\"high\"");
let medium = serde_json::to_string(&Confidence::Medium).unwrap();
assert_eq!(medium, "\"medium\"");
let low = serde_json::to_string(&Confidence::Low).unwrap();
assert_eq!(low, "\"low\"");
}
#[test]
fn test_suggest_action_delete() {
let action = suggest_action(
ActionType::Delete,
"my_function",
"function",
"src/lib.rs",
Confidence::High,
);
assert_eq!(action.action_type, ActionType::Delete);
assert_eq!(action.confidence, Confidence::High);
assert!(action.reason.contains("Delete symbol"));
assert!(action.reason.contains("my_function"));
assert!(action.params.is_some());
let params = action.params.unwrap();
assert_eq!(
params.get("remove_references"),
Some(&serde_json::Value::Bool(true))
);
}
#[test]
fn test_suggest_action_replace() {
let action = suggest_action(
ActionType::Replace,
"my_struct",
"struct",
"src/types.rs",
Confidence::Medium,
);
assert_eq!(action.action_type, ActionType::Replace);
assert_eq!(action.confidence, Confidence::Medium);
assert!(action.reason.contains("Replace symbol"));
let params = action.params.unwrap();
assert_eq!(
params.get("preserve_signature"),
Some(&serde_json::Value::Bool(true))
);
}
#[test]
fn test_suggest_action_expand() {
let action = suggest_action(
ActionType::Expand,
"MyClass",
"class",
"src/main.py",
Confidence::High,
);
assert_eq!(action.action_type, ActionType::Expand);
assert!(action.reason.contains("Expand symbol"));
let params = action.params.unwrap();
assert_eq!(
params.get("levels"),
Some(&serde_json::Value::Number(2.into()))
);
}
#[test]
fn test_suggested_action_serialization() {
let action = SuggestedAction {
action_type: ActionType::Delete,
confidence: Confidence::High,
reason: "Test reason".to_string(),
params: None,
};
let json = serde_json::to_string(&action).unwrap();
assert!(json.contains("\"action_type\":\"delete\""));
assert!(json.contains("\"confidence\":\"high\""));
assert!(json.contains("\"reason\":\"Test reason\""));
assert!(!json.contains("\"params\""));
}
}