diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..a821aa99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ + +/target +**/*.rs.bk +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..ab32af11 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "rust-g" +version = "0.1.0" +authors = ["Bjorn Neergaard "] + +[lib] +crate-type = ["lib", "cdylib"] + +[profile.release] +lto = true + +[dependencies] +chrono = "0.4" +libc = "0.2.0" diff --git a/src/byond.rs b/src/byond.rs new file mode 100644 index 00000000..8ce3a96b --- /dev/null +++ b/src/byond.rs @@ -0,0 +1,67 @@ +use std::cell::Cell; +use std::ffi::{CStr, CString}; +use std::slice; + +use libc::{c_char, c_int}; + +thread_local! { + static RETURN_STRING: Cell = Cell::new(Default::default()); +} + +pub fn parse_args(argc: c_int, argv: *const *const c_char) -> Vec { + let args: Vec<&CStr> = unsafe { + slice::from_raw_parts(argv, argc as usize) + .into_iter() + .map(|ptr| CStr::from_ptr(*ptr)) + .collect() + }; + + args.into_iter() + .map(|cstr| cstr.to_string_lossy()) + .map(|str| str.into_owned()) + .collect() +} + +pub fn return_string(string: Option) -> *const c_char { + let cstring = match string { + Some(msg) => CString::new(msg).expect("null in returned string!"), + None => CString::new("").unwrap(), + }; + let ptr = cstring.as_ptr(); + + RETURN_STRING.with(|cell| { + cell.set(cstring); + }); + + ptr as *const c_char +} + +#[macro_export] +macro_rules! byond_function { + ($name:ident() $body:block) => { + #[no_mangle] + pub extern "C" fn $name( + _argc: ::libc::c_int, _argv: *const *const ::libc::c_char + ) -> *const ::libc::c_char { + ::byond::return_string((|| $body)()) + } + }; + + ($name:ident($($arg:ident),*) $body:block) => { + #[no_mangle] + pub extern "C" fn $name( + _argc: ::libc::c_int, _argv: *const *const ::libc::c_char + ) -> *const ::libc::c_char { + let __args = ::byond::parse_args(_argc, _argv); + + let mut __argn = 0; + $( + let $arg = &__args[__argn]; + __argn += 1; + )* + + ::byond::return_string((|| $body)()) + } + }; +} + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..9e07e501 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +extern crate chrono; +extern crate libc; + +#[macro_use] +mod byond; + +pub mod log; diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 00000000..66e6f9ef --- /dev/null +++ b/src/log.rs @@ -0,0 +1,64 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::fs; +use std::fs::{File, OpenOptions}; +use std::io; +use std::io::Write; +use std::path::Path; + +use chrono::Utc; + +thread_local! { + static FILE_MAP: RefCell> = RefCell::new(HashMap::new()); +} + +fn timestamp_string(data: &str) -> String { + format!("[{}] {}", Utc::now().format("%Y-%m-%d %H:%M:%S%.3f"), data) +} + +fn write(filename: &str, data: String) -> Result<(), io::Error> { + FILE_MAP.with(|cell| { + let mut map = cell.borrow_mut(); + let file = match map.entry(filename.to_owned()) { + Occupied(elem) => elem.into_mut(), + Vacant(elem) => { + let path = Path::new(filename); + match path.parent() { + Some(p) => fs::create_dir_all(p)?, + None => {}, + }; + + let file = OpenOptions::new() + .append(true) + .create(true) + .open(path)?; + elem.insert(file) + }, + }; + + writeln!(file, "{}", data) + }) +} + +fn close() { + FILE_MAP.with(|cell| { + let mut map = cell.borrow_mut(); + map.clear(); + }); +} + +byond_function! { log_write(filename, line) { + let line = timestamp_string(line); + + match write(filename, line) { + Ok(_) => None, + Err(err) => Some(err.to_string()), + } +} } + +byond_function! { log_close_all() { + close(); + + None +} }