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
use crate::api::{APIClient, API, BASE_URL};
use crate::api_types::{compile, download, execute, fmt, share};
use crate::input;
use crate::printer::Printer;
use std::collections::HashMap;
use url::{ParseError, Url};

pub struct Handler {
    // TODO: mockable for test
    api_cli: APIClient,
    printer: Printer,
}

impl Handler {
    pub fn new() -> Handler {
        Handler {
            api_cli: APIClient::new(),
            printer: Printer::new(),
        }
    }

    pub fn run(&mut self, input: input::RunInput) -> Result<(), Box<dyn std::error::Error>> {
        let code = input::code_from_path(&input.file_path)?;

        // TODO: refactor for run_type
        let request = execute::Request {
            crate_type: "bin".to_string(),
            tests: false,
            mode: input.mode,
            channel: input.channel,
            edition: input.edition,
            backtrace: input.backtrace,
            code: code,
        };

        let resp = self.api_cli.execute(request)?;

        // TODO: handle following pattern.
        // error
        // Response { success: false, stdout: "", stderr: "", error: "Unable to deserialize request: Expected request with `Content-Type: application/json`" }
        //
        // error stdrtt
        // Response { success: false, stdout: "", stderr: "   Compiling playground v0.0.1 (/playground)\nerror: expected item, found `[`\n --> src/main.rs:1:1\n  |\n1 | [package]\n  | ^ expected item\n\nerror: could not compile `playground` due to previous error\n", error: "" }
        //
        // success
        // Response { success: true, stdout: "Client of The Rust Playground!\n", stderr: "   Compiling playground v0.0.1 (/playground)\n    Finished dev [unoptimized + debuginfo] target(s) in 2.66s\n     Running `target/debug/playground`\n", error: "" }
        //

        self.printer.print_run(resp)?;

        Ok(())
    }

    pub fn fmt(&mut self, input: input::FmtInput) -> Result<(), Box<dyn std::error::Error>> {
        let code = input::code_from_path(&input.file_path)?;

        let request = fmt::Request {
            edition: input.edition,
            code: code,
        };

        let resp = self.api_cli.fmt(request)?;

        self.printer.print_fmt(resp)?;

        Ok(())
    }

    pub fn share(&mut self, input: input::ShareInput) -> Result<(), Box<dyn std::error::Error>> {
        let code = input::code_from_path(&input.file_path)?;

        let request = share::Request { code: code };

        let resp = self.api_cli.share(request)?;

        self.printer.print_share(resp)?;

        Ok(())
    }

    pub fn download(
        &mut self,
        input: input::DownloadInput,
    ) -> Result<(), Box<dyn std::error::Error>> {
        let id = pick_id_from(input.id_or_url)?;

        let request = download::Request { id: id };

        let resp = self.api_cli.download(request)?;

        self.printer.print_download(resp)?;

        Ok(())
    }
}

// TODO: error handling
fn pick_id_from(id_or_url: String) -> Result<String, Box<std::error::Error>> {
    if id_or_url.starts_with(BASE_URL) {
        // parse url
        // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7b61a4b7baf87fb4b5feb8668721ff8b
        let url = Url::parse(&id_or_url);
        if let Ok(url) = url {
            let hash_query: HashMap<_, _> = url.query_pairs().into_owned().collect();

            match hash_query.get("gist") {
                Some(id) => return Ok(id.to_string()),
                None => return Ok("".to_string()),
            };
        } else {
            return Ok("".to_string());
        }
    } else {
        // maybe id
        Ok(id_or_url)
    }
}