use core::cell::UnsafeCell;
use crate::wire::{DOC_VERSION, Format, Lint};
struct Store(UnsafeCell<(String, String)>);
unsafe impl Sync for Store {}
static SETTINGS: Store = Store(UnsafeCell::new((String::new(), String::new())));
pub unsafe fn store_settings(fmt_ptr: *const u8, fmt_len: u32, lint_ptr: *const u8, lint_len: u32) {
let fmt = unsafe { core::slice::from_raw_parts(fmt_ptr, fmt_len as usize) };
let lint = unsafe { core::slice::from_raw_parts(lint_ptr, lint_len as usize) };
let pair = (
String::from_utf8_lossy(fmt).into_owned(),
String::from_utf8_lossy(lint).into_owned(),
);
unsafe { *SETTINGS.0.get() = pair };
}
pub fn settings() -> (String, String) {
unsafe { (*SETTINGS.0.get()).clone() }
}
pub unsafe fn dispatch_format<F, E>(src_ptr: *const u8, src_len: u32, handler: F) -> *const u32
where
F: FnOnce(&str) -> Result<Format, E>,
E: core::fmt::Display,
{
unsafe {
crate::abi::dispatch(src_ptr, src_len, src_ptr, 0, |src, _| {
handler(src)
.map(|format| encode_format(&format))
.map_err(|e| e.to_string())
})
}
}
pub unsafe fn dispatch_lint<F, E>(src_ptr: *const u8, src_len: u32, handler: F) -> *const u32
where
F: FnOnce(&str) -> Result<Lint, E>,
E: core::fmt::Display,
{
unsafe {
crate::abi::dispatch(src_ptr, src_len, src_ptr, 0, |src, _| {
handler(src)
.map(|lint| encode_lint(&lint))
.map_err(|e| e.to_string())
})
}
}
fn encode_format(format: &Format) -> String {
let value = serde_json::json!({
"comments": format.comments,
"doc": DOC_VERSION,
"document": format.document,
"spans": format.spans,
});
serde_json::to_string(&value).expect("a reply always serialises")
}
fn encode_lint(lint: &Lint) -> String {
let value = serde_json::json!({
"comments": lint.comments,
"findings": lint.findings,
"luau": lint.luau,
});
serde_json::to_string(&value).expect("a reply always serialises")
}
#[cfg(test)]
mod tests {
use super::*;
use crate::wire::{Doc, Finding};
#[test]
fn a_format_reply_encodes_the_documented_shape() {
let format = Format::document(Doc::host(0, 4)).with_comments(vec![(0, 2)]);
let sent: serde_json::Value = serde_json::from_str(&encode_format(&format)).unwrap();
assert_eq!(
sent,
serde_json::json!({
"doc": 1,
"document": { "host": { "start": 0, "end": 4, "parse": "block" } },
"spans": [],
"comments": [[0, 2]],
})
);
}
#[test]
fn a_lint_reply_encodes_the_documented_shape() {
let lint = Lint {
findings: vec![Finding::new("tidy", (2, 7), "untidy")],
luau: Some("shadow".into()),
comments: vec![(0, 1)],
};
let sent: serde_json::Value = serde_json::from_str(&encode_lint(&lint)).unwrap();
assert_eq!(
sent,
serde_json::json!({
"findings": [{ "span": [2, 7], "lint": "tidy", "message": "untidy" }],
"luau": "shadow",
"comments": [[0, 1]],
})
);
}
#[test]
fn settings_store_and_read_back() {
assert_eq!(settings(), (String::new(), String::new()));
let fmt = "{\"column_width\":100}";
let lint = "{\"tidy\":\"warn\"}";
unsafe {
store_settings(
fmt.as_ptr(),
fmt.len() as u32,
lint.as_ptr(),
lint.len() as u32,
);
}
assert_eq!(settings(), (fmt.to_string(), lint.to_string()));
}
}