#![deny(missing_docs)]
use fmt::Write as FmtWrite;
use io::{BufReader, prelude::*};
use std::collections::HashMap;
use std::fs::File;
use std::mem::take;
use std::os::fd::FromRawFd;
use std::process::ExitCode;
use std::time::Duration;
use std::{fmt, io};
#[derive(Debug)]
enum IError {
IO(io::Error),
InvalidLineFormat(Box<str>),
NoInit,
CantHandleVersion(u32),
}
type Socket = BufReader<File>;
#[derive(Debug)]
pub struct Error(IError);
impl From<IError> for Error {
fn from(value: IError) -> Self {
Self(value)
}
}
impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Self(IError::IO(value))
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self.0 {
IError::IO(e) => write!(f, "{e}"),
IError::NoInit => write!(f, "First command should be init."),
IError::InvalidLineFormat(line) => {
write!(f, "Protocol error: invalid line format: {line}")
}
IError::CantHandleVersion(version) => {
write!(f, "Can't handle camlet version {version}")
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
if let Error(IError::IO(io)) = self {
return Some(io);
}
None
}
}
type Result<T> = std::result::Result<T, Error>;
pub trait UniformValue {
fn to_raw(&self, into: &mut String);
}
impl UniformValue for i32 {
fn to_raw(&self, into: &mut String) {
into.push_str(self.format_into(&mut core::fmt::NumBuffer::new()));
}
}
impl UniformValue for f32 {
fn to_raw(&self, into: &mut String) {
write!(into, "{:e}", self).unwrap()
}
}
impl UniformValue for u32 {
fn to_raw(&self, into: &mut String) {
into.push_str(self.format_into(&mut core::fmt::NumBuffer::new()));
}
}
macro_rules! impl_vector_uniforms {
($t:ty, $sep:literal) => {
impl UniformValue for [$t; 2] {
fn to_raw(&self, into: &mut String) {
self[0].to_raw(into);
into.push($sep);
self[1].to_raw(into);
}
}
impl UniformValue for [$t; 3] {
fn to_raw(&self, into: &mut String) {
self[0].to_raw(into);
into.push($sep);
self[1].to_raw(into);
into.push($sep);
self[2].to_raw(into);
}
}
impl UniformValue for [$t; 4] {
fn to_raw(&self, into: &mut String) {
self[0].to_raw(into);
into.push($sep);
self[1].to_raw(into);
into.push($sep);
self[2].to_raw(into);
into.push($sep);
self[3].to_raw(into);
}
}
};
}
impl_vector_uniforms!(i32, ',');
impl_vector_uniforms!(u32, ',');
impl_vector_uniforms!(f32, ',');
impl_vector_uniforms!([f32; 2], ';');
impl_vector_uniforms!([f32; 3], ';');
impl_vector_uniforms!([f32; 4], ';');
#[derive(Debug, Clone)]
pub struct Shader {
code: String,
bound_textures: String,
uniforms: String,
}
impl Shader {
pub fn new(code: &str) -> Self {
Shader {
code: code.into(),
bound_textures: String::new(),
uniforms: String::new(),
}
}
pub fn from_path<P: AsRef<std::path::Path>>(path: P) -> io::Result<Self> {
Ok(Self {
code: std::fs::read_to_string(path)?,
bound_textures: String::new(),
uniforms: String::new(),
})
}
pub fn bind_textures<I: IntoIterator<Item = (u8, u32)>>(&mut self, textures: I) -> &mut Self {
self.bound_textures.clear();
for x in textures {
let (slot, texture) = x;
if !self.bound_textures.is_empty() {
self.bound_textures.push(',');
}
write!(self.bound_textures, "{slot}:{texture}").unwrap();
}
self
}
pub fn set_uniform<U: UniformValue>(&mut self, name: &str, value: U) -> &mut Self {
self.uniforms.push_str(name);
self.uniforms.push_str(" = ");
value.to_raw(&mut self.uniforms);
self.uniforms.push('\n');
self
}
pub fn set_uniform_array<U: UniformValue>(
&mut self,
name: &str,
value: impl IntoIterator<Item = U>,
) -> &mut Self {
self.uniforms.push_str(name);
self.uniforms.push_str(" = ");
let mut first = true;
for item in value {
if !first {
self.uniforms.push('|');
}
first = false;
item.to_raw(&mut self.uniforms);
}
self.uniforms.push('\n');
self
}
}
#[derive(Debug)]
pub struct Command {
name: String,
arguments: HashMap<String, String>,
response: HashMap<String, String>,
}
impl Command {
pub fn get(&self, name: &str) -> Option<&str> {
self.arguments.get(name).map(|s| s.as_ref())
}
fn has(&self, name: &str) -> bool {
self.arguments.contains_key(name)
}
fn get_required(&self, name: &str) -> &str {
match self.get(name) {
Some(val) => val,
None => {
panic!(
"didn't find required argument {name} to {} command",
self.name
)
}
}
}
fn get_required_u32(&self, name: &str) -> u32 {
let value = self.get_required(name);
match value.parse() {
Ok(val) => val,
Err(_) => {
panic!(
"argument {name} to {} command was not an unsigned integer: {value}",
self.name
)
}
}
}
fn get_required_i32(&self, name: &str) -> i32 {
let value = self.get_required(name);
match value.parse() {
Ok(val) => val,
Err(_) => {
panic!(
"argument {name} to {} command was not an integer: {value}",
self.name
)
}
}
}
fn get_required_f64(&self, name: &str) -> f64 {
let value = self.get_required(name);
match value.parse() {
Ok(val) => val,
Err(_) => {
panic!(
"argument {name} to {} command was not a number: {value}",
self.name
)
}
}
}
pub fn set<N: Into<String>, V: Into<String>>(&mut self, name: N, value: V) {
self.response.insert(name.into(), value.into());
}
fn respond(&self, file: &mut File) -> Result<()> {
let mut response = format!("ack {}\n", self.name);
for (argument, value) in self.response.iter() {
response.push_str(argument);
response.push_str(":\n");
for line in value.split('\n') {
response.push('>');
response.push_str(line);
response.push('\n');
}
}
response.push('\n');
file.write_all(response.as_bytes())?;
Ok(())
}
fn clear_shader(&mut self, prefix: &str) {
self.response
.insert(format!("{prefix}_shader"), String::new());
}
fn set_shader(&mut self, prefix: &str, shader: Shader) {
let Shader {
code,
bound_textures,
uniforms,
} = shader;
self.response.insert(format!("{prefix}_shader"), code);
self.response
.insert(format!("{prefix}_bound_textures"), bound_textures);
self.response.insert(format!("{prefix}_uniforms"), uniforms);
}
}
#[derive(Debug)]
pub struct CommandInit(Command);
impl CommandInit {
pub fn version_major(&self) -> u32 {
self.0.get_required_u32("version_major")
}
pub fn version_minor(&self) -> u32 {
self.0.get_required_u32("version_minor")
}
pub fn version_patch(&self) -> u32 {
self.0.get_required_u32("version_patch")
}
fn check_setting(&self, identifier: &str, name: &str) {
if identifier.is_empty() || identifier.len() > 30 {
panic!("Setting identifier must be 1-30 bytes long.");
}
if identifier.contains([' ', '\n']) {
panic!("Setting identifier must not contain spaces or newlines.");
}
if identifier.starts_with('_') {
panic!("Setting identifier must not start with underscore.");
}
if name.starts_with(';') {
panic!("Setting name must not contain semicolon");
}
}
pub fn define_keyboard_setting(&mut self, identifier: &str, name: &str, default_value: &str) {
self.check_setting(identifier, name);
self.0.set(
format!("setting_{identifier}"),
format!("keyboard;{name};{default_value}"),
);
}
pub fn define_file_setting(&mut self, identifier: &str, name: &str, default_value: &str) {
self.check_setting(identifier, name);
self.0.set(
format!("setting_{identifier}"),
format!("file;{name};{default_value}"),
);
}
pub fn define_directory_setting(&mut self, identifier: &str, name: &str, default_value: &str) {
self.check_setting(identifier, name);
self.0.set(
format!("setting_{identifier}"),
format!("directory;{name};{default_value}"),
);
}
pub fn define_number_setting(
&mut self,
identifier: &str,
name: &str,
min: f64,
max: f64,
step: f64,
default_value: f64,
) {
self.check_setting(identifier, name);
if !(default_value >= min && default_value <= max) {
panic!("number setting default value must be between minimum and maximum");
}
if !min.is_finite() || !max.is_finite() || !step.is_finite() || !default_value.is_finite() {
panic!("number setting minimum, maximum, step, and default value must all be finite");
}
self.0.set(
format!("setting_{identifier}"),
format!("number,{min},{max},{step};{name};{default_value}"),
);
}
pub fn define_select_setting<S: AsRef<str>, I: IntoIterator<Item = S>>(
&mut self,
identifier: &str,
name: &str,
options: I,
default_value: &str,
) {
self.check_setting(identifier, name);
let mut value = String::from("select;");
let mut first = true;
let mut found = false;
for option in options {
if option.as_ref() == default_value {
found = true;
}
if !first {
value.push(',');
}
value.push_str(option.as_ref());
first = false;
}
if !found {
panic!("default value for select setting must be one of the options.");
}
value.push(';');
value.push_str(name);
value.push(';');
value.push_str(default_value);
self.0.set(format!("setting_{identifier}"), value);
}
}
#[derive(Debug)]
pub struct CommandQuit(Command);
#[derive(Debug)]
pub struct CommandKeyDown(Command);
impl CommandKeyDown {
pub fn key(&self) -> u32 {
self.0.get_required_u32("key")
}
pub fn scancode(&self) -> u32 {
self.0.get_required_u32("scancode")
}
pub fn name(&self) -> &str {
self.0.get_required("name")
}
pub fn repeat(&self) -> bool {
self.0.has("repeat")
}
pub fn combo(&self) -> &str {
self.0.get_required("combo")
}
pub fn modifiers(&self) -> &str {
self.0.get_required("modifiers")
}
}
#[derive(Debug)]
pub struct CommandKeyUp(Command);
impl CommandKeyUp {
pub fn key(&self) -> u32 {
self.0.get_required_u32("key")
}
pub fn scancode(&self) -> u32 {
self.0.get_required_u32("scancode")
}
pub fn name(&self) -> &str {
self.0.get_required("name")
}
}
#[derive(Debug)]
pub struct CommandCameraConnected(Command);
impl CommandCameraConnected {
pub fn name(&self) -> &str {
self.0.get_required("name")
}
pub fn hardware_id(&self) -> &str {
self.0.get_required("hardware_id")
}
pub fn device_path(&self) -> &str {
self.0.get_required("device_path")
}
}
#[derive(Debug)]
pub struct CommandCameraDisconnected(Command);
impl CommandCameraDisconnected {
pub fn device_path(&self) -> &str {
self.0.get_required("device_path")
}
}
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum DownloadFormat {
Jpeg {
quality: u8,
},
Png,
Raw,
}
impl DownloadFormat {
fn to_raw(self, buffer: &mut [u8; 16]) -> &str {
match self {
Self::Jpeg { quality } => {
if !(1..=100).contains(&quality) {
panic!("JPEG quality must be between 1 and 100: {quality}")
}
write!(&mut buffer[..], "jpg,{quality}").unwrap();
let len = buffer.iter().position(|&x| x == 0).unwrap();
std::str::from_utf8(&buffer[..len]).unwrap()
}
Self::Png => "png",
Self::Raw => "raw",
}
}
}
#[derive(Debug)]
pub struct CommandFrame(Command);
impl CommandFrame {
pub fn is_menu_open(&self) -> bool {
self.0.has("menu_open")
}
pub fn camera_width(&self) -> u32 {
self.0.get_required_u32("camera_width")
}
pub fn camera_height(&self) -> u32 {
self.0.get_required_u32("camera_height")
}
pub fn picture_width(&self) -> u32 {
self.0.get_required_u32("picture_width")
}
pub fn picture_height(&self) -> u32 {
self.0.get_required_u32("picture_height")
}
pub fn window_width(&self) -> u32 {
self.0.get_required_u32("window_width")
}
pub fn window_height(&self) -> u32 {
self.0.get_required_u32("window_height")
}
pub fn has_new_camera_frame(&self) -> bool {
self.0.has("new_camera_frame")
}
pub fn active_camera(&self) -> Option<&str> {
self.0.get("active_camera")
}
pub fn is_photo_mode(&self) -> bool {
self.0.get_required("mode") == "photo"
}
pub fn is_video_mode(&self) -> bool {
self.0.get_required("mode") == "video"
}
pub fn set_camera_shader(&mut self, shader: Shader) {
self.0.set_shader("camera", shader)
}
pub fn clear_camera_shader(&mut self) {
self.0.clear_shader("camera")
}
pub fn set_overlay_shader(&mut self, shader: Shader) {
self.0.set_shader("overlay", shader)
}
pub fn clear_overlay_shader(&mut self) {
self.0.clear_shader("camera")
}
pub fn take_photo(&mut self) {
self.0.set("take_photo", "");
}
pub fn start_video(&mut self) {
self.0.set("start_video", "");
}
pub fn stop_video(&mut self) {
self.0.set("stop_video", "");
}
pub fn photo_mode(&mut self) {
self.0.set("set_mode", "photo");
}
pub fn video_mode(&mut self) {
self.0.set("set_mode", "video");
}
pub fn switch_camera(&mut self, device_path: &str) {
self.0.set("switch_camera", device_path);
}
pub fn render_text(&mut self, text: &str) {
self.0.set("render_text", text);
}
pub fn upload_texture(&mut self, path: &str) {
self.0.set("upload_texture", path);
}
pub fn download_texture(&mut self, path: &str, texture: u32, format: DownloadFormat) {
self.0.set(
"download_texture",
format!(
"{texture},{};{path}",
format.to_raw(&mut Default::default())
),
);
}
pub fn grab_camera(&mut self) {
self.0.set("grab_camera", "");
}
pub fn grab_processed(&mut self) {
self.0.set("grab_processed", "");
}
pub fn release(&mut self, texture: u32) {
if texture == 0 {
return;
}
use std::collections::hash_map::Entry;
match self.0.response.entry("release".into()) {
Entry::Vacant(v) => {
v.insert(format!("{texture}"));
}
Entry::Occupied(mut o) => {
o.get_mut().push_str(&format!(",{texture}"));
}
}
}
pub fn window_mouse_x(&self) -> u32 {
self.0.get_required_u32("window_mouse_x")
}
pub fn window_mouse_y(&self) -> u32 {
self.0.get_required_u32("window_mouse_y")
}
pub fn picture_mouse_x(&self) -> i32 {
self.0.get_required_i32("picture_mouse_x")
}
pub fn picture_mouse_y(&self) -> i32 {
self.0.get_required_i32("picture_mouse_y")
}
pub fn timer(&self) -> Option<Duration> {
Some(Duration::from_secs_f64(self.0.get("timer")?.parse().ok()?))
}
pub fn cancel_timer(&mut self) {
self.0.set("cancel_timer", "");
}
}
#[derive(Debug)]
pub struct CommandGrabbed(Command);
#[derive(Debug, Clone, Copy, PartialEq)]
#[non_exhaustive]
pub enum TextureSource {
Camera,
Processed,
Upload,
Text,
Other,
}
impl CommandGrabbed {
pub fn texture(&self) -> u32 {
self.0.get_required_u32("texture")
}
pub fn source(&self) -> TextureSource {
match self.0.get_required("source") {
"camera" => TextureSource::Camera,
"processed" => TextureSource::Processed,
"upload" => TextureSource::Upload,
"text" => TextureSource::Text,
_ => TextureSource::Other,
}
}
pub fn text_dimensions(&self) -> Vec<(f32, f32, f32, f32)> {
let Some(raw) = self.0.get("text_dimensions") else {
assert_ne!(
self.source(),
TextureSource::Text,
"text_dimensions should always be passed for source = text."
);
return vec![];
};
let mut dimensions = vec![];
for line in raw.split('\n') {
let mut dim = [0.0f32; 4];
let mut dim_iter = dim.iter_mut();
for part in line.split(',') {
let d = dim_iter.next().expect(
"Each line of text_dimensions should only have 4 comma-separated parts.",
);
*d = part
.parse()
.expect("text_dimensions should consist of floats.");
}
assert!(
dim_iter.next().is_none(),
"Each line of text_dimensions should have exactly 4 comma-separated parts."
);
let [x, y, width, height] = dim;
dimensions.push((x, y, width, height));
}
dimensions
}
}
#[derive(Debug)]
pub struct CommandMouseDown(Command);
impl CommandMouseDown {
pub fn window_x(&self) -> u32 {
self.0.get_required_u32("window_x")
}
pub fn window_y(&self) -> u32 {
self.0.get_required_u32("window_y")
}
pub fn picture_x(&self) -> i32 {
self.0.get_required_i32("picture_x")
}
pub fn picture_y(&self) -> i32 {
self.0.get_required_i32("picture_y")
}
pub fn button(&self) -> char {
self.0
.get_required("button")
.chars()
.next()
.expect("button must be exactly 1 character.")
}
pub fn clicks(&self) -> u32 {
self.0.get_required_u32("clicks")
}
}
#[derive(Debug)]
pub struct CommandMouseUp(Command);
impl CommandMouseUp {
pub fn window_x(&self) -> u32 {
self.0.get_required_u32("window_x")
}
pub fn window_y(&self) -> u32 {
self.0.get_required_u32("window_y")
}
pub fn picture_x(&self) -> i32 {
self.0.get_required_i32("picture_x")
}
pub fn picture_y(&self) -> i32 {
self.0.get_required_i32("picture_y")
}
pub fn button(&self) -> char {
self.0
.get_required("button")
.chars()
.next()
.expect("button must be exactly 1 character.")
}
}
#[derive(Debug)]
pub struct CommandMouseScroll(Command);
impl CommandMouseScroll {
pub fn logical_x(&self) -> i32 {
self.0.get_required_i32("logical_x")
}
pub fn logical_y(&self) -> i32 {
self.0.get_required_i32("logical_y")
}
pub fn physical_x(&self) -> i32 {
self.0.get_required_i32("physical_x")
}
pub fn physical_y(&self) -> i32 {
self.0.get_required_i32("physical_y")
}
pub fn window_x(&self) -> u32 {
self.0.get_required_u32("window_x")
}
pub fn window_y(&self) -> u32 {
self.0.get_required_u32("window_y")
}
pub fn picture_x(&self) -> i32 {
self.0.get_required_i32("picture_x")
}
pub fn picture_y(&self) -> i32 {
self.0.get_required_i32("picture_y")
}
}
#[derive(Debug)]
pub struct CommandSetting(Command);
impl CommandSetting {
pub fn id(&self) -> &str {
self.0.get_required("id")
}
pub fn value(&self) -> &str {
self.0.get_required("value")
}
pub fn done(&self) -> bool {
self.0.has("done")
}
}
#[derive(Debug)]
pub struct CommandPhoto(Command);
#[derive(Debug, Clone, Copy)]
#[non_exhaustive]
pub enum PhotoFormat {
Png,
Jpeg {
quality: u8,
},
Other,
}
impl CommandPhoto {
pub fn path(&self) -> &str {
self.0.get_required("path")
}
pub fn format(&self) -> PhotoFormat {
match self.0.get_required("format") {
"png" => PhotoFormat::Png,
"jpg" => PhotoFormat::Jpeg {
quality: self
.0
.get_required_u32("quality")
.try_into()
.expect("quality should fit in u8"),
},
_ => PhotoFormat::Other,
}
}
pub fn width(&self) -> u32 {
self.0.get_required_u32("width")
}
pub fn height(&self) -> u32 {
self.0.get_required_u32("height")
}
pub fn cancel(&mut self) {
self.0.set("cancel", "");
}
pub fn disable_flash(&mut self) {
self.0.set("no_flash", "");
}
pub fn set_camera_shader(&mut self, shader: Shader) {
self.0.set_shader("camera", shader);
}
pub fn clear_camera_shader(&mut self) {
self.0.clear_shader("camera");
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct CommandPhotoOk(Command);
#[derive(Debug)]
#[allow(dead_code)]
pub struct CommandPhotoError(Command);
#[derive(Debug)]
pub struct CommandVideoStart(Command);
impl CommandVideoStart {
pub fn path(&self) -> &str {
self.0.get_required("path")
}
pub fn width(&self) -> u32 {
self.0.get_required_u32("width")
}
pub fn height(&self) -> u32 {
self.0.get_required_u32("height")
}
pub fn framerate(&self) -> f64 {
self.0.get_required_f64("framerate")
}
pub fn cancel(&mut self) {
self.0.set("cancel", "");
}
}
#[derive(Debug)]
#[allow(dead_code)]
pub struct CommandVideoStarted(Command);
#[derive(Debug)]
#[allow(dead_code)]
pub struct CommandVideoStop(Command);
#[allow(unused_variables)]
pub trait Plugin {
fn init(command: &mut CommandInit) -> std::result::Result<Self, Box<dyn std::error::Error>>
where
Self: Sized;
fn setting(&mut self, command: &mut CommandSetting) {}
fn quit(&mut self, command: &mut CommandQuit) {}
fn key_down(&mut self, command: &mut CommandKeyDown) {}
fn key_up(&mut self, command: &mut CommandKeyUp) {}
fn camera_connected(&mut self, command: &mut CommandCameraConnected) {}
fn camera_disconnected(&mut self, command: &mut CommandCameraDisconnected) {}
fn frame(&mut self, command: &mut CommandFrame) {}
fn grabbed(&mut self, command: &mut CommandGrabbed) {}
fn mouse_down(&mut self, command: &mut CommandMouseDown) {}
fn mouse_up(&mut self, command: &mut CommandMouseUp) {}
fn mouse_scroll(&mut self, command: &mut CommandMouseScroll) {}
fn photo(&mut self, command: &mut CommandPhoto) {}
fn photo_ok(&mut self, command: &mut CommandPhotoOk) {}
fn photo_error(&mut self, command: &mut CommandPhotoError) {}
fn video_start(&mut self, command: &mut CommandVideoStart) {}
fn video_started(&mut self, command: &mut CommandVideoStarted) {}
fn video_stop(&mut self, command: &mut CommandVideoStop) {}
fn command(&mut self, command: &mut Command) {}
}
fn handle_specific_command(plugin: &mut dyn Plugin, command: Command) -> Command {
macro_rules! handle {
($method:ident, $type:ident) => {{
let mut specific = $type(command);
let _: &dyn fmt::Debug = &specific;
plugin.$method(&mut specific);
return specific.0;
}};
}
match command.name.as_str() {
"_camera_connected" => handle!(camera_connected, CommandCameraConnected),
"_camera_disconnected" => handle!(camera_disconnected, CommandCameraDisconnected),
"frame" => handle!(frame, CommandFrame),
"_grabbed" => handle!(grabbed, CommandGrabbed),
"_key_down" => handle!(key_down, CommandKeyDown),
"_key_up" => handle!(key_up, CommandKeyUp),
"_mouse_down" => handle!(mouse_down, CommandMouseDown),
"_mouse_scroll" => handle!(mouse_scroll, CommandMouseScroll),
"_mouse_up" => handle!(mouse_up, CommandMouseUp),
"photo" => handle!(photo, CommandPhoto),
"_photo_error" => handle!(photo_error, CommandPhotoError),
"_photo_ok" => handle!(photo_ok, CommandPhotoOk),
"_quit" => handle!(quit, CommandQuit),
"_setting" => handle!(setting, CommandSetting),
"video_start" => handle!(video_start, CommandVideoStart),
"_video_started" => handle!(video_started, CommandVideoStarted),
"_video_stop" => handle!(video_stop, CommandVideoStop),
_ => (),
}
command
}
fn receive_command(socket: &mut Socket) -> Result<Command> {
let mut name = String::new();
socket.read_line(&mut name)?;
if name.ends_with('\n') {
name.pop();
}
let mut line = String::new();
let mut first_line_of_argument_value = false;
let mut arguments = HashMap::new();
let mut argument_name = String::new();
let mut argument_value = String::new();
loop {
line.clear();
socket.read_line(&mut line)?;
if line.ends_with('\n') {
line.pop();
}
if line.is_empty() {
break;
}
if let Some(line) = line.strip_prefix('>') {
if !first_line_of_argument_value {
argument_value.push('\n');
}
first_line_of_argument_value = false;
argument_value.push_str(line);
} else if let Some(arg) = line.strip_suffix(':') {
if arg.is_empty() {
Err(IError::InvalidLineFormat(line.clone().into()))?
}
if !argument_name.is_empty() {
arguments.insert(argument_name, take(&mut argument_value));
}
first_line_of_argument_value = true;
argument_name = arg.into();
} else {
Err(IError::InvalidLineFormat(line.clone().into()))?
}
}
if !argument_name.is_empty() {
arguments.insert(argument_name, argument_value);
}
Ok(Command {
name,
arguments,
response: Default::default(),
})
}
fn run_with_plugin(plugin: &mut dyn Plugin, socket: &mut BufReader<File>) -> Result<()> {
loop {
let mut command = receive_command(socket)?;
plugin.command(&mut command);
let command = handle_specific_command(plugin, command);
if !command.name.starts_with('_') {
command.respond(socket.get_mut())?;
}
if command.name == "_quit" {
break;
}
}
Ok(())
}
pub fn try_run<P: Plugin>() -> Result<()> {
let file = unsafe { std::fs::File::from_raw_fd(3) };
let mut socket = BufReader::new(file);
let command = receive_command(&mut socket)?;
if command.name != "init" {
Err(IError::NoInit)?;
}
let mut command_init = CommandInit(command);
let vmajor = command_init.version_major();
if vmajor != 1 {
Err(IError::CantHandleVersion(vmajor))?;
}
let mut plugin = match P::init(&mut command_init) {
Ok(plugin) => plugin,
Err(e) => {
command_init.0.set("error", format!("{e}"));
return command_init.0.respond(socket.get_mut());
}
};
command_init.0.set("ok", "");
command_init.0.respond(socket.get_mut())?;
run_with_plugin(&mut plugin, &mut socket)
}
pub fn run<P: Plugin>() -> ExitCode {
match try_run::<P>() {
Err(e) => {
eprintln!("Error: {e}");
ExitCode::FAILURE
}
Ok(()) => ExitCode::SUCCESS,
}
}