#![doc(
html_logo_url = "https://varlink.org/images/varlink.png",
html_favicon_url = "https://varlink.org/images/varlink-small.png"
)]
use self::varlink_grammar::ParseInterface;
use std::collections::BTreeMap;
use std::collections::HashSet;
use chainerror::*;
mod format;
pub use crate::format::{Format, FormatColored};
#[cfg(test)]
mod test;
mod varlink_grammar;
derive_str_cherr!(Error);
pub enum VType<'a> {
Bool,
Int,
Float,
String,
Object,
Typename(&'a str),
Struct(Box<VStruct<'a>>),
Enum(Box<VEnum<'a>>),
}
pub enum VTypeExt<'a> {
Array(Box<VTypeExt<'a>>),
Dict(Box<VTypeExt<'a>>),
Option(Box<VTypeExt<'a>>),
Plain(VType<'a>),
}
pub struct Argument<'a> {
pub name: &'a str,
pub vtype: VTypeExt<'a>,
}
pub struct VStruct<'a> {
pub elts: Vec<Argument<'a>>,
}
pub struct VEnum<'a> {
pub elts: Vec<&'a str>,
}
pub struct VError<'a> {
pub name: &'a str,
pub doc: &'a str,
pub parm: VStruct<'a>,
}
pub enum VStructOrEnum<'a> {
VStruct(Box<VStruct<'a>>),
VEnum(Box<VEnum<'a>>),
}
pub struct Typedef<'a> {
pub name: &'a str,
pub doc: &'a str,
pub elt: VStructOrEnum<'a>,
}
pub struct Method<'a> {
pub name: &'a str,
pub doc: &'a str,
pub input: VStruct<'a>,
pub output: VStruct<'a>,
}
enum MethodOrTypedefOrError<'a> {
Error(VError<'a>),
Typedef(Typedef<'a>),
Method(Method<'a>),
}
pub struct IDL<'a> {
pub description: &'a str,
pub name: &'a str,
pub doc: &'a str,
pub methods: BTreeMap<&'a str, Method<'a>>,
pub method_keys: Vec<&'a str>,
pub typedefs: BTreeMap<&'a str, Typedef<'a>>,
pub typedef_keys: Vec<&'a str>,
pub errors: BTreeMap<&'a str, VError<'a>>,
pub error_keys: Vec<&'a str>,
pub error: HashSet<String>,
}
fn trim_doc(s: &str) -> &str {
s.trim_matches(&[
' ', '\n', '\r', '\u{00A0}', '\u{FEFF}', '\u{1680}', '\u{180E}', '\u{2000}', '\u{2001}',
'\u{2002}', '\u{2003}', '\u{2004}', '\u{2005}', '\u{2006}', '\u{2007}', '\u{2008}',
'\u{2009}', '\u{200A}', '\u{202F}', '\u{205F}', '\u{3000}', '\u{2028}', '\u{2029}',
] as &[_])
}
impl<'a> IDL<'a> {
fn from_token(
description: &'a str,
name: &'a str,
mt: Vec<MethodOrTypedefOrError<'a>>,
doc: &'a str,
) -> IDL<'a> {
let mut i = IDL {
description,
name,
doc,
methods: BTreeMap::new(),
method_keys: Vec::new(),
typedefs: BTreeMap::new(),
typedef_keys: Vec::new(),
errors: BTreeMap::new(),
error_keys: Vec::new(),
error: HashSet::new(),
};
for o in mt {
match o {
MethodOrTypedefOrError::Method(m) => {
if i.error_keys.contains(&m.name) || i.typedef_keys.contains(&m.name) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of `{}`!",
i.name, m.name
));
}
i.method_keys.push(m.name);
if let Some(d) = i.methods.insert(m.name, m) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of method `{}`!",
i.name, d.name
));
};
}
MethodOrTypedefOrError::Typedef(t) => {
if i.error_keys.contains(&t.name) || i.method_keys.contains(&t.name) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of `{}`!",
i.name, t.name
));
}
i.typedef_keys.push(t.name);
if let Some(d) = i.typedefs.insert(t.name, t) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of type `{}`!",
i.name, d.name
));
};
}
MethodOrTypedefOrError::Error(e) => {
if i.typedef_keys.contains(&e.name) || i.method_keys.contains(&e.name) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of `{}`!",
i.name, e.name
));
}
i.error_keys.push(e.name);
if let Some(d) = i.errors.insert(e.name, e) {
i.error.insert(format!(
"Interface `{}`: multiple definitions of error `{}`!",
i.name, d.name
));
};
}
};
}
i
}
}
impl<'a> IDL<'a> {
pub fn from_string(s: &'a str) -> ChainResult<Self, Error> {
let interface = ParseInterface(s).map_err(|e| {
let line = s.split('\n').nth(e.location.line - 1).unwrap();
cherr!(
e,
Error(format!(
"Varlink parse error\n{}\n{marker:>col$}",
line,
marker = "^",
col = e.location.column
))
)
})?;
if !interface.error.is_empty() {
let mut v: Vec<_> = interface.error.into_iter().collect();
v.sort();
let mut s = String::new();
let mut first: bool = true;
for i in v.iter() {
if !first {
s += "\n";
}
first = false;
s += &i.to_string();
}
Err(strerr!(Error, "Interface definition error: '{}'\n", s))
} else {
Ok(interface)
}
}
}