#![recursion_limit = "1024"]
extern crate anyhow;
extern crate serde;
extern crate tera;
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use anyhow::Result;
use serde::ser::SerializeStruct;
use tera::Tera;
type Map<K, V> = BTreeMap<K, V>;
mod channel;
mod connection;
mod server;
#[derive(Default, Clone)]
struct Property<'a> {
name: Cow<'a, str>,
type_s: Cow<'a, str>,
result: bool,
documentation: Cow<'a, str>,
initialise: bool,
initialisation: Option<Cow<'a, str>>,
update: Option<Cow<'a, str>>,
should_update: bool,
method_name: Option<Cow<'a, str>>,
enum_name: Cow<'a, str>,
value_name: Option<Cow<'a, str>>,
functions: Map<Cow<'a, str>, Cow<'a, str>>,
transmutable: Vec<Cow<'a, str>>,
default_args: Cow<'a, str>,
default_args_update: Cow<'a, str>,
api_getter: bool,
public: bool,
requested: bool,
}
impl<'a> Property<'a> {
fn is_ref_type(&self) -> bool {
["String", "Permissions"].contains(&self.type_s.as_ref())
|| self.type_s.starts_with("Option")
|| self.type_s.starts_with("Map<")
|| self.type_s.starts_with("Vec<")
}
fn create_return_type(&self) -> String {
let is_ref_type = self.is_ref_type();
let mut result_type = String::new();
if self.result {
result_type.push_str("Result<")
}
if is_ref_type {
result_type.push('&');
}
if self.type_s == "String" {
result_type.push_str("str");
} else {
result_type.push_str(self.type_s.as_ref());
}
if self.result {
result_type.push_str(", Error>");
}
result_type
}
fn create_ref_type(&self) -> String {
let is_ref_type = self.is_ref_type();
let mut result_type = String::new();
if is_ref_type {
result_type.push_str("&'a ");
}
if self.type_s == "String" {
result_type.push_str("str");
} else {
result_type.push_str(self.type_s.as_ref());
}
result_type
}
fn create_getter_body(&self) -> String {
let is_ref_type = self.is_ref_type();
let mut body = String::new();
if !self.result && is_ref_type {
body.push('&');
}
body.push_str(format!("self.{}", self.name).as_str());
if self.result && is_ref_type {
body.push_str(".as_ref()");
if self.type_s == "String" {
body.push_str(".map(|v| v.as_str())");
}
body.push_str(".map_err(|e| *e)");
}
body
}
fn create_constructor_body(&self) -> String {
let p = self.create_initialisation();
if p.is_empty() { self.name.clone().into_owned() } else { p }
}
fn create_update_body(&self) -> String {
self.intern_create_initialisation(self.default_args_update.as_ref(), true)
}
fn create_initialisation(&self) -> String {
if self.result {
String::from("Err(Error::Ok)")
} else {
self.intern_create_initialisation(self.default_args.as_ref(), false)
}
}
fn intern_create_initialisation(&self, default_args: &str, update: bool) -> String {
if !self.initialise || (update && !self.should_update) {
return String::new();
} else if update && self.update.is_some() {
return self.update.as_ref().unwrap().clone().into_owned();
} else if self.initialisation.is_some() {
return self.initialisation.as_ref().unwrap().clone().into_owned();
}
let value_name = self
.value_name
.as_ref()
.map(|s| s.clone())
.unwrap_or(to_pascal_case(self.name.as_ref()).into());
let mut s = String::new();
if let Some(function) = self.method_name.as_ref() {
s.push_str(
format!("{}({}{}::{})", function, default_args, self.enum_name, value_name)
.as_str(),
);
} else if let Some(function) = self.functions.get(self.type_s.as_ref()) {
s.push_str(
format!("{}({}{}::{})", function, default_args, self.enum_name, value_name)
.as_str(),
);
} else if self.transmutable.contains(&self.type_s) {
for t in &["i32", "u64"] {
if let Some(function) = self.functions.get(*t) {
s.push_str(
format!(
"{}({}{}::{}).map(|v| unsafe {{ transmute(v) }})",
function, default_args, self.enum_name, value_name
)
.as_str(),
);
break;
}
}
} else {
match self.type_s.as_ref() {
"Duration" => {
let function: &str = if let Some(f) = self.functions.get("u64") {
f
} else if let Some(f) = self.functions.get("i32") {
f
} else {
"get_property_as_int"
};
s.push_str(
format!(
"{}({}{}::{}).map(|d| Duration::seconds(d as i64))",
function, default_args, self.enum_name, value_name
)
.as_str(),
)
}
"DateTime<Utc>" => {
let function: &str = if let Some(f) = self.functions.get("u64") {
f
} else if let Some(f) = self.functions.get("i32") {
f
} else {
"get_property_as_int"
};
s.push_str(
format!(
"{}({}{}::{}).map(|d| DateTime::from_timestamp(d as i64, 0).unwrap())",
function, default_args, self.enum_name, value_name
)
.as_str(),
)
}
"bool" => {
for t in &["i32", "u64"] {
if let Some(function) = self.functions.get(*t) {
s.push_str(
format!(
"{}({}{}::{}).map(|v| v != 0)",
function, default_args, self.enum_name, value_name
)
.as_str(),
);
break;
}
}
}
_ => {}
}
}
s
}
}
impl<'a> serde::Serialize for Property<'a> {
fn serialize<S: serde::Serializer>(
&self, serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
let mut s = serializer.serialize_struct("Property", 22)?;
s.serialize_field("name", &self.name)?;
s.serialize_field("type_s", &self.type_s)?;
s.serialize_field("result", &self.result)?;
let documentation =
self.documentation.lines().map(|l| format!("/// {}\n", l)).collect::<String>();
s.serialize_field("documentation", &documentation)?;
s.serialize_field("initialise", &self.initialise)?;
s.serialize_field("initialisation", &self.initialisation)?;
s.serialize_field("update", &self.update)?;
s.serialize_field("should_update", &self.should_update)?;
s.serialize_field("method_name", &self.method_name)?;
s.serialize_field("enum_name", &self.enum_name)?;
s.serialize_field("value_name", &self.value_name)?;
s.serialize_field("functions", &self.functions)?;
s.serialize_field("transmutable", &self.transmutable)?;
s.serialize_field("default_args", &self.default_args)?;
s.serialize_field("default_args_update", &self.default_args_update)?;
s.serialize_field("api_getter", &self.api_getter)?;
s.serialize_field("public", &self.public)?;
s.serialize_field("requested", &self.requested)?;
s.serialize_field("return_type", &self.create_return_type())?;
s.serialize_field("getter_body", &self.create_getter_body())?;
s.serialize_field("constructor_body", &self.create_constructor_body())?;
s.serialize_field("update_body", &self.create_update_body())?;
s.end()
}
}
#[derive(Default, Clone)]
struct PropertyBuilder<'a> {
name: Cow<'a, str>,
type_s: Cow<'a, str>,
result: bool,
documentation: Cow<'a, str>,
initialise: bool,
initialisation: Option<Cow<'a, str>>,
update: Option<Cow<'a, str>>,
should_update: bool,
method_name: Option<Cow<'a, str>>,
enum_name: Cow<'a, str>,
value_name: Option<Cow<'a, str>>,
functions: Map<Cow<'a, str>, Cow<'a, str>>,
transmutable: Vec<Cow<'a, str>>,
default_args: Cow<'a, str>,
default_args_update: Cow<'a, str>,
api_getter: bool,
public: bool,
requested: bool,
}
#[allow(dead_code)]
impl<'a> PropertyBuilder<'a> {
fn new() -> PropertyBuilder<'a> {
let mut result = Self::default();
result.initialise = true;
result.result = true;
result.should_update = true;
result.api_getter = true;
result
}
fn name<S: Into<Cow<'a, str>>>(&self, name: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.name = name.into();
res
}
fn type_s<S: Into<Cow<'a, str>>>(&self, type_s: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.type_s = type_s.into();
res
}
fn result(&self, result: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.result = result;
res
}
fn documentation<S: Into<Cow<'a, str>>>(&self, documentation: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.documentation = documentation.into();
res
}
fn initialise(&self, initialise: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.initialise = initialise;
res
}
fn initialisation<S: Into<Cow<'a, str>>>(&self, initialisation: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.initialisation = Some(initialisation.into());
res
}
fn update<S: Into<Cow<'a, str>>>(&self, update: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.update = Some(update.into());
res
}
fn should_update(&self, should_update: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.should_update = should_update.into();
res
}
fn method_name<S: Into<Cow<'a, str>>>(&self, method_name: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.method_name = Some(method_name.into());
res
}
fn enum_name<S: Into<Cow<'a, str>>>(&self, enum_name: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.enum_name = enum_name.into();
res
}
fn value_name<S: Into<Cow<'a, str>>>(&self, value_name: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.value_name = Some(value_name.into());
res
}
fn functions<S1: Into<Cow<'a, str>>, S2: Into<Cow<'a, str>>>(
&self, functions: Map<S1, S2>,
) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.functions = functions.into_iter().map(|(k, v)| (k.into(), v.into())).collect();
res
}
fn transmutable<S: Into<Cow<'a, str>>>(&self, transmutable: Vec<S>) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.transmutable = transmutable.into_iter().map(|s| s.into()).collect();
res
}
fn default_args<S: Into<Cow<'a, str>>>(&self, default_args: S) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.default_args = default_args.into();
res
}
fn default_args_update<S: Into<Cow<'a, str>>>(
&self, default_args_update: S,
) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.default_args_update = default_args_update.into();
res
}
fn api_getter(&self, api_getter: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.api_getter = api_getter;
res
}
fn public(&self, public: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.public = public;
res
}
fn requested(&self, requested: bool) -> PropertyBuilder<'a> {
let mut res = self.clone();
res.requested = requested;
res
}
fn finalize(self) -> Property<'a> {
Property {
name: self.name,
type_s: self.type_s,
result: self.result,
documentation: self.documentation,
initialise: self.initialise,
initialisation: self.initialisation,
update: self.update,
should_update: self.should_update,
method_name: self.method_name,
enum_name: self.enum_name,
value_name: self.value_name,
functions: self.functions.clone(),
transmutable: self.transmutable.clone(),
default_args: self.default_args,
default_args_update: self.default_args_update,
api_getter: self.api_getter,
public: self.public,
requested: self.requested,
}
}
}
struct Struct<'a> {
name: Cow<'a, str>,
api_name: Cow<'a, str>,
documentation: Cow<'a, str>,
properties: Vec<Property<'a>>,
extra_attributes: Cow<'a, str>,
extra_initialisation: Cow<'a, str>,
extra_creation: Cow<'a, str>,
extra_implementation: Cow<'a, str>,
extra_property_type: Cow<'a, str>,
extra_property_list: Vec<(Cow<'a, str>, Cow<'a, str>, Cow<'a, str>)>,
extra_properties: Cow<'a, str>,
constructor_args: Cow<'a, str>,
public: bool,
do_struct: bool,
do_impl: bool,
do_api_impl: bool,
do_update: bool,
do_constructor: bool,
do_properties: bool,
}
#[derive(Default, Clone)]
struct StructBuilder<'a> {
name: Cow<'a, str>,
api_name: Cow<'a, str>,
documentation: Cow<'a, str>,
properties: Vec<Property<'a>>,
extra_attributes: Cow<'a, str>,
extra_initialisation: Cow<'a, str>,
extra_creation: Cow<'a, str>,
extra_implementation: Cow<'a, str>,
extra_property_type: Cow<'a, str>,
extra_property_list: Vec<(Cow<'a, str>, Cow<'a, str>, Cow<'a, str>)>,
extra_properties: Cow<'a, str>,
constructor_args: Cow<'a, str>,
public: bool,
do_struct: bool,
do_impl: bool,
do_api_impl: bool,
do_update: bool,
do_constructor: bool,
do_properties: bool,
}
#[allow(dead_code)]
impl<'a> StructBuilder<'a> {
fn new() -> StructBuilder<'a> {
let mut result = Self::default();
result.do_struct = true;
result.do_impl = true;
result.do_constructor = true;
result.do_update = true;
result.do_api_impl = false;
result
}
fn name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> StructBuilder<'a> {
let mut res = self.clone();
res.name = name.into();
res
}
fn api_name<S: Into<Cow<'a, str>>>(&mut self, api_name: S) -> StructBuilder<'a> {
let mut res = self.clone();
res.api_name = api_name.into();
res
}
fn documentation<S: Into<Cow<'a, str>>>(&mut self, documentation: S) -> StructBuilder<'a> {
let mut res = self.clone();
res.documentation = documentation.into();
res
}
fn properties(&mut self, properties: Vec<Property<'a>>) -> StructBuilder<'a> {
let mut res = self.clone();
res.properties = properties;
res
}
fn extra_attributes<S: Into<Cow<'a, str>>>(
&mut self, extra_attributes: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_attributes = extra_attributes.into();
res
}
fn extra_initialisation<S: Into<Cow<'a, str>>>(
&mut self, extra_initialisation: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_initialisation = extra_initialisation.into();
res
}
fn extra_property_type<S: Into<Cow<'a, str>>>(
&mut self, extra_property_type: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_property_type = extra_property_type.into();
res
}
fn extra_property_list(
&mut self, extra_property_list: Vec<(Cow<'a, str>, Cow<'a, str>, Cow<'a, str>)>,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_property_list = extra_property_list;
res
}
fn extra_properties<S: Into<Cow<'a, str>>>(
&mut self, extra_properties: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_properties = extra_properties.into();
res
}
fn extra_creation<S: Into<Cow<'a, str>>>(&mut self, extra_creation: S) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_creation = extra_creation.into();
res
}
fn extra_implementation<S: Into<Cow<'a, str>>>(
&mut self, extra_implementation: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.extra_implementation = extra_implementation.into();
res
}
fn constructor_args<S: Into<Cow<'a, str>>>(
&mut self, constructor_args: S,
) -> StructBuilder<'a> {
let mut res = self.clone();
res.constructor_args = constructor_args.into();
res
}
fn public(&mut self, public: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.public = public;
res
}
fn do_struct(&mut self, do_struct: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_struct = do_struct;
res
}
fn do_impl(&mut self, do_impl: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_impl = do_impl;
res
}
fn do_api_impl(&mut self, do_api_impl: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_api_impl = do_api_impl;
res
}
fn do_update(&mut self, do_update: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_update = do_update;
res
}
fn do_constructor(&mut self, do_constructor: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_constructor = do_constructor;
res
}
fn do_properties(&mut self, do_properties: bool) -> StructBuilder<'a> {
let mut res = self.clone();
res.do_properties = do_properties;
res
}
fn finalize(self) -> Struct<'a> {
Struct {
name: self.name,
api_name: self.api_name,
documentation: self.documentation,
properties: self.properties.clone(),
extra_attributes: self.extra_attributes,
extra_initialisation: self.extra_initialisation,
extra_creation: self.extra_creation,
extra_implementation: self.extra_implementation,
extra_property_type: self.extra_property_type,
extra_property_list: self.extra_property_list,
extra_properties: self.extra_properties,
constructor_args: self.constructor_args,
public: self.public,
do_struct: self.do_struct,
do_impl: self.do_impl,
do_api_impl: self.do_api_impl,
do_update: self.do_update,
do_constructor: self.do_constructor,
do_properties: self.do_properties,
}
}
}
impl<'a> Struct<'a> {
fn create_struct(
&self, f: &mut dyn Write, tera: &Tera, all_structs: &[&Struct<'static>],
) -> Result<()> {
let mut context = tera::Context::new();
context.insert("s", &self);
context.insert("all_structs", &all_structs);
let mut properties = Vec::<&Property<'static>>::new();
for s in all_structs.iter().filter(|s| s.api_name == self.api_name) {
for p in &s.properties {
if properties.iter().all(|p2| p.name != p2.name) {
properties.push(p);
}
}
}
context.insert("properties", &properties);
let mut property_types = Vec::<(_, _)>::new();
for s in all_structs.iter().filter(|s| s.api_name == self.api_name) {
for p in &s.properties {
let t = p.type_s.to_string();
if p.result && p.api_getter && property_types.iter().all(|p| p.0 != t) {
property_types.push((t, p.create_ref_type()));
}
}
for &(ref t, ref r, _) in &s.extra_property_list {
let t = t.as_ref();
let r = r.as_ref();
if property_types.iter().all(|p| p.0 != t) {
property_types.push((r.to_string(), t.to_string()));
}
}
}
context.insert("property_types", &property_types);
let s = tera.render("struct.rs.tera", &context)?;
f.write_all(s.as_bytes())?;
Ok(())
}
}
impl<'a> serde::Serialize for Struct<'a> {
fn serialize<S: serde::Serializer>(
&self, serializer: S,
) -> std::result::Result<S::Ok, S::Error> {
let mut s = serializer.serialize_struct("Struct", 19)?;
s.serialize_field("name", &self.name)?;
s.serialize_field("api_name", &self.api_name)?;
let documentation =
self.documentation.lines().map(|l| format!("/// {}\n", l)).collect::<String>();
s.serialize_field("documentation", &documentation)?;
s.serialize_field("properties", &self.properties)?;
s.serialize_field("extra_attributes", &self.extra_attributes)?;
s.serialize_field("extra_initialisation", &self.extra_initialisation)?;
s.serialize_field("extra_creation", &self.extra_creation)?;
s.serialize_field("extra_implementation", &self.extra_implementation)?;
s.serialize_field("extra_property_type", &self.extra_property_type)?;
s.serialize_field("extra_property_list", &self.extra_property_list)?;
s.serialize_field("extra_properties", &self.extra_properties)?;
s.serialize_field("constructor_args", &self.constructor_args)?;
s.serialize_field("public", &self.public)?;
s.serialize_field("do_struct", &self.do_struct)?;
s.serialize_field("do_impl", &self.do_impl)?;
s.serialize_field("do_api_impl", &self.do_api_impl)?;
s.serialize_field("do_update", &self.do_update)?;
s.serialize_field("do_constructor", &self.do_constructor)?;
s.serialize_field("do_properties", &self.do_properties)?;
s.end()
}
}
fn main() -> Result<()> {
let out_dir = env::var("OUT_DIR")?;
for f in
&["build.rs", "channel.rs", "connection.rs", "server.rs", "struct.rs.tera", "macros.tera"]
{
println!("cargo:rerun-if-changed=build/{}", f);
}
let mut tera = Tera::new("build/*.tera")?;
tera.register_filter("indent", |value: &tera::Value, args: &HashMap<String, tera::Value>| {
if let Some(&tera::Value::Number(ref n)) = args.get("depth") {
if let tera::Value::String(s) = value {
if let Some(n) = n.as_u64() {
Ok(tera::Value::String(indent(s, n as usize)))
} else {
Err("the indent depth must be a number".into())
}
} else {
Err("indent expects a string to filter".into())
}
} else {
Err("Expected argument 'depth' for indent".into())
}
});
tera.register_filter("title", |value: &tera::Value, _: &_| {
if let tera::Value::String(s) = value {
Ok(tera::Value::String(to_pascal_case(s)))
} else {
Err("title expects a string to filter".into())
}
});
tera.register_filter("simplify", |value: &tera::Value, _: &_| {
if let tera::Value::String(s) = value {
Ok(tera::Value::String(s.replace(&['<', '>', ',', ' '] as &[char], "")))
} else {
Err("title expects a string to filter".into())
}
});
let path = Path::new(&out_dir);
let channel_f = File::create(&path.join("channel.rs"))?;
let connection_f = File::create(&path.join("connection.rs"))?;
let server_f = File::create(&path.join("server.rs"))?;
let mut files = vec![channel_f, connection_f, server_f];
let structs = vec![channel::create(), connection::create(), server::create()];
let mut all_structs = Vec::new();
for s in &structs {
all_structs.extend(s);
}
for (i, ss) in structs.iter().enumerate() {
let f = &mut files[i];
for s in ss {
s.create_struct(f, &tera, all_structs.as_slice())?;
}
}
Ok(())
}
fn to_pascal_case<S: AsRef<str>>(text: S) -> String {
let sref = text.as_ref();
let mut s = String::with_capacity(sref.len());
let mut uppercase = true;
for c in sref.chars() {
if c == '_' {
uppercase = true;
} else {
if uppercase {
s.push(c.to_uppercase().next().unwrap());
uppercase = false;
} else {
s.push(c);
}
}
}
s
}
fn indent<S: AsRef<str>>(s: S, count: usize) -> String {
let sref = s.as_ref();
let line_count = sref.lines().count();
let mut result = String::with_capacity(sref.len() + line_count * count * 4);
for l in sref.lines() {
if !l.is_empty() {
result.push_str(std::iter::repeat("\t").take(count).collect::<String>().as_str());
}
result.push_str(l);
result.push('\n');
}
result
}