1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
use std::io;
use std::process::ExitStatus;
use tokio::task::JoinHandle;

use super::super::kill;

#[derive(Clone)]
pub struct SharedCommandSpawned<T, R> {
    data: T,
    killer: kill::CommandKiller<R>,
}

pub(super) type CommandTokioJoinHandle<T> = JoinHandle<(
    io::Result<ExitStatus>,
    Option<kill::KillJoinHandleFinalStatus<T>>,
)>;

pub struct CommandJoinHandle<R>(CommandTokioJoinHandle<R>);

impl<R> CommandJoinHandle<R> {
    pub async fn join(self) -> super::CommandStopped<(), R> {
        let (exit_status, killed) = self.0.await.expect("command task handle should not panic");

        super::CommandStopped {
            data: (),
            exit_status,
            killed,
        }
    }
}

pub struct CommandSpawned<T, R> {
    pub data: T,
    pub killer: kill::CommandKiller<R>,
    pub join_handle: CommandJoinHandle<R>,
}

impl<T, R> CommandSpawned<T, R> {
    pub(super) fn new(
        data: T,
        kill_sender: kill::KillSender<R>,
        join_handle: CommandTokioJoinHandle<R>,
    ) -> Self {
        Self {
            data,
            killer: kill::CommandKiller::new(kill_sender),
            join_handle: CommandJoinHandle(join_handle),
        }
    }

    pub fn kill(&self, reason: kill::KillCommandReason<R>) -> kill::KillResult {
        self.killer.kill(reason)
    }

    pub async fn wait_into_stopped(self) -> super::CommandStopped<T, R> {
        let Self {
            data, join_handle, ..
        } = self;

        join_handle.join().await.with_data(data).1
    }

    pub fn with_data<S>(self, new_data: S) -> (T, CommandSpawned<S, R>) {
        let Self {
            data,
            join_handle,
            killer,
        } = self;

        (
            data,
            CommandSpawned {
                data: new_data,
                join_handle,
                killer,
            },
        )
    }
}

impl<T: Clone, R> CommandSpawned<T, R> {
    pub fn share_data(&self) -> T {
        self.data.clone()
    }
}

impl<T, R: Clone> CommandSpawned<T, R> {
    pub fn share_killer(&self) -> kill::CommandKiller<R> {
        self.killer.clone()
    }
}

impl<T: Clone, R: Clone> CommandSpawned<T, R> {
    pub fn share(&self) -> SharedCommandSpawned<T, R> {
        SharedCommandSpawned {
            data: self.data.clone(),
            killer: self.killer.clone(),
        }
    }
}