use ractor::RpcReplyPort; use ractor_cluster::BytesConvertable; use ractor_cluster::RactorClusterMessage; use crate::pb::RepositoryHeader; impl BytesConvertable for RepositoryHeader { fn into_bytes(self) -> Vec { prost::Message::encode_to_vec(&self) } fn from_bytes(bytes: Vec) -> Self { prost::Message::decode(bytes.as_slice()).unwrap_or_default() } } #[derive(Debug, Clone)] pub struct RouteDecision { pub found: bool, pub storage_name: String, pub relative_path: String, pub actor_name: String, pub grpc_addr: String, } impl BytesConvertable for RouteDecision { fn into_bytes(self) -> Vec { encode_strings(&[ if self.found { "1" } else { "0" }.to_string(), self.storage_name, self.relative_path, self.actor_name, self.grpc_addr, ]) } fn from_bytes(bytes: Vec) -> Self { let values = decode_strings(bytes); Self { found: values.first().is_some_and(|v| v == "1"), storage_name: values.get(1).cloned().unwrap_or_default(), relative_path: values.get(2).cloned().unwrap_or_default(), actor_name: values.get(3).cloned().unwrap_or_default(), grpc_addr: values.get(4).cloned().unwrap_or_default(), } } } #[derive(Debug, Clone)] pub struct NodeHealth { pub storage_name: String, pub repo_count: u64, pub healthy: bool, pub version: String, } impl BytesConvertable for NodeHealth { fn into_bytes(self) -> Vec { encode_strings(&[ self.storage_name, self.repo_count.to_string(), if self.healthy { "1" } else { "0" }.to_string(), self.version, ]) } fn from_bytes(bytes: Vec) -> Self { let values = decode_strings(bytes); Self { storage_name: values.first().cloned().unwrap_or_default(), repo_count: values.get(1).and_then(|v| v.parse().ok()).unwrap_or_default(), healthy: values.get(2).is_some_and(|v| v == "1"), version: values.get(3).cloned().unwrap_or_default(), } } } #[derive(RactorClusterMessage)] pub enum GitNodeMessage { ScanAndRegister, RegisterRepository(RepositoryHeader), RemoveRepository(RepositoryHeader), #[rpc] RouteRepository(RepositoryHeader, RpcReplyPort), #[rpc] ListRepositoryPaths(RpcReplyPort), #[rpc] RepositoryExists(RepositoryHeader, RpcReplyPort), #[rpc] GetNodeHealth(RpcReplyPort), } #[derive(ractor_cluster::RactorMessage)] pub enum RepoActorMessage { UpdateMetadata(RepositoryHeader), } fn encode_strings(values: &[String]) -> Vec { let mut buf = Vec::new(); for value in values { let bytes = value.as_bytes(); buf.extend((bytes.len() as u64).to_be_bytes()); buf.extend(bytes); } buf } fn decode_strings(bytes: Vec) -> Vec { let mut values = Vec::new(); let mut offset = 0; while offset + 8 <= bytes.len() { let len = u64::from_be_bytes(bytes[offset..offset + 8].try_into().unwrap()) as usize; offset += 8; if offset + len > bytes.len() { break; } values.push(String::from_utf8_lossy(&bytes[offset..offset + len]).into_owned()); offset += len; } values }