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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
use super::{Inner, Output};
use crate::sync::Mutex;
use std::fmt::{self, Debug};
use std::io::{Result, Write};
use std::rc::Rc;
use std::sync::Arc;
use termcolor::{Color, ColorSpec, WriteColor};

/// Unit of work arranged by a Sequencer.
///
/// Use the standard library `write!` or `writeln!` macros for writing the
/// output of a task. Additionally this type provides some methods for setting
/// the color of task output.
///
/// Refer to the crate-level documentation and the documentation of the
/// Sequencer type for the recommended patterns of launching tasks.
///
/// ```
/// use oqueue::{Color::Blue, Task};
///
/// fn work(task: Task) {
///     task.color(Blue);
///     writeln!(task, "hello from task #{}", task.index);
/// }
/// ```
#[readonly::make]
#[derive(Clone)]
pub struct Task {
    handle: Rc<Handle>,

    /// Index of the current task. This is a sequential counter that begins at 0
    /// and increments by 1 for each successively started task. It may be
    /// helpful in determining what work this task is responsible for
    /// performing.
    ///
    /// This field is read-only; writing to its value will not compile.
    #[readonly]
    pub index: usize,
}

struct Handle {
    inner: Arc<Mutex<Inner>>,
    index: usize,
}

impl Debug for Task {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter
            .debug_tuple("Task")
            .field(&self.handle.index)
            .finish()
    }
}

impl Task {
    pub(super) fn new(index: usize, inner: Arc<Mutex<Inner>>) -> Self {
        Task {
            handle: Rc::new(Handle { inner, index }),
            index,
        }
    }

    /// Set output to appear in bold uncolored.
    pub fn bold(&self) {
        let mut spec = ColorSpec::new();
        spec.set_bold(true);
        let _ = self.apply(|w| w.set_color(&spec));
    }

    /// Set output to appear in color (not bold).
    pub fn color(&self, color: Color) {
        let mut spec = ColorSpec::new();
        spec.set_fg(Some(color));
        let _ = self.apply(|w| w.set_color(&spec));
    }

    /// Set output to appear bold and colored.
    pub fn bold_color(&self, color: Color) {
        let mut spec = ColorSpec::new();
        spec.set_bold(true);
        spec.set_fg(Some(color));
        let _ = self.apply(|w| w.set_color(&spec));
    }

    /// Set output to non-bold uncolored.
    pub fn reset_color(&self) {
        let _ = self.apply(|w| w.reset());
    }

    #[doc(hidden)]
    pub fn write_fmt(&self, args: fmt::Arguments) {
        let _ = self.apply(|w| w.write_fmt(args));
    }

    fn apply<T>(&self, f: impl FnOnce(&mut dyn WriteColor) -> T) -> T {
        let inner = &mut *self.handle.inner.lock();

        if self.handle.index == inner.finished {
            f(&mut inner.stream)
        } else {
            f(&mut inner.get(self.handle.index).buffer)
        }
    }
}

impl Write for Task {
    fn write(&mut self, b: &[u8]) -> Result<usize> {
        self.apply(|w| w.write(b))
    }

    fn flush(&mut self) -> Result<()> {
        self.apply(|w| w.flush())
    }

    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
        self.apply(|w| w.write_all(buf))
    }

    fn write_fmt(&mut self, args: fmt::Arguments) -> Result<()> {
        self.apply(|w| w.write_fmt(args))
    }
}

impl WriteColor for Task {
    fn supports_color(&self) -> bool {
        self.apply(|w| w.supports_color())
    }

    fn set_color(&mut self, spec: &ColorSpec) -> Result<()> {
        self.apply(|w| w.set_color(spec))
    }

    fn reset(&mut self) -> Result<()> {
        self.apply(|w| w.reset())
    }
}

impl Drop for Handle {
    fn drop(&mut self) {
        let inner = &mut *self.inner.lock();

        inner.get(self.index).done = true;

        while inner.pending.get(0).map_or(false, Output::is_done) {
            inner.finished += 1;
            let mut task = inner.pending.pop_front().unwrap();
            let _ = task.buffer.reset();
            let _ = inner.writer.print(&task.buffer);
        }

        if let Some(head) = inner.pending.get_mut(0) {
            let _ = inner.writer.print(&head.buffer);
            head.buffer.clear();
        }
    }
}