barley-std 0.4.0

The Barley standard library
Documentation
use barley_runtime::prelude::*;
use tokio::{fs::File, io::AsyncWriteExt};
use std::path::PathBuf;


pub struct WriteFile {
    path: PathBuf,
    content: ActionInput<String>
}

impl WriteFile {
    pub fn new_static<P, S>(path: P, content: S) -> Self
    where
        P: Into<PathBuf>,
        S: ToString,
    {
        Self {
            path: path.into(),
            content: ActionInput::new_static(content.to_string()),
        }
    }

    pub fn new_dynamic<P>(path: P, content: ActionObject) -> Self
    where
        P: Into<PathBuf>,
    {
        Self {
            path: path.into(),
            content: ActionInput::new_dynamic(content)
        }
    }
}

#[async_trait]
impl Action for WriteFile {
    async fn probe(&self, _runtime: Runtime) -> Result<Probe, ActionError> {
        Ok(Probe {
            needs_run: true,
            can_rollback: false
        })
    }

    async fn run(&self, runtime: Runtime, op: Operation) -> Result<Option<ActionOutput>, ActionError> {
        if matches!(op, Operation::Rollback) {
            return Err(ActionError::OperationNotSupported)
        }

        let content = match self.content {
            ActionInput::Static(ref s) => s.clone(),
            ActionInput::Dynamic(ref obj) => {
                let output = runtime.get_output(obj.clone()).await
                    .ok_or(ActionError::ActionFailed(
                        "Failed to get output".to_string(),
                        "Failed to get output".to_string()
                    ))?;

                match output {
                    ActionOutput::String(s) => s,
                    _ => return Err(ActionError::ActionFailed(
                        "Output is not a string".to_string(),
                        "Output is not a string".to_string()
                    ))
                }
            }
        };

        let mut file = File::create(&self.path).await
            .map_err(|e| ActionError::ActionFailed(
                format!("Failed to create file: {}", e),
                format!("Failed to create file: {}", self.path.display())
            ))?;
        
        file.write_all(content.as_bytes()).await
            .map_err(|e| ActionError::ActionFailed(
                format!("Failed to write to file: {}", e),
                format!("Failed to write to file: {}", self.path.display())
            ))?;
        
        Ok(None)
    }

    fn display_name(&self) -> String {
        format!("Write file {}", self.path.display())
    }
}

pub struct ReadFile {
    path: PathBuf
}

impl ReadFile {
    pub fn new<P>(path: P) -> Self
    where
        P: Into<PathBuf>,
    {
        Self {
            path: path.into()
        }
    }
}

#[async_trait]
impl Action for ReadFile {
    async fn probe(&self, _runtime: Runtime) -> Result<Probe, ActionError> {
        Ok(Probe {
            needs_run: true,
            can_rollback: false
        })
    }

    async fn run(&self, _runtime: Runtime, op: Operation) -> Result<Option<ActionOutput>, ActionError> {
        if matches!(op, Operation::Rollback) {
            return Err(ActionError::OperationNotSupported)
        }

        let content = tokio::fs::read_to_string(&self.path).await
            .map_err(|e| ActionError::ActionFailed(
                format!("Failed to read file: {}", e),
                format!("Failed to read file: {}", self.path.display())
            ))?;

        Ok(Some(ActionOutput::String(content)))
    }

    fn display_name(&self) -> String {
        format!("Read file {}", self.path.display())
    }
}

pub struct DeleteFile {
    path: PathBuf
}

impl DeleteFile {
    pub fn new<P>(path: P) -> Self
    where
        P: Into<PathBuf>,
    {
        Self {
            path: path.into()
        }
    }
}

#[async_trait]
impl Action for DeleteFile {
    async fn probe(&self, _runtime: Runtime) -> Result<Probe, ActionError> {
        Ok(Probe {
            needs_run: self.path.exists(),
            can_rollback: false
        })
    }

    async fn run(&self, _runtime: Runtime, op: Operation) -> Result<Option<ActionOutput>, ActionError> {
        if matches!(op, Operation::Rollback) {
            return Err(ActionError::OperationNotSupported)
        }

        tokio::fs::remove_file(&self.path).await
            .map_err(|e| ActionError::ActionFailed(
                format!("Failed to delete file: {}", e),
                format!("Failed to delete file: {}", self.path.display())
            ))?;

        Ok(None)
    }

    fn display_name(&self) -> String {
        format!("Delete file {}", self.path.display())
    }
}