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
mod models;

use crate::models::{TranslateRequest, TranslateRequestData, TranslateResponse};
use async_process::{Child, ChildStdin, ChildStdout, Command, Stdio};
use async_trait::async_trait;
use futures_lite::io::AsyncWriteExt;
use futures_lite::AsyncReadExt;
use marek_translate_api::TextTranslator;
use std::error::Error;

pub struct TranslateLocally {
    _command: Child,
    stdin: ChildStdin,
    stdout: ChildStdout,
}

unsafe impl Send for TranslateLocally {}

impl TranslateLocally {
    pub fn new() -> Result<Self, Box<dyn Error + Send + Sync>> {
        #[cfg(target_os = "windows")]
        let mut command = Command::new("./translateLocally.exe")
            .arg("-p")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()?;

        #[cfg(not(target_os = "windows"))]
        let mut command = Command::new("./translateLocally")
            .arg("-p")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()?;

        let stdin = command.stdin.take().unwrap();
        let stdout = command.stdout.take().unwrap();

        Ok(TranslateLocally {
            _command: command,
            stdin,
            stdout,
        })
    }
}

#[async_trait]
impl TextTranslator for TranslateLocally {
    async fn translate(
        &mut self,
        source_text: &str,
        source_lang: &str,
        dest_lang: &str,
    ) -> Result<String, Box<dyn Error + Send + Sync>> {
        let request = TranslateRequest {
            id: 1,
            command: "Translate",
            data: TranslateRequestData {
                src: source_lang,
                trg: dest_lang,
                text: source_text,
            },
        };

        let request = serde_json::to_vec(&request)?;
        let length = (request.len() as u32).to_ne_bytes();

        self.stdin.write(&length).await?;
        self.stdin.write(&request).await?;

        let mut response_len = [0u8; 4];
        self.stdout.read(&mut response_len).await?;
        let response_len = u32::from_ne_bytes(response_len);

        let mut res = vec![0u8; response_len as usize];
        self.stdout.read(&mut res).await?;

        let response = serde_json::from_slice::<TranslateResponse>(&res)?;

        if !response.success {
            return Err("translation response doesn't contain success".into());
        }

        if let Some(data) = response.data {
            Ok(data.target.text)
        } else {
            Err("translation response doesn't contain data".into())
        }
    }
}