use std::fmt::Display;
use std::io;
use std::ops::Deref;
use std::path::Path;
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct SchemaField {
name: String,
type_name: String,
optional: bool,
}
impl SchemaField {
pub fn required(name: impl Into<String>, type_name: impl Into<String>) -> Self {
Self {
name: name.into(),
type_name: type_name.into(),
optional: false,
}
}
pub fn optional(name: impl Into<String>, type_name: impl Into<String>) -> Self {
Self {
name: name.into(),
type_name: type_name.into(),
optional: true,
}
}
pub fn to_aaml(&self) -> String {
if self.optional {
format!("{}*: {}", self.name, self.type_name)
} else {
format!("{}: {}", self.name, self.type_name)
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AAMBuilder {
buffer: String,
}
impl AAMBuilder {
pub fn new() -> Self {
Self {
buffer: String::new(),
}
}
pub fn with_capacity(capacity: usize) -> Self {
Self {
buffer: String::with_capacity(capacity),
}
}
fn push_sep(&mut self) {
if !self.buffer.is_empty() {
self.buffer.push('\n');
}
}
pub fn add_line(&mut self, key: &str, value: &str) -> &mut Self {
self.push_sep();
self.buffer.push_str(key);
self.buffer.push_str(" = ");
self.buffer.push_str(value);
self
}
pub fn comment(&mut self, text: &str) -> &mut Self {
self.push_sep();
self.buffer.push_str("# ");
self.buffer.push_str(text);
self
}
pub fn schema(
&mut self,
name: &str,
fields: impl IntoIterator<Item = SchemaField>,
) -> &mut Self {
let fields_str: Vec<String> = fields.into_iter().map(|f| f.to_aaml()).collect();
self.push_sep();
self.buffer.push_str("@schema ");
self.buffer.push_str(name);
self.buffer.push_str(" { ");
self.buffer.push_str(&fields_str.join(", "));
self.buffer.push_str(" }");
self
}
pub fn schema_multiline(
&mut self,
name: &str,
fields: impl IntoIterator<Item = SchemaField>,
) -> &mut Self {
self.push_sep();
self.buffer.push_str("@schema ");
self.buffer.push_str(name);
self.buffer.push_str(" {");
for field in fields {
self.buffer.push('\n');
self.buffer.push_str(" ");
self.buffer.push_str(&field.to_aaml());
}
self.buffer.push('\n');
self.buffer.push('}');
self
}
pub fn derive(
&mut self,
path: &str,
schemas: impl IntoIterator<Item = impl AsRef<str>>,
) -> &mut Self {
self.push_sep();
self.buffer.push_str("@derive ");
self.buffer.push_str(path);
for schema in schemas {
self.buffer.push_str("::");
self.buffer.push_str(schema.as_ref());
}
self
}
pub fn import(&mut self, path: &str) -> &mut Self {
self.push_sep();
self.buffer.push_str("@import ");
self.buffer.push_str(path);
self
}
pub fn type_alias(&mut self, alias: &str, type_name: &str) -> &mut Self {
self.push_sep();
self.buffer.push_str("@type ");
self.buffer.push_str(alias);
self.buffer.push_str(" = ");
self.buffer.push_str(type_name);
self
}
#[deprecated(
since = "1.1.0",
note = "Prefer the typed directive methods (schema, derive, import, type_alias) over this method when possible."
)]
pub fn add_raw(&mut self, raw_line: &str) -> &mut Self {
self.push_sep();
self.buffer.push_str(raw_line);
self
}
pub fn to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
std::fs::write(path, self.buffer.as_bytes())
}
pub fn build(self) -> String {
self.buffer
}
pub fn as_string(&self) -> String {
self.buffer.clone()
}
}
impl Deref for AAMBuilder {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.buffer
}
}
impl Default for AAMBuilder {
fn default() -> Self {
Self::new()
}
}
impl Display for AAMBuilder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.buffer)
}
}