parent
660e69b3a4
commit
4d93975d9f
@ -0,0 +1,87 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
|
||||
|
||||
fn chunk32(chunk: &Vec<u8>) -> Result<(i32, i32)> {
|
||||
let (delta_chunk, size_chunk) = {
|
||||
let (mut delta_chunk, mut size_chunk): ([u8; 4], [u8; 4]) = ([0; 4], [0; 4]);
|
||||
for i in 0..4 {
|
||||
delta_chunk[i] = chunk[i];
|
||||
size_chunk[i] = chunk[i + 4];
|
||||
}
|
||||
(delta_chunk, size_chunk)
|
||||
};
|
||||
|
||||
let delta = i32::from_be_bytes(delta_chunk);
|
||||
let size = i32::from_be_bytes(size_chunk);
|
||||
|
||||
if size < 0 {
|
||||
return Err("size is too small".into());
|
||||
}
|
||||
|
||||
Ok((delta, size))
|
||||
}
|
||||
|
||||
fn chunk64(delta_chunk: &Vec<u8>, size_chunk: &Vec<u8>) -> Result<(i32, i32)> {
|
||||
let delta = i64::from_be_bytes(delta_chunk.as_slice().try_into()?);
|
||||
let size = i32::from_be_bytes(size_chunk.as_slice().try_into()?);
|
||||
|
||||
if delta > i32::MAX.into() {
|
||||
return Err("delta out of range".into());
|
||||
}
|
||||
|
||||
if size < 1 {
|
||||
return Err("size should not be zero".into());
|
||||
}
|
||||
|
||||
let delta: i32 = delta.try_into()?;
|
||||
|
||||
Ok((delta, size))
|
||||
}
|
||||
|
||||
pub fn parse_log(raw: &Vec<u8>) -> Result<Value> {
|
||||
let mut raw = raw.to_owned();
|
||||
let mut chunks: Vec<Map<String, Value>> = Vec::new();
|
||||
|
||||
while raw.len() > 0 {
|
||||
let chunk: Vec<u8> = raw.drain(0..7).collect();
|
||||
if chunk.len() != 8 {
|
||||
return Err("chunk size too small".into());
|
||||
}
|
||||
|
||||
let (delta, size) = {
|
||||
if let Ok((delta, size)) = chunk32(&chunk) {
|
||||
(delta, size)
|
||||
} else {
|
||||
let next_chunk: Vec<u8> = raw.drain(0..3).collect();
|
||||
if next_chunk.len() != 4 {
|
||||
return Err("next_chunk size too small".into());
|
||||
}
|
||||
|
||||
if let Ok((delta, size)) = chunk64(&chunk, &next_chunk) {
|
||||
(delta, size)
|
||||
} else {
|
||||
return Err("broken".into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let r = Range { start: 0 as usize, end: (size - 1) as usize };
|
||||
|
||||
let text_chunk: Vec<u8> = raw.drain(r).collect();
|
||||
if text_chunk.len() != size as usize {
|
||||
return Err("text_chunk size too small".into());
|
||||
}
|
||||
|
||||
let text: String = String::from_utf8_lossy(&text_chunk).into();
|
||||
|
||||
let mut bm: Map<String, Value> = Map::new();
|
||||
bm.insert("delta".into(), delta.into());
|
||||
bm.insert("text".into(), text.into());
|
||||
chunks.push(bm);
|
||||
}
|
||||
|
||||
Ok(chunks.into())
|
||||
}
|
@ -1 +1,5 @@
|
||||
pub struct TinTin {}
|
||||
use serde_json::Value;
|
||||
|
||||
pub fn parse_log(raw: &Vec<u8>) -> Value {
|
||||
Value::Null
|
||||
}
|
||||
|
@ -1,21 +1,20 @@
|
||||
use actix_web::{post, web, HttpResponse};
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::{post, web, Error, HttpResponse};
|
||||
|
||||
use handlebars::Handlebars;
|
||||
use serde_json::json;
|
||||
|
||||
use crate::FormData;
|
||||
use crate::WotLog;
|
||||
|
||||
#[post("/submit")]
|
||||
pub async fn post(form: web::Form<FormData>, hb: web::Data<Handlebars<'_>>) -> HttpResponse {
|
||||
let form_data = form.into_inner();
|
||||
let wot_log = WotLog::from(&form_data);
|
||||
wot_log.parse();
|
||||
|
||||
let data = json!({
|
||||
"name": "rewot"
|
||||
});
|
||||
pub async fn post(
|
||||
payload: Multipart,
|
||||
hb: web::Data<Handlebars<'_>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let mut wotlog = WotLog::from_payload(payload).await?;
|
||||
wotlog.parse_log()?;
|
||||
|
||||
let body = hb.render("submit", &data).unwrap();
|
||||
//let body = hb.render("submit", &json!({})).unwrap();
|
||||
let body = format!("{:#?}", wotlog);
|
||||
|
||||
HttpResponse::Ok().body(body)
|
||||
Ok(HttpResponse::Ok().body(body))
|
||||
}
|
||||
|
@ -1,62 +1,97 @@
|
||||
use lazy_static::lazy_static;
|
||||
use log::{debug, error, info};
|
||||
use regex::Regex;
|
||||
use serde::Deserialize;
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::Error;
|
||||
|
||||
use crate::default_false;
|
||||
use crate::Action;
|
||||
use crate::Client;
|
||||
use crate::FormData;
|
||||
use chrono::Utc;
|
||||
use futures_util::StreamExt;
|
||||
use harsh::Harsh;
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
use crate::{client, Client};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WotLog {
|
||||
/// ID of the log
|
||||
#[serde(default = "String::new")]
|
||||
pub id: String,
|
||||
|
||||
/// Title of the log
|
||||
#[serde(default = "String::new")]
|
||||
pub title: String,
|
||||
|
||||
/// Name of the player
|
||||
#[serde(default = "String::new")]
|
||||
pub player: String,
|
||||
|
||||
/// Is the log public
|
||||
#[serde(default = "default_false")]
|
||||
pub public: bool,
|
||||
|
||||
/// Log
|
||||
#[serde(default = "String::new")]
|
||||
pub log: String,
|
||||
/// Raw bytes of file as received
|
||||
pub raw: Vec<u8>,
|
||||
|
||||
/// Client type
|
||||
#[serde(default = "Client::default")]
|
||||
pub client: Client,
|
||||
|
||||
/// Parsed JSON
|
||||
pub json: Value,
|
||||
}
|
||||
|
||||
impl From<&FormData> for WotLog {
|
||||
fn from(form_data: &FormData) -> Self {
|
||||
impl WotLog {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
id: "".into(),
|
||||
title: form_data.title.clone(),
|
||||
player: form_data.player.clone(),
|
||||
public: if form_data.public == "public" { true } else { false },
|
||||
log: form_data.log.clone(),
|
||||
client: Client::None,
|
||||
id: {
|
||||
let harsh = Harsh::default();
|
||||
let ts = Utc::now();
|
||||
harsh.encode(&[ts.timestamp_nanos() as u64])
|
||||
},
|
||||
player: String::new(),
|
||||
public: false,
|
||||
title: String::new(),
|
||||
raw: Vec::new(),
|
||||
json: Value::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WotLog {
|
||||
/// Read and parse the log
|
||||
pub fn parse(self: &Self) {
|
||||
let mut lineno: u128 = 1;
|
||||
for line in self.log.lines() {
|
||||
let line = line.trim_end_matches('\r');
|
||||
pub async fn from_payload(payload: Multipart) -> Result<Self, Error> {
|
||||
let mut payload = payload;
|
||||
let mut wotlog = WotLog::new();
|
||||
|
||||
while let Some(item) = payload.next().await {
|
||||
let mut field = item?;
|
||||
let mut data: Vec<u8> = Vec::new();
|
||||
|
||||
let action = Action::from(line);
|
||||
lineno += 1;
|
||||
while let Some(Ok(chunk)) = field.next().await {
|
||||
for byte in chunk {
|
||||
data.push(byte);
|
||||
}
|
||||
}
|
||||
|
||||
match field.name() {
|
||||
"submit_client" => {
|
||||
//let b: Vec<u8> = data.iter().map(|b| *b).collect();
|
||||
wotlog.client = Client::from(&String::from_utf8_lossy(&data).to_string())
|
||||
}
|
||||
"submit_player" => wotlog.player = String::from_utf8_lossy(&data).into(),
|
||||
"submit_public" => {
|
||||
if &data == "public".as_bytes() {
|
||||
wotlog.public = true;
|
||||
}
|
||||
}
|
||||
"submit_title" => wotlog.title = String::from_utf8_lossy(&data).into(),
|
||||
"submit_file" => wotlog.raw = data.into(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(wotlog)
|
||||
}
|
||||
|
||||
/// Read and parse the log
|
||||
pub fn parse_log(self: &mut Self) -> Result<(), Error> {
|
||||
self.json = match self.client {
|
||||
Client::Mudlet => client::mudlet::parse_log(&self.raw)?,
|
||||
Client::TinTin => Value::Null,
|
||||
Client::ZMud => Value::Null,
|
||||
Client::None => Value::Null,
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue