use std::io::ErrorKind;
use std::io::Read;
use std::io::Result;
use std::io::Write;
use std::path::PathBuf;
use serde::Deserialize;
use serde::Serialize;
use crate::communication::Message;
use crate::plugins::ConfigChange;
use crate::plugins::FormatConfigId;
use crate::plugins::FormatRange;
use crate::communication::MessageReader;
use crate::communication::MessageWriter;
pub type MessageId = u32;
mod message_ids {
use super::MessageId;
pub const SUCCESS_ID: MessageId = 0;
pub const DATA_RESPONSE_ID: MessageId = 1;
pub const ERROR_ID: MessageId = 2;
pub const CLOSE_ID: MessageId = 3;
pub const IS_ALIVE_ID: MessageId = 4;
pub const GET_PLUGIN_INFO_ID: MessageId = 5;
pub const GET_LICENSE_TEXT_ID: MessageId = 6;
pub const REGISTER_CONFIG_ID: MessageId = 7;
pub const RELEASE_CONFIG_ID: MessageId = 8;
pub const GET_CONFIG_DIAGNOSTICS_ID: MessageId = 9;
pub const GET_FILE_MATCHING_INFO_ID: MessageId = 10;
pub const GET_RESOLVED_CONFIG_ID: MessageId = 11;
pub const CHECK_CONFIG_UPDATES_ID: MessageId = 12;
pub const FORMAT_ID: MessageId = 13;
pub const FORMAT_RESPONSE_ID: MessageId = 14;
pub const CANCEL_FORMAT_ID: MessageId = 15;
pub const HOST_FORMAT_ID: MessageId = 16;
}
#[derive(Debug)]
pub struct ProcessPluginMessage {
pub id: MessageId,
pub body: MessageBody,
}
impl ProcessPluginMessage {
pub fn read<TRead: Read + Unpin>(reader: &mut MessageReader<TRead>) -> Result<ProcessPluginMessage> {
let id = reader.read_u32()?;
let message_kind = reader.read_u32()?;
let body = match message_kind {
message_ids::SUCCESS_ID => MessageBody::Success(reader.read_u32()?),
message_ids::DATA_RESPONSE_ID => {
let message_id = reader.read_u32()?;
let data = reader.read_sized_bytes()?;
MessageBody::DataResponse(ResponseBody { message_id, data })
}
message_ids::ERROR_ID => {
let message_id = reader.read_u32()?;
let data = reader.read_sized_bytes()?;
MessageBody::Error(ResponseBody { message_id, data })
}
message_ids::CLOSE_ID => MessageBody::Close,
message_ids::IS_ALIVE_ID => MessageBody::IsAlive,
message_ids::GET_PLUGIN_INFO_ID => MessageBody::GetPluginInfo,
message_ids::GET_LICENSE_TEXT_ID => MessageBody::GetLicenseText,
message_ids::REGISTER_CONFIG_ID => {
let config_id = FormatConfigId::from_raw(reader.read_u32()?);
let global_config = reader.read_sized_bytes()?;
let plugin_config = reader.read_sized_bytes()?;
MessageBody::RegisterConfig(RegisterConfigMessageBody {
config_id,
global_config,
plugin_config,
})
}
message_ids::RELEASE_CONFIG_ID => MessageBody::ReleaseConfig(FormatConfigId::from_raw(reader.read_u32()?)),
message_ids::GET_CONFIG_DIAGNOSTICS_ID => MessageBody::GetConfigDiagnostics(FormatConfigId::from_raw(reader.read_u32()?)),
message_ids::GET_FILE_MATCHING_INFO_ID => MessageBody::GetFileMatchingInfo(FormatConfigId::from_raw(reader.read_u32()?)),
message_ids::GET_RESOLVED_CONFIG_ID => MessageBody::GetResolvedConfig(FormatConfigId::from_raw(reader.read_u32()?)),
message_ids::CHECK_CONFIG_UPDATES_ID => {
let body_bytes = reader.read_sized_bytes()?;
MessageBody::CheckConfigUpdates(body_bytes)
}
message_ids::FORMAT_ID => {
let file_path = reader.read_sized_bytes()?;
let start_byte_index = reader.read_u32()?;
let end_byte_index = reader.read_u32()?;
let config_id = FormatConfigId::from_raw(reader.read_u32()?);
let override_config = reader.read_sized_bytes()?;
let file_text = reader.read_sized_bytes()?;
MessageBody::Format(FormatMessageBody {
file_path: PathBuf::from(String::from_utf8_lossy(&file_path).to_string()),
range: if start_byte_index == 0 && end_byte_index == file_text.len() as u32 {
None
} else {
Some(std::ops::Range {
start: start_byte_index as usize,
end: end_byte_index as usize,
})
},
config_id,
file_bytes: file_text,
override_config,
})
}
message_ids::FORMAT_RESPONSE_ID => {
let message_id = reader.read_u32()?;
let response_kind = reader.read_u32()?;
let data = match response_kind {
0 => None,
1 => Some(reader.read_sized_bytes()?),
_ => {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
format!("Unknown format response kind: {}", response_kind),
))
}
};
MessageBody::FormatResponse(ResponseBody { message_id, data })
}
message_ids::CANCEL_FORMAT_ID => MessageBody::CancelFormat(reader.read_u32()?),
message_ids::HOST_FORMAT_ID => {
let original_message_id = reader.read_u32()?;
let file_path = reader.read_sized_bytes()?;
let start_byte_index = reader.read_u32()?;
let end_byte_index = reader.read_u32()?;
let override_config = reader.read_sized_bytes()?;
let file_text = reader.read_sized_bytes()?;
MessageBody::HostFormat(HostFormatMessageBody {
original_message_id,
file_path: PathBuf::from(String::from_utf8_lossy(&file_path).to_string()),
range: if start_byte_index == 0 && end_byte_index == file_text.len() as u32 {
None
} else {
Some(std::ops::Range {
start: start_byte_index as usize,
end: end_byte_index as usize,
})
},
file_text,
override_config,
})
}
_ => {
return Ok(ProcessPluginMessage {
id,
body: MessageBody::Unknown(message_kind),
});
}
};
reader.read_success_bytes()?;
Ok(ProcessPluginMessage { id, body })
}
}
impl Message for ProcessPluginMessage {
fn write<TWrite: Write + Unpin>(&self, writer: &mut MessageWriter<TWrite>) -> Result<()> {
writer.send_u32(self.id)?;
match &self.body {
MessageBody::Success(message_id) => {
writer.send_u32(message_ids::SUCCESS_ID)?;
writer.send_u32(*message_id)?;
}
MessageBody::DataResponse(response) => {
writer.send_u32(message_ids::DATA_RESPONSE_ID)?;
writer.send_u32(response.message_id)?;
writer.send_sized_bytes(&response.data)?;
}
MessageBody::Error(response) => {
writer.send_u32(message_ids::ERROR_ID)?;
writer.send_u32(response.message_id)?;
writer.send_sized_bytes(&response.data)?;
}
MessageBody::Close => {
writer.send_u32(message_ids::CLOSE_ID)?;
}
MessageBody::IsAlive => {
writer.send_u32(message_ids::IS_ALIVE_ID)?;
}
MessageBody::GetPluginInfo => {
writer.send_u32(message_ids::GET_PLUGIN_INFO_ID)?;
}
MessageBody::GetLicenseText => {
writer.send_u32(message_ids::GET_LICENSE_TEXT_ID)?;
}
MessageBody::RegisterConfig(body) => {
writer.send_u32(message_ids::REGISTER_CONFIG_ID)?;
writer.send_u32(body.config_id.as_raw())?;
writer.send_sized_bytes(&body.global_config)?;
writer.send_sized_bytes(&body.plugin_config)?;
}
MessageBody::ReleaseConfig(config_id) => {
writer.send_u32(message_ids::RELEASE_CONFIG_ID)?;
writer.send_u32(config_id.as_raw())?;
}
MessageBody::GetConfigDiagnostics(config_id) => {
writer.send_u32(message_ids::GET_CONFIG_DIAGNOSTICS_ID)?;
writer.send_u32(config_id.as_raw())?;
}
MessageBody::GetFileMatchingInfo(config_id) => {
writer.send_u32(message_ids::GET_FILE_MATCHING_INFO_ID)?;
writer.send_u32(config_id.as_raw())?;
}
MessageBody::GetResolvedConfig(config_id) => {
writer.send_u32(message_ids::GET_RESOLVED_CONFIG_ID)?;
writer.send_u32(config_id.as_raw())?;
}
MessageBody::CheckConfigUpdates(body_bytes) => {
writer.send_u32(message_ids::CHECK_CONFIG_UPDATES_ID)?;
writer.send_sized_bytes(body_bytes)?;
}
MessageBody::Format(body) => {
writer.send_u32(message_ids::FORMAT_ID)?;
writer.send_sized_bytes(body.file_path.to_string_lossy().as_bytes())?;
writer.send_u32(body.range.as_ref().map(|r| r.start).unwrap_or(0) as u32)?;
writer.send_u32(body.range.as_ref().map(|r| r.end).unwrap_or(body.file_bytes.len()) as u32)?;
writer.send_u32(body.config_id.as_raw())?;
writer.send_sized_bytes(&body.override_config)?;
writer.send_sized_bytes(&body.file_bytes)?;
}
MessageBody::FormatResponse(response) => {
writer.send_u32(message_ids::FORMAT_RESPONSE_ID)?;
writer.send_u32(response.message_id)?;
match &response.data {
None => {
writer.send_u32(0)?;
}
Some(data) => {
writer.send_u32(1)?;
writer.send_sized_bytes(data)?;
}
}
}
MessageBody::CancelFormat(message_id) => {
writer.send_u32(message_ids::CANCEL_FORMAT_ID)?;
writer.send_u32(*message_id)?;
}
MessageBody::HostFormat(body) => {
writer.send_u32(message_ids::HOST_FORMAT_ID)?;
writer.send_u32(body.original_message_id)?;
writer.send_sized_bytes(body.file_path.to_string_lossy().as_bytes())?;
writer.send_u32(body.range.as_ref().map(|r| r.start).unwrap_or(0) as u32)?;
writer.send_u32(body.range.as_ref().map(|r| r.end).unwrap_or(body.file_text.len()) as u32)?;
writer.send_sized_bytes(&body.override_config)?;
writer.send_sized_bytes(&body.file_text)?;
}
MessageBody::Unknown(_) => unreachable!(), }
writer.send_success_bytes()?;
Ok(())
}
}
#[derive(Debug)]
pub enum MessageBody {
Success(MessageId),
DataResponse(ResponseBody<Vec<u8>>),
Error(ResponseBody<Vec<u8>>),
Close,
IsAlive,
GetPluginInfo,
GetLicenseText,
RegisterConfig(RegisterConfigMessageBody),
ReleaseConfig(FormatConfigId),
GetConfigDiagnostics(FormatConfigId),
GetFileMatchingInfo(FormatConfigId),
GetResolvedConfig(FormatConfigId),
CheckConfigUpdates(Vec<u8>),
Format(FormatMessageBody),
FormatResponse(ResponseBody<Option<Vec<u8>>>),
CancelFormat(MessageId),
HostFormat(HostFormatMessageBody),
Unknown(u32),
}
#[derive(Debug)]
pub struct ResponseBody<T: std::fmt::Debug> {
pub message_id: MessageId,
pub data: T,
}
#[derive(Debug)]
pub struct RegisterConfigMessageBody {
pub config_id: FormatConfigId,
pub global_config: Vec<u8>,
pub plugin_config: Vec<u8>,
}
pub type CheckConfigUpdatesMessageBody = crate::plugins::CheckConfigUpdatesMessage;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CheckConfigUpdatesResponseBody {
pub changes: Vec<ConfigChange>,
}
#[derive(Debug)]
pub struct FormatMessageBody {
pub file_path: PathBuf,
pub range: FormatRange,
pub config_id: FormatConfigId,
pub override_config: Vec<u8>,
pub file_bytes: Vec<u8>,
}
#[derive(Debug)]
pub struct HostFormatMessageBody {
pub original_message_id: MessageId,
pub file_path: PathBuf,
pub range: FormatRange,
pub override_config: Vec<u8>,
pub file_text: Vec<u8>,
}