use std::{
collections::{BTreeMap, HashMap},
fmt, fs, io,
path::Path,
};
use serde::{
de::{self, Deserializer, Visitor},
ser::{SerializeMap, SerializeSeq, Serializer},
Deserialize, Serialize,
};
lazy_static::lazy_static! {
static ref ANY_TABLE: BTreeMap<String, Field> = {
let mut map = BTreeMap::new();
map.insert("*".to_owned(), Field::Any);
map
};
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct StandardLibrary {
#[serde(rename = "selene")]
pub meta: Option<StandardLibraryMeta>,
#[serde(flatten)]
pub globals: BTreeMap<String, Field>,
#[serde(skip)]
pub structs: BTreeMap<String, Field>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
pub struct StandardLibraryMeta {
#[serde(default)]
pub base: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub structs: Option<BTreeMap<String, BTreeMap<String, Field>>>,
}
#[derive(Debug)]
pub enum StandardLibraryError {
DeserializeError(toml::de::Error),
IoError(io::Error),
}
impl fmt::Display for StandardLibraryError {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
StandardLibraryError::DeserializeError(error) => {
write!(formatter, "deserialize error: {}", error)
}
StandardLibraryError::IoError(error) => write!(formatter, "io error: {}", error),
}
}
}
impl std::error::Error for StandardLibraryError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
use StandardLibraryError::*;
match self {
DeserializeError(error) => Some(error),
IoError(error) => Some(error),
}
}
}
impl From<io::Error> for StandardLibraryError {
fn from(error: io::Error) -> Self {
StandardLibraryError::IoError(error)
}
}
impl StandardLibrary {
pub fn from_name(name: &str) -> Option<StandardLibrary> {
macro_rules! names {
{$($name:expr => $path:expr,)+} => {
match name {
$(
$name => {
let mut std = toml::from_str::<StandardLibrary>(
include_str!($path)
).unwrap_or_else(|error| {
panic!(
"default standard library '{}' failed deserialization: {}",
name,
error,
)
});
if let Some(meta) = &std.meta {
if let Some(base_name) = &meta.base {
let base = StandardLibrary::from_name(base_name);
std.extend(
base.expect("built-in library based off of non-existent built-in"),
);
}
}
std.inflate();
Some(std)
},
)+
_ => None
}
};
}
names! {
"lua51" => "../default_std/lua51.toml",
"lua52" => "../default_std/lua52.toml",
}
}
pub fn from_config_name(
name: &str,
directory: Option<&Path>,
) -> Result<Option<StandardLibrary>, StandardLibraryError> {
let mut library: Option<StandardLibrary> = None;
for segment in name.split('+') {
let segment_library = match StandardLibrary::from_name(segment) {
Some(default) => default,
None => {
let mut path = directory
.map(Path::to_path_buf)
.unwrap_or_else(||
panic!(
"from_config_name used with no directory, but segment `{}` is not a built-in library",
segment
)
);
path.push(format!("{}.toml", segment));
match StandardLibrary::from_file(&path)? {
Some(library) => library,
None => return Ok(None),
}
}
};
match library {
Some(ref mut base) => base.extend(segment_library),
None => library = Some(segment_library),
};
}
if let Some(ref mut library) = library {
library.inflate();
}
Ok(library)
}
pub fn from_file(filename: &Path) -> Result<Option<StandardLibrary>, StandardLibraryError> {
let content = fs::read_to_string(filename)?;
let mut library: StandardLibrary =
toml::from_str(&content).map_err(StandardLibraryError::DeserializeError)?;
if let Some(meta) = &library.meta {
if let Some(base_name) = &meta.base {
if let Some(base) =
StandardLibrary::from_config_name(&base_name, filename.parent())?
{
library.extend(base);
}
}
}
Ok(Some(library))
}
pub fn find_global(&self, names: &[String]) -> Option<&Field> {
assert!(!names.is_empty());
let mut current = &self.globals;
for name in names.iter().take(names.len() - 1) {
if let Some(child) = current
.get(name)
.or_else(|| current.get("*"))
.map(|field| self.unstruct(field))
{
match child {
Field::Any => {
current = &ANY_TABLE;
}
Field::Table(children) => {
current = children;
}
_ => return None,
};
} else {
return None;
}
}
current
.get(names.last().unwrap())
.or_else(|| current.get("*"))
.map(|field| self.unstruct(field))
}
pub fn unstruct<'a>(&'a self, field: &'a Field) -> &'a Field {
if let Field::Struct(name) = field {
self.structs
.get(name)
.unwrap_or_else(|| panic!("no struct named `{}` exists", name))
} else {
field
}
}
pub fn extend(&mut self, mut other: StandardLibrary) {
fn merge(into: &mut BTreeMap<String, Field>, other: &mut BTreeMap<String, Field>) {
for (k, v) in other {
let (k, mut v) = (k.to_owned(), v.to_owned());
if let Field::Removed = v {
into.remove(&k);
continue;
}
if let Some(conflict) = into.get_mut(&k) {
if let Field::Table(ref mut from_children) = v {
if let Field::Table(into_children) = conflict {
merge(into_children, from_children);
continue;
}
}
}
into.insert(k, v);
}
}
if let Some(other_meta) = &mut other.meta {
if let Some(other_structs) = &mut other_meta.structs {
if self.meta.is_none() {
self.meta = Some(StandardLibraryMeta::default());
}
let meta = self.meta.as_mut().unwrap();
if let Some(structs) = meta.structs.as_mut() {
structs.extend(other_structs.iter().map(|(k, v)| (k.clone(), v.clone())));
} else {
meta.structs = Some(other_structs.clone());
}
}
}
let mut globals = BTreeMap::new();
merge(&mut globals, &mut other.globals);
merge(&mut globals, &mut self.globals);
self.globals = globals;
}
pub fn inflate(&mut self) {
let structs = self
.meta
.as_ref()
.and_then(|meta| meta.structs.as_ref())
.cloned();
for (name, children) in structs.unwrap_or_default() {
self.structs
.insert(name.to_owned(), Field::Table(children.clone()));
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Field {
Any,
Function {
arguments: Vec<Argument>,
method: bool,
},
Property {
writable: Option<Writable>,
},
Struct(String),
Table(BTreeMap<String, Field>),
Removed,
}
impl<'de> Deserialize<'de> for Field {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let field_raw = FieldSerde::deserialize(deserializer)?;
if field_raw.any {
return Ok(Field::Any);
}
if field_raw.removed {
return Ok(Field::Removed);
}
let is_function = field_raw.args.is_some() || field_raw.method;
if !field_raw.property
&& !is_function
&& field_raw.children.is_empty()
&& field_raw.strukt.is_none()
{
return Err(de::Error::custom(
"can't determine what kind of field this is",
));
}
if field_raw.property && is_function {
return Err(de::Error::custom("field is both a property and a function"));
}
if field_raw.property {
return Ok(Field::Property {
writable: field_raw.writable,
});
}
if let Some(name) = field_raw.strukt {
return Ok(Field::Struct(name));
}
if is_function {
return Ok(Field::Function {
arguments: field_raw.args.unwrap_or_else(Vec::new),
method: field_raw.method,
});
}
Ok(Field::Table(field_raw.children))
}
}
impl Serialize for Field {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Field::Any => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("any", &true)?;
map.end()
}
Field::Function { arguments, method } => {
let mut map = serializer.serialize_map(None)?;
if *method {
map.serialize_entry("method", &true)?;
}
map.serialize_entry("args", arguments)?;
map.end()
}
Field::Property { writable } => {
let mut map = serializer.serialize_map(None)?;
map.serialize_entry("property", &true)?;
if let Some(writable) = writable {
map.serialize_entry("writable", writable)?;
}
map.end()
}
Field::Struct(name) => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("struct", name)?;
map.end()
}
Field::Table(table) => {
toml::Value::try_from(table).unwrap().serialize(serializer)
}
Field::Removed => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("removed", &true)?;
map.end()
}
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
pub enum Writable {
NewFields,
Overridden,
Full,
}
#[derive(Debug, Deserialize)]
struct FieldSerde {
#[serde(default)]
property: bool,
#[serde(default)]
method: bool,
#[serde(default)]
removed: bool,
#[serde(default)]
writable: Option<Writable>,
#[serde(default)]
args: Option<Vec<Argument>>,
#[serde(default)]
#[serde(rename = "struct")]
strukt: Option<String>,
#[serde(default)]
any: bool,
#[serde(flatten)]
children: BTreeMap<String, Field>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct Argument {
#[serde(default)]
#[serde(skip_serializing_if = "Required::required_no_message")]
pub required: Required,
#[serde(rename = "type")]
pub argument_type: ArgumentType,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum ArgumentType {
Any,
Bool,
Constant(Vec<String>),
Display(String),
Function,
Nil,
Number,
String,
Table,
Vararg,
}
impl Serialize for ArgumentType {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
&ArgumentType::Any
| &ArgumentType::Bool
| &ArgumentType::Function
| &ArgumentType::Nil
| &ArgumentType::Number
| &ArgumentType::String
| &ArgumentType::Table
| &ArgumentType::Vararg => serializer.serialize_str(&self.to_string()),
ArgumentType::Constant(constants) => {
let mut seq = serializer.serialize_seq(Some(constants.len()))?;
for constant in constants {
seq.serialize_element(constant)?;
}
seq.end()
}
ArgumentType::Display(display) => {
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("display", display)?;
map.end()
}
}
}
}
impl<'de> Deserialize<'de> for ArgumentType {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(ArgumentTypeVisitor)
}
}
struct ArgumentTypeVisitor;
impl<'de> Visitor<'de> for ArgumentTypeVisitor {
type Value = ArgumentType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an argument type or an array of constant strings")
}
fn visit_map<A: de::MapAccess<'de>>(self, mut access: A) -> Result<Self::Value, A::Error> {
let mut map: HashMap<String, String> = HashMap::new();
while let Some((key, value)) = access.next_entry()? {
map.insert(key, value);
}
if let Some(display) = map.remove("display") {
Ok(ArgumentType::Display(display))
} else {
Err(de::Error::custom(
"map value must have a `display` property",
))
}
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let mut constants = Vec::new();
while let Some(value) = seq.next_element()? {
constants.push(value);
}
Ok(ArgumentType::Constant(constants))
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
match value {
"any" => Ok(ArgumentType::Any),
"bool" => Ok(ArgumentType::Bool),
"function" => Ok(ArgumentType::Function),
"nil" => Ok(ArgumentType::Nil),
"number" => Ok(ArgumentType::Number),
"string" => Ok(ArgumentType::String),
"table" => Ok(ArgumentType::Table),
"..." => Ok(ArgumentType::Vararg),
other => Err(de::Error::custom(format!("unknown type {}", other))),
}
}
}
impl fmt::Display for ArgumentType {
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
match self {
ArgumentType::Any => write!(formatter, "any"),
ArgumentType::Bool => write!(formatter, "bool"),
ArgumentType::Constant(options) => write!(
formatter,
"{}",
options
.iter()
.map(|string| format!("\"{}\"", string))
.collect::<Vec<_>>()
.join(", ")
),
ArgumentType::Display(display) => write!(formatter, "{}", display),
ArgumentType::Function => write!(formatter, "function"),
ArgumentType::Nil => write!(formatter, "nil"),
ArgumentType::Number => write!(formatter, "number"),
ArgumentType::String => write!(formatter, "string"),
ArgumentType::Table => write!(formatter, "table"),
ArgumentType::Vararg => write!(formatter, "..."),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Required {
NotRequired,
Required(Option<String>),
}
impl Required {
fn required_no_message(&self) -> bool {
self == &Required::Required(None)
}
}
impl Default for Required {
fn default() -> Self {
Required::Required(None)
}
}
impl<'de> Deserialize<'de> for Required {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_any(RequiredVisitor)
}
}
impl Serialize for Required {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match self {
Required::NotRequired => serializer.serialize_bool(false),
Required::Required(None) => serializer.serialize_bool(true),
Required::Required(Some(message)) => serializer.serialize_str(message),
}
}
}
struct RequiredVisitor;
impl<'de> Visitor<'de> for RequiredVisitor {
type Value = Required;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a boolean or a string message (when required)")
}
fn visit_bool<E: de::Error>(self, value: bool) -> Result<Self::Value, E> {
if value {
Ok(Required::Required(None))
} else {
Ok(Required::NotRequired)
}
}
fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
Ok(Required::Required(Some(value.to_owned())))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_serde() {
StandardLibrary::from_name("lua51").expect("lua51.toml wasn't found");
StandardLibrary::from_name("lua52").expect("lua52.toml wasn't found");
}
}