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
use std::future::IntoFuture;

use crate::{
    model::{RenderAdded, RenderOptions, RenderSkinOption},
    routing::Route,
    util::multipart::Form,
    ClientError, OrdrClient,
};

use super::{OrdrFuture, Request};

enum ReplaySource<'a> {
    File(&'a [u8]),
    Url(&'a str),
}

/// Commission a render job to o!rdr.
///
/// If successful, progress of the rendering can be tracking through the [`OrdrWebsocket`](crate::OrdrWebsocket).
#[must_use]
pub struct CommissionRender<'a> {
    ordr: &'a OrdrClient,
    replay_source: ReplaySource<'a>,
    username: &'a str,
    skin: &'a RenderSkinOption<'a>,
    options: Option<&'a RenderOptions>,
}

impl<'a> CommissionRender<'a> {
    pub(crate) const fn with_file(
        ordr: &'a OrdrClient,
        replay_file: &'a [u8],
        username: &'a str,
        skin: &'a RenderSkinOption<'a>,
    ) -> Self {
        Self {
            ordr,
            replay_source: ReplaySource::File(replay_file),
            username,
            skin,
            options: None,
        }
    }

    pub(crate) const fn with_url(
        ordr: &'a OrdrClient,
        replay_url: &'a str,
        username: &'a str,
        skin: &'a RenderSkinOption<'a>,
    ) -> Self {
        Self {
            ordr,
            replay_source: ReplaySource::Url(replay_url),
            username,
            skin,
            options: None,
        }
    }

    /// Specify rendering options.
    pub fn options(mut self, options: &'a RenderOptions) -> Self {
        self.options = Some(options);

        self
    }
}

impl IntoFuture for &mut CommissionRender<'_> {
    type Output = Result<RenderAdded, ClientError>;
    type IntoFuture = OrdrFuture<RenderAdded>;

    fn into_future(self) -> Self::IntoFuture {
        let mut form = self.options.map_or_else(Form::new, Form::serialize);

        match self.replay_source {
            ReplaySource::File(bytes) => form.push_replay("replayFile", bytes),
            ReplaySource::Url(url) => form.push_text("replayURL", url),
        };

        form.push_text("username", self.username);

        match self.skin {
            RenderSkinOption::Official { name } => {
                form.push_text("skin", name.as_ref())
                    .push_text("customSkin", "false");
            }
            RenderSkinOption::Custom { id } => {
                form.push_text("skin", id.to_string())
                    .push_text("customSkin", "true");
            }
        }

        if let Some(verification) = self.ordr.verification() {
            form.push_text("verificationKey", verification.as_str());
        }

        self.ordr
            .request(Request::builder(Route::Render).form(form).build())
    }
}

impl IntoFuture for CommissionRender<'_> {
    type Output = Result<RenderAdded, ClientError>;
    type IntoFuture = OrdrFuture<RenderAdded>;

    fn into_future(mut self) -> Self::IntoFuture {
        (&mut self).into_future()
    }
}