use crate::ingest::SemanticKind;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolHintOperation {
DeleteBody,
ChangeSignature,
ChangeType,
ReplaceBody,
Query,
Get,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ToolHints {
pub requires_full_context: bool,
pub apply_atomically: bool,
pub may_break_tests: bool,
pub requires_compilation: bool,
}
impl Default for ToolHints {
fn default() -> Self {
Self {
requires_full_context: false,
apply_atomically: true, may_break_tests: false,
requires_compilation: false,
}
}
}
impl ToolHints {
pub fn new() -> Self {
Self::default()
}
pub fn with_requires_full_context(mut self, value: bool) -> Self {
self.requires_full_context = value;
self
}
pub fn with_may_break_tests(mut self, value: bool) -> Self {
self.may_break_tests = value;
self
}
pub fn with_requires_compilation(mut self, value: bool) -> Self {
self.requires_compilation = value;
self
}
pub fn for_function_delete(is_public: bool) -> Self {
Self {
requires_full_context: false,
apply_atomically: true,
may_break_tests: is_public,
requires_compilation: true,
}
}
pub fn for_struct_modify(is_public: bool) -> Self {
Self {
requires_full_context: false,
apply_atomically: true,
may_break_tests: is_public,
requires_compilation: true,
}
}
pub fn for_body_replace() -> Self {
Self {
requires_full_context: false,
apply_atomically: true,
may_break_tests: false,
requires_compilation: false,
}
}
}
pub fn derive_tool_hints(
semantic_kind: SemanticKind,
is_public: bool,
operation: ToolHintOperation,
) -> ToolHints {
let requires_full_context = matches!(
(semantic_kind, operation),
(SemanticKind::Function, ToolHintOperation::DeleteBody)
| (SemanticKind::Function, ToolHintOperation::ReplaceBody)
| (SemanticKind::Trait, _)
);
let apply_atomically = true;
let may_break_tests = matches!(
(semantic_kind, is_public),
(SemanticKind::Function, true) | (SemanticKind::Trait, true) | (SemanticKind::Type, true)
);
let requires_compilation = match operation {
ToolHintOperation::ChangeType => true,
ToolHintOperation::ChangeSignature if is_public => true,
ToolHintOperation::DeleteBody if is_public => true,
_ => false,
};
ToolHints {
requires_full_context,
apply_atomically,
may_break_tests,
requires_compilation,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tool_hints_default() {
let hints = ToolHints::default();
assert!(!hints.requires_full_context);
assert!(hints.apply_atomically);
assert!(!hints.may_break_tests);
assert!(!hints.requires_compilation);
}
#[test]
fn test_tool_hints_builder() {
let hints = ToolHints::new()
.with_requires_full_context(true)
.with_may_break_tests(true)
.with_requires_compilation(true);
assert!(hints.requires_full_context);
assert!(hints.apply_atomically); assert!(hints.may_break_tests);
assert!(hints.requires_compilation);
}
#[test]
fn test_tool_hints_serialization() {
let hints = ToolHints::new()
.with_requires_full_context(true)
.with_may_break_tests(false);
let json = serde_json::to_string(&hints).unwrap();
assert!(json.contains("\"requires_full_context\":true"));
assert!(json.contains("\"apply_atomically\":true"));
assert!(json.contains("\"may_break_tests\":false"));
}
#[test]
fn test_for_function_delete() {
let hints = ToolHints::for_function_delete(true);
assert!(!hints.requires_full_context);
assert!(hints.apply_atomically);
assert!(hints.may_break_tests); assert!(hints.requires_compilation);
let hints_private = ToolHints::for_function_delete(false);
assert!(!hints_private.may_break_tests); }
#[test]
fn test_for_struct_modify() {
let hints = ToolHints::for_struct_modify(true);
assert!(!hints.requires_full_context);
assert!(hints.apply_atomically);
assert!(hints.may_break_tests); assert!(hints.requires_compilation);
let hints_private = ToolHints::for_struct_modify(false);
assert!(!hints_private.may_break_tests); }
#[test]
fn test_for_body_replace() {
let hints = ToolHints::for_body_replace();
assert!(!hints.requires_full_context);
assert!(hints.apply_atomically);
assert!(!hints.may_break_tests); assert!(!hints.requires_compilation); }
}