use std::path::PathBuf;
use std::sync::Arc;
use serde::Deserialize;
use serde_json::Value;
use sqry_core::graph::unified::GraphMemorySize;
use sqry_core::project::ProjectRootMode;
use sqry_daemon_protocol::LoadResult;
use crate::config::{DaemonConfig, WORKING_SET_MULTIPLIER};
use crate::error::DaemonError;
use crate::workspace::{WorkspaceKey, WorkspaceState};
use super::super::path_policy::resolve_index_root;
use super::super::protocol::{ResponseEnvelope, ResponseMeta};
use super::{HandlerContext, MethodError, format_panic_payload};
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LoadParams {
pub index_root: PathBuf,
#[serde(default)]
pub root_mode: Option<ProjectRootMode>,
#[serde(default)]
pub config_fingerprint: Option<u64>,
}
const INITIAL_WORKING_SET_BYTES: u64 = 2 * 1024 * 1024;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
fn working_set_estimate_for_initial(_cfg: &DaemonConfig) -> u64 {
#[allow(clippy::cast_precision_loss)]
let scaled = (INITIAL_WORKING_SET_BYTES as f64) * WORKING_SET_MULTIPLIER;
scaled as u64
}
pub(crate) async fn handle(ctx: &HandlerContext, params: Value) -> Result<Value, MethodError> {
let params: LoadParams = serde_json::from_value(params).map_err(MethodError::InvalidParams)?;
let canonical_root = resolve_index_root(¶ms.index_root)?;
let key = WorkspaceKey::new(
canonical_root.clone(),
params.root_mode.unwrap_or_default(),
params.config_fingerprint.unwrap_or(0),
);
let manager = Arc::clone(&ctx.manager);
let builder = Arc::clone(&ctx.workspace_builder);
let estimate = working_set_estimate_for_initial(&ctx.config);
let key_for_task = key.clone();
let graph = match tokio::task::spawn_blocking(move || {
manager.get_or_load(&key_for_task, &*builder, estimate)
})
.await
{
Ok(Ok(graph)) => graph,
Ok(Err(daemon_err)) => return Err(MethodError::Daemon(daemon_err)),
Err(join_err) if join_err.is_panic() => {
let reason = format_panic_payload(join_err);
return Err(MethodError::Daemon(DaemonError::WorkspaceBuildFailed {
root: key.index_root.clone(),
reason: format!("workspace builder panicked: {reason}"),
}));
}
Err(join_err) => return Err(MethodError::JoinError(join_err)),
};
let current_bytes = graph.heap_bytes() as u64;
let envelope = ResponseEnvelope {
result: LoadResult {
root: key.index_root.clone(),
current_bytes,
state: WorkspaceState::Loaded,
},
meta: ResponseMeta::loaded(ctx.daemon_version),
};
serde_json::to_value(&envelope)
.map_err(|e| MethodError::Internal(anyhow::anyhow!("serialise daemon/load: {e}")))
}