Skip to content

Commit

Permalink
Move ShellScript out of the workflow module
Browse files Browse the repository at this point in the history
Signed-off-by: Didier Wenzek <[email protected]>
  • Loading branch information
didier-wenzek committed Oct 25, 2024
1 parent 8c4587f commit 9304a6b
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 77 deletions.
1 change: 1 addition & 0 deletions crates/core/tedge_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod health;
pub mod measurement;
pub mod mqtt_topics;
pub mod path;
pub mod script;
mod software;
mod store;
pub mod workflow;
Expand Down
80 changes: 80 additions & 0 deletions crates/core/tedge_api/src/script.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
use serde::de::Error;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use std::fmt::Display;
use std::fmt::Formatter;
use std::str::FromStr;

/// A parsed Unix command line
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ShellScript {
pub command: String,
pub args: Vec<String>,
}

/// Deserialize an Unix command line
impl<'de> Deserialize<'de> for ShellScript {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let command_line = String::deserialize(deserializer)?;
command_line.parse().map_err(Error::custom)
}
}

impl FromStr for ShellScript {
type Err = String;

fn from_str(command_line: &str) -> Result<Self, Self::Err> {
let mut args = shell_words::split(command_line)
.map_err(|err| format!("invalid script: {command_line}: {err}"))?;
if args.is_empty() {
Err("invalid script: empty".to_string())
} else {
let script = args.remove(0);
Ok(ShellScript {
command: script,
args,
})
}
}
}

/// Serialize an Unix command line, using appropriate quotes
impl Serialize for ShellScript {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(serializer)
}
}

impl Display for ShellScript {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut args = vec![self.command.clone()];
args.append(&mut self.args.clone());
f.write_str(&shell_words::join(args))
}
}

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

#[test]
fn script_parse_and_display() {
let script: ShellScript = "sh -c 'sleep 10'".parse().unwrap();
assert_eq!(
script,
ShellScript {
command: "sh".to_string(),
args: vec!["-c".to_string(), "sleep 10".to_string()]
}
);
assert_eq!(format!("{script}"), "sh -c 'sleep 10'");
}
}
1 change: 1 addition & 0 deletions crates/core/tedge_api/src/workflow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod toml_config;
use crate::mqtt_topics::EntityTopicId;
use crate::mqtt_topics::MqttSchema;
use crate::mqtt_topics::OperationType;
use crate::script::ShellScript;
use ::log::info;
pub use error::*;
use mqtt_channel::MqttMessage;
Expand Down
76 changes: 2 additions & 74 deletions crates/core/tedge_api/src/workflow/script.rs
Original file line number Diff line number Diff line change
@@ -1,72 +1,11 @@
use crate::workflow::GenericStateUpdate;
use crate::workflow::ScriptDefinitionError;
use serde::de::Error;
use serde::Deserialize;
use serde::Deserializer;
use serde::Serialize;
use serde::Serializer;
use serde_json::Value;
use std::cmp::max;
use std::fmt::Display;
use std::fmt::Formatter;
use std::os::unix::prelude::ExitStatusExt;
use std::str::FromStr;
use std::time::Duration;

/// A parsed Unix command line
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ShellScript {
pub command: String,
pub args: Vec<String>,
}

/// Deserialize an Unix command line
impl<'de> Deserialize<'de> for ShellScript {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let command_line = String::deserialize(deserializer)?;
command_line.parse().map_err(Error::custom)
}
}

impl FromStr for ShellScript {
type Err = String;

fn from_str(command_line: &str) -> Result<Self, Self::Err> {
let mut args = shell_words::split(command_line)
.map_err(|err| format!("invalid script: {command_line}: {err}"))?;
if args.is_empty() {
Err("invalid script: empty".to_string())
} else {
let script = args.remove(0);
Ok(ShellScript {
command: script,
args,
})
}
}
}

/// Serialize an Unix command line, using appropriate quotes
impl Serialize for ShellScript {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
self.to_string().serialize(serializer)
}
}

impl Display for ShellScript {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut args = vec![self.command.clone()];
args.append(&mut self.args.clone());
f.write_str(&shell_words::join(args))
}
}

/// Define how to interpret the exit code of a script as the next state for a command
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct ExitHandlers {
Expand Down Expand Up @@ -396,23 +335,12 @@ impl Default for DefaultHandlers {
#[cfg(test)]
mod tests {
use super::*;
use crate::script::ShellScript;
use crate::workflow::OperationAction;
use crate::workflow::OperationWorkflow;
use serde_json::json;
use std::process::Command;

#[test]
fn script_parse_and_display() {
let script: ShellScript = "sh -c 'sleep 10'".parse().unwrap();
assert_eq!(
script,
ShellScript {
command: "sh".to_string(),
args: vec!["-c".to_string(), "sleep 10".to_string()]
}
);
assert_eq!(format!("{script}"), "sh -c 'sleep 10'");
}
use std::str::FromStr;

#[test]
fn successful_exit_code_determines_next_state() {
Expand Down
2 changes: 1 addition & 1 deletion crates/core/tedge_api/src/workflow/toml_config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::mqtt_topics::OperationType;
use crate::script::ShellScript;
use crate::workflow::AwaitHandlers;
use crate::workflow::DefaultHandlers;
use crate::workflow::ExecHandlers;
Expand All @@ -9,7 +10,6 @@ use crate::workflow::IterateHandlers;
use crate::workflow::OperationAction;
use crate::workflow::OperationWorkflow;
use crate::workflow::ScriptDefinitionError;
use crate::workflow::ShellScript;
use crate::workflow::WorkflowDefinitionError;
use serde::de::Error;
use serde::Deserialize;
Expand Down
4 changes: 2 additions & 2 deletions crates/extensions/c8y_mapper_ext/src/converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ use tedge_api::mqtt_topics::IdGenerator;
use tedge_api::mqtt_topics::MqttSchema;
use tedge_api::mqtt_topics::OperationType;
use tedge_api::pending_entity_store::PendingEntityData;
use tedge_api::script::ShellScript;
use tedge_api::workflow::GenericCommandState;
use tedge_api::workflow::ShellScript;
use tedge_api::CommandLog;
use tedge_api::DownloadInfo;
use tedge_api::EntityStore;
Expand Down Expand Up @@ -1655,7 +1655,7 @@ pub(crate) mod tests {
use tedge_api::mqtt_topics::MqttSchema;
use tedge_api::mqtt_topics::OperationType;
use tedge_api::pending_entity_store::PendingEntityData;
use tedge_api::workflow::ShellScript;
use tedge_api::script::ShellScript;
use tedge_api::SoftwareUpdateCommand;
use tedge_config::AutoLogUpload;
use tedge_config::SoftwareManagementApiFlag;
Expand Down

0 comments on commit 9304a6b

Please sign in to comment.