use serde_json::Value;
use std::collections::HashMap;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Duration;
use crate::config::NeutralIpcConfig;
use crate::constants::*;
use crate::error::{NeutralIpcError, Result};
use crate::record::NeutralIpcRecord;
pub(crate) struct NeutralIpcClient {
control: u8,
format1: u8,
content1: Vec<u8>,
format2: u8,
content2: String,
pub(crate) result: HashMap<String, Value>,
}
impl NeutralIpcClient {
pub(crate) fn new(control: u8, format1: u8, content1: &[u8], format2: u8, content2: &str) -> Self {
Self {
control,
format1,
content1: content1.to_vec(),
format2,
content2: content2.to_string(),
result: HashMap::new(),
}
}
pub(crate) fn start(&mut self) -> Result<&HashMap<String, Value>> {
let config = NeutralIpcConfig::new();
let host = config.get_host();
let port = config.get_port();
let timeout = config.get_timeout();
let buffer_size = config.get_buffer_size();
let mut stream = TcpStream::connect(format!("{}:{}", host, port))?;
stream.set_read_timeout(Some(Duration::from_secs(timeout as u64)))?;
stream.set_write_timeout(Some(Duration::from_secs(timeout as u64)))?;
let request = NeutralIpcRecord::encode_record(
self.control,
self.format1,
&self.content1,
self.format2,
self.content2.as_bytes(),
);
stream.write_all(&request)?;
let mut response_header = vec![0u8; HEADER_LEN];
stream.read_exact(&mut response_header)?;
let response = NeutralIpcRecord::decode_header(&response_header)?;
let length1 = response.get("length-1")
.and_then(|v| v.as_u64())
.ok_or(NeutralIpcError::InvalidResponse)? as usize;
let length2 = response.get("length-2")
.and_then(|v| v.as_u64())
.ok_or(NeutralIpcError::InvalidResponse)? as usize;
let content1 = self.read_content(&mut stream, length1, buffer_size)?;
let content2 = self.read_content(&mut stream, length2, buffer_size)?;
self.result = NeutralIpcRecord::decode_record(&response_header, &content1, &content2)?;
Ok(&self.result)
}
fn read_content(&self, stream: &mut TcpStream, length: usize, buffer_size: usize) -> Result<String> {
if length == 0 {
return Ok(String::new());
}
let mut chunks = Vec::new();
let mut remaining = length;
while remaining > 0 {
let chunk_size = std::cmp::min(buffer_size, remaining);
let mut chunk = vec![0u8; chunk_size];
let bytes_read = stream.read(&mut chunk)?;
if bytes_read == 0 {
return Err(NeutralIpcError::ConnectionClosed);
}
chunks.extend_from_slice(&chunk[..bytes_read]);
remaining -= bytes_read;
}
String::from_utf8(chunks).map_err(|_| NeutralIpcError::InvalidUtf8)
}
}
pub fn is_server_available() -> bool {
let config = NeutralIpcConfig::new();
let host = config.get_host();
let port = config.get_port();
match TcpStream::connect_timeout(
&format!("{}:{}", host, port).parse().unwrap(),
std::time::Duration::from_secs(1)
) {
Ok(mut stream) => {
let minimal_request = NeutralIpcRecord::encode_record(
CTRL_PARSE_TEMPLATE,
CONTENT_JSON,
b"{}",
CONTENT_TEXT,
b""
);
stream.set_read_timeout(Some(std::time::Duration::from_secs(1))).ok();
stream.set_write_timeout(Some(std::time::Duration::from_secs(1))).ok();
match stream.write_all(&minimal_request) {
Ok(_) => {
let mut header_buffer = [0u8; HEADER_LEN];
stream.read_exact(&mut header_buffer).is_ok()
},
Err(_) => false
}
},
Err(_) => false
}
}