userd 0.2.0

A user daemon, managing services and regular running of jobs, in user space.
use std::sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
};

use anyhow::Ok;
use nu_protocol::{
    PipelineData, Signals,
    ast::Block,
    debugger::WithoutDebug,
    engine::{EngineState, Stack, StateWorkingSet},
};

use crate::prelude::*;

pub(crate) struct Activity {
    name: String,
    engine_state: EngineState,
    block: Arc<Block>,
    ctrl_c: Arc<AtomicBool>,
}

impl Activity {
    pub(crate) fn new(name: String, command: String) -> Result<Self> {
        let ctrl_c = Arc::new(AtomicBool::new(false));

        let mut engine_state = nu_cmd_lang::create_default_context();
        engine_state = nu_command::add_shell_command_context(engine_state);
        engine_state.set_signals(Signals::new(ctrl_c.clone()));

        let init_cwd = std::env::current_dir()?;
        nu_cli::gather_parent_env_vars(&mut engine_state, init_cwd.as_ref());

        let mut working_set = StateWorkingSet::new(&engine_state);
        let block = nu_parser::parse(&mut working_set, None, command.as_bytes(), false);
        engine_state.merge_delta(working_set.render())?;

        Ok(Self {
            name,
            engine_state,
            block,
            ctrl_c,
        })
    }

    pub(crate) fn start_activity(&self) -> impl Future<Output = Result<()>> + use<> {
        let engine_state = self.engine_state.clone();
        let block = self.block.clone();
        let name = self.name.clone();

        async {
            tokio::task::spawn_blocking(move || {
                let mut stack = Stack::new();
                log::info!("Start activity '{name}'");
                let output = nu_engine::eval_block_with_early_return::<WithoutDebug>(
                    &engine_state,
                    &mut stack,
                    &block,
                    PipelineData::empty(),
                )?;
                output.drain()?;
                log::info!("Finished activity '{name}'");
                Ok(())
            })
            .await??;
            Ok(())
        }
    }

    pub(crate) fn cancel_activity(&self) {
        self.ctrl_c.store(true, Ordering::Relaxed);
    }
}