Skip to content

Commit

Permalink
fix: fix compatibilities issues w/ api and docs
Browse files Browse the repository at this point in the history
* fix: Implement new communication protocol
* docs: internal_api & main
* fix: Add carriage return on send
* chore: renamed crate's models
* docs: documented api module
* fix: parse json with serialize_optionnal_string & docs
* fix: serde mapping & fix reading

Signed-off-by: Alexis-Bernard <[email protected]>
Signed-off-by: WoodenMaiden <[email protected]>
  • Loading branch information
alexis-ascoz authored and WoodenMaiden committed Apr 17, 2023
1 parent eee63df commit 2de3333
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 98 deletions.
3 changes: 3 additions & 0 deletions agent/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ thiserror = "1.0.32"
[lib]
name = "agent_lib"
path = "lib/src/lib.rs"

[dev-dependencies]
rand = "0.8.5"
67 changes: 67 additions & 0 deletions agent/lib/src/api/comms.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// This message is sent to the API server to indicate wether
// the agent is ready or not to receive messages.

pub const MESSAGE_SIZE_NB_BYTES: usize = 8;

/// Represents a message sent by the agent
pub struct Message {
/// These are characters e.g. 00002048
pub message_size: [u8; MESSAGE_SIZE_NB_BYTES],
/// stringified json, vec because size is unknown
pub message: Vec<u8>
}

impl Message {
pub fn new(message_to_send: String) -> Self {
let mut message_size = [0; MESSAGE_SIZE_NB_BYTES];
let message = message_to_send.as_bytes().to_vec();

let string_size = format!("{:0>8}", message.len());
//We can't call directly as bytes as both &str and String sizes are not known at
//compile time unlike message_size

for (i, c) in string_size.chars().enumerate() {
message_size[i] = c as u8;
}

Self {
message_size,
message
}
}

pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.message_size);
bytes.extend_from_slice(&self.message);
bytes
}


}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn new_message_well_encoded() {
let message_data = "Hello world".to_string();
let message = Message::new(message_data);
assert_eq!(message.message, [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]);
assert_eq!(message.message_size, [48, 48, 48, 48, 48, 48, 49, 49]);

assert_eq!(message.to_bytes(), [48, 48, 48, 48, 48, 48, 49, 49, 72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]);
}


#[test]
fn message_size_badly_encoded() {
let message_data = "Hello world".to_string();
let message = Message::new(message_data);
assert_eq!(message.message, [72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]);
assert_ne!(message.message_size, [48, 48, 48, 48, 48, 48, 49, 50]); // should be 11, is 12
}

}

File renamed without changes.
70 changes: 59 additions & 11 deletions agent/lib/src/external_api/model.rs → agent/lib/src/api/model.rs
Original file line number Diff line number Diff line change
@@ -1,65 +1,94 @@
use serde::{Deserialize, Serialize};


/// Represents a file to be included in the workspace
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub struct FileModel {
/// Name of the file, paths relative to the workspace
pub filename: String,
/// Content of the file
pub content: String,
}

#[derive(Deserialize, Serialize, Debug)]
pub struct CodeEntry {
pub files: Vec<FileModel>,
pub script: Vec<String>, // All commands to execute at startup
}

/// Identifies the type of the message
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum Type {
/// Status message to indicate that the agent is ready
Status,
/// Request message
Request,
/// Response message answering to a request message
Response,
}

/// Code to tell what the Request/Response message is about
#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)]
pub enum Code {
/// Represents a request to run the code or a response to such request
Run,
/// Indicates that your status is that you are ok and ready to communicate
Ok,
}

/// Represents a Status message
#[derive(Deserialize, Serialize, Debug)]
pub struct StatusMessage {
/// Type of the message
pub r#type: Type,
/// Code of the message
pub code: Code,
}

impl StatusMessage {
pub fn new() -> StatusMessage {
pub fn new(code: Code) -> StatusMessage {
StatusMessage {
// r#type is a reserved keyword in Rust, so we need to use the raw identifier syntax
r#type: Type::Status,
code: Code::Ok,
code
}
}
}

impl Default for StatusMessage {
fn default() -> Self {
Self::new()
Self::new(Code::Ok)
}
}

/// Serializes an Option<String> as a String by returning an empty string if the Option is None
fn serialize_optionnal_string<S>(value: &Option<String>, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match value {
Some(v) => serializer.serialize_str(v),
None => serializer.serialize_str(""),
}
}

/// Represents the output of a step
#[derive(Deserialize, Serialize, Debug)]
pub struct ResponseStep {
/// Command that was run
pub command: String,
/// Exit code of the command
pub result: i32,
pub stdout: String,
/// Stdout of the command. If it is None, it will be serialized as an empty string
/// to avoid api crashes
#[serde(serialize_with = "serialize_optionnal_string")]
pub stdout: Option<String>,
/// Stderr of the command
pub stderr: String,
/// Whether the stdout should be returned or not
#[serde(alias = "enableOutput")]
pub enable_output: bool,
}

impl ResponseStep {
pub fn new(
command: String,
result: i32,
stdout: String,
stdout: Option<String>,
stderr: String,
enable_output: bool,
) -> ResponseStep {
Expand All @@ -73,9 +102,12 @@ impl ResponseStep {
}
}

/// Contains the id of the request and the result of all steps
#[derive(Deserialize, Serialize, Debug)]
pub struct ResponseData {
/// Id of the request (UUID)
pub id: String,
/// Result of all steps
pub steps: Vec<ResponseStep>,
}

Expand All @@ -85,10 +117,14 @@ impl ResponseData {
}
}

/// Represents a Response message with code Type::Run, meaning that it is a response to a run code request
#[derive(Deserialize, Serialize, Debug)]
pub struct ResponseMessage {
/// Type of the message
pub r#type: Type,
/// Code of the message
pub code: Code,
/// Data of the message
pub data: ResponseData,
}

Expand All @@ -102,16 +138,24 @@ impl ResponseMessage {
}
}

/// Represent a step in the request with type Type::Run
#[derive(Deserialize, Serialize, Debug, Clone, PartialEq, Eq)]
pub struct RequestStep {
/// Command to run
pub command: String,
/// Whether the stdout should be returned or not (stderr will alaways be)
#[serde(alias = "enableOutput")]
pub enable_output: bool,
}

/// Represents the data of a request message with type Type::Run
#[derive(Deserialize, Serialize, Debug)]
pub struct RequestData {
/// Id of the request (UUID)
pub id: String,
/// Files to be included in the workspace, paths relative to the workspace
pub files: Vec<FileModel>,
/// Steps to be executed
pub steps: Vec<RequestStep>,
}

Expand All @@ -121,10 +165,14 @@ impl RequestData {
}
}

/// Represents a Request message with type Type::Run
#[derive(Deserialize, Serialize, Debug)]
pub struct RequestMessage {
/// Type of the message
pub r#type: Type,
/// Code of the message
pub code: Code,
/// Data of the message
pub data: RequestData,
}

Expand Down
Loading

0 comments on commit 2de3333

Please sign in to comment.