Files
IdeaSDK/crates/app-tauri/src/state.rs
Blomios ef84d5cc49 feat(backend): MCP d'édition de templates (#81)
Ajoute le MCP dédié à l'édition de templates : catalogue et classification
des templates (mcp/templates.rs infrastructure + app-tauri), use cases et
provider (application/template), enforcement de la policy des tools (mod.rs,
server.rs, tools.rs), avec la parité côté chemin OpenAI-compatible
(openai_tools.rs x2).

Lots B1 (catalogue/classification), B2 (use cases/provider) et B3
(enforcement policy) livrés en un seul commit cohérent.

QA vert (seul l'échec de bind loopback #80, connu et non-régression, écarté).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-20 18:45:02 +02:00

128 lines
4.6 KiB
Rust

//! Tauri-managed application state.
//!
//! The non-Tauri composition root lives in `backend::BackendCore`. This type is
//! the desktop driving-adapter envelope: it adds Tauri transport bridges and
//! OS-window focus state, then dereferences to the shared core so existing
//! commands keep their field/method access unchanged.
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use backend::BackendCore;
use infrastructure::{TemplateToolProvider, TicketToolProvider};
use serde::{Deserialize, Serialize};
use crate::chat::ChatBridge;
use crate::embedded_server::EmbeddedServerController;
use crate::pty::PtyBridge;
use crate::templates::AppTemplateToolProvider;
use crate::tickets::AppTicketToolProvider;
pub use backend::ResumeContext;
/// Focused project snapshot shared with detached panel windows.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FocusedProjectDto {
/// Project id as a string.
pub id: String,
/// Project display name.
pub name: String,
/// Absolute project root.
pub root: String,
}
/// Desktop adapter state managed by Tauri.
pub struct AppState {
core: Arc<BackendCore>,
/// Generic PTY to Tauri Channel bridge registry.
pub pty_bridge: Arc<PtyBridge>,
/// Structured reply to Tauri Channel bridge registry.
pub chat_bridge: Arc<ChatBridge>,
/// Desktop-owned embedded HTTP server lifecycle manager.
pub embedded_server: Arc<EmbeddedServerController>,
/// Project currently focused by the main window; panel-only windows follow it.
focused_project: Mutex<Option<FocusedProjectDto>>,
}
impl AppState {
/// Builds the shared backend core and wraps it with desktop-only transport
/// state. `app_data_dir` is resolved by the Tauri adapter before crossing
/// into the backend core.
#[must_use]
pub fn build(app_data_dir: PathBuf) -> Self {
Self::build_with_resource_dir(app_data_dir, None)
}
/// Builds the shared backend core with the Tauri bundle resource directory
/// available to desktop-only adapters.
#[must_use]
pub fn build_with_resource_dir(app_data_dir: PathBuf, resource_dir: Option<PathBuf>) -> Self {
let core = Arc::new(BackendCore::build(app_data_dir.clone()));
core.ticket_tool_binder
.bind(Arc::new(AppTicketToolProvider {
create: Arc::clone(&core.create_issue),
read: Arc::clone(&core.read_issue),
list: Arc::clone(&core.list_issues),
update: Arc::clone(&core.update_issue),
read_carnet: Arc::clone(&core.read_issue_carnet),
update_carnet: Arc::clone(&core.update_issue_carnet),
link: Arc::clone(&core.link_issues),
unlink: Arc::clone(&core.unlink_issues),
list_sprints: Arc::clone(&core.list_sprints),
}) as Arc<dyn TicketToolProvider>);
core.template_tool_binder
.bind(Arc::new(AppTemplateToolProvider {
create: Arc::clone(&core.create_template),
read: Arc::clone(&core.read_template),
list: Arc::clone(&core.list_templates),
update: Arc::clone(&core.update_template),
delete: Arc::clone(&core.delete_template),
}) as Arc<dyn TemplateToolProvider>);
Self {
core,
pty_bridge: Arc::new(PtyBridge::new()),
chat_bridge: Arc::new(ChatBridge::new()),
embedded_server: Arc::new(EmbeddedServerController::with_resource_dir(
app_data_dir,
resource_dir,
)),
focused_project: Mutex::new(None),
}
}
/// Clones the shared backend core for adapter components that must share
/// the desktop composition root.
#[must_use]
pub fn core(&self) -> Arc<BackendCore> {
Arc::clone(&self.core)
}
/// Updates the focused project state. `None` means no project is focused/open.
pub fn set_focused_project(&self, project: Option<FocusedProjectDto>) {
*self
.focused_project
.lock()
.expect("focused project mutex poisoned") = project;
}
/// Returns the current focused project, if any.
#[must_use]
pub fn get_focused_project(&self) -> Option<FocusedProjectDto> {
self.focused_project
.lock()
.expect("focused project mutex poisoned")
.clone()
}
}
impl Deref for AppState {
type Target = BackendCore;
fn deref(&self) -> &Self::Target {
&self.core
}
}