180 lines
5.0 KiB
Rust
180 lines
5.0 KiB
Rust
use crate::protobuf::gou::{MessageType, Starpoles};
|
|
use crate::utils::cxx_string_to_string;
|
|
use ::log::{debug, error, info, trace, warn};
|
|
use bytes::Bytes;
|
|
use cxx::CxxString;
|
|
use encoding_rs::Encoding;
|
|
use futures_util::SinkExt;
|
|
use prost::Message as WebMessage;
|
|
use spin::Mutex;
|
|
use std::sync::OnceLock;
|
|
use std::sync::mpsc;
|
|
use tokio::net::TcpStream;
|
|
use tokio::runtime::Runtime;
|
|
use tokio_tungstenite::tungstenite::Message;
|
|
|
|
static LOGGER_SENDER: OnceLock<mpsc::Sender<Starpoles>> = OnceLock::new();
|
|
|
|
mod config;
|
|
mod log;
|
|
mod network;
|
|
pub mod protobuf;
|
|
pub mod utils;
|
|
|
|
#[cxx::bridge]
|
|
mod ffi {
|
|
extern "Rust" {
|
|
fn download_file(url: &CxxString, path: &CxxString) -> Result<()>;
|
|
|
|
fn http_get(url: &CxxString) -> Result<String>;
|
|
|
|
/// 尝试将未知编码的字符串转换为UTF-8
|
|
fn to_utf8(string: &CxxString) -> String;
|
|
|
|
/// UTF-16LE转换为UTF-8
|
|
fn unicode_to_utf_8(string: &CxxString) -> String;
|
|
|
|
/// 猜测编码
|
|
fn guess_encoding(string: &CxxString) -> String;
|
|
|
|
fn init_log(is_debug: bool, ws_uel: &CxxString);
|
|
|
|
fn log_trace(msg: &CxxString);
|
|
|
|
fn log_debug(msg: &CxxString);
|
|
|
|
fn log_info(msg: &CxxString);
|
|
|
|
fn log_warning(msg: &CxxString);
|
|
|
|
fn log_error(msg: &CxxString);
|
|
}
|
|
}
|
|
|
|
fn unicode_to_utf_8(string: &CxxString) -> String {
|
|
// 约定:传入的内容按 UTF-16LE 字节序解释
|
|
let mut bytes = string.as_bytes();
|
|
|
|
// UTF-16 必须是偶数长度;若是奇数长度,丢弃最后 1 个字节避免越界/误解码
|
|
if (bytes.len() & 1) == 1 {
|
|
bytes = &bytes[..bytes.len() - 1];
|
|
}
|
|
|
|
// 用 encoding_rs 直接把 UTF-16LE 转成 Rust 的 UTF-8 String
|
|
let (cow, _actual_used, _had_errors) = encoding_rs::UTF_16LE.decode(bytes);
|
|
cow.into_owned()
|
|
}
|
|
|
|
fn guess_encoding(string: &CxxString) -> String {
|
|
utils::guess_encoding_label(string)
|
|
}
|
|
|
|
fn to_utf8(string: &CxxString) -> String {
|
|
cxx_string_to_string(string).to_string()
|
|
}
|
|
|
|
fn log_error(msg: &cxx::CxxString) {
|
|
let msg = cxx_string_to_string(msg);
|
|
let lock = LOGGER_SENDER.get().unwrap();
|
|
let _ = lock.send(Starpoles {
|
|
r#type: MessageType::Error.into(),
|
|
message: msg,
|
|
});
|
|
//error!("{}", msg);
|
|
}
|
|
|
|
fn log_warning(msg: &cxx::CxxString) {
|
|
let msg = cxx_string_to_string(msg);
|
|
let lock = LOGGER_SENDER.get().unwrap();
|
|
let _ = lock.send(Starpoles {
|
|
r#type: MessageType::Warning.into(),
|
|
message: msg,
|
|
});
|
|
}
|
|
|
|
fn log_info(msg: &cxx::CxxString) {
|
|
let msg = cxx_string_to_string(msg);
|
|
let lock = LOGGER_SENDER.get().unwrap();
|
|
let _ = lock.send(Starpoles {
|
|
r#type: MessageType::Info.into(),
|
|
message: msg,
|
|
});
|
|
}
|
|
|
|
fn log_debug(msg: &cxx::CxxString) {
|
|
let msg = cxx_string_to_string(msg);
|
|
let lock = LOGGER_SENDER.get().unwrap();
|
|
let _ = lock.send(Starpoles {
|
|
r#type: MessageType::Error.into(),
|
|
message: msg,
|
|
});
|
|
}
|
|
|
|
fn log_trace(msg: &cxx::CxxString) {
|
|
let msg = cxx_string_to_string(msg);
|
|
let lock = LOGGER_SENDER.get().unwrap();
|
|
let _ = lock.send(Starpoles {
|
|
r#type: MessageType::Trace.into(),
|
|
message: msg,
|
|
});
|
|
}
|
|
|
|
fn init_log(is_debug: bool, ws_uel: &CxxString) {
|
|
let url = cxx_string_to_string(ws_uel);
|
|
let (tx, rx) = mpsc::channel::<Starpoles>();
|
|
LOGGER_SENDER.set(tx).expect("已经初始化过了");
|
|
std::thread::spawn(move || {
|
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
|
.enable_all()
|
|
.build()
|
|
.unwrap();
|
|
|
|
rt.block_on(async move {
|
|
loop {
|
|
match tokio_tungstenite::connect_async(url.clone()).await {
|
|
Ok((mut stream, _resp)) => {
|
|
while let Ok(line) = rx.recv() {
|
|
let mut buffer = vec![];
|
|
// Vec总是具备足够的空间
|
|
line.encode(&mut buffer).unwrap();
|
|
if stream
|
|
.send(Message::Binary(Bytes::from(buffer)))
|
|
.await
|
|
.is_err()
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
Err(_) => {
|
|
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
log::init_log(is_debug);
|
|
}
|
|
|
|
fn get_runtime() -> Runtime {
|
|
Runtime::new().expect("创建Tokio运行时失败")
|
|
}
|
|
|
|
fn download_file(
|
|
url: &cxx::CxxString,
|
|
path: &cxx::CxxString,
|
|
) -> Result<(), Box<dyn std::error::Error>> {
|
|
let rt = get_runtime();
|
|
let url = cxx_string_to_string(url);
|
|
let path = cxx_string_to_string(path);
|
|
rt.block_on(network::download_file(&url, &path))?;
|
|
Ok(())
|
|
}
|
|
|
|
fn http_get(url: &cxx::CxxString) -> Result<String, Box<dyn std::error::Error>> {
|
|
let rt = get_runtime();
|
|
let url = cxx_string_to_string(url);
|
|
rt.block_on(network::http_get(&url))
|
|
}
|