#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc)]
use std::fmt::Display;
use std::fmt::Write;
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_writer(&self, mut w: impl Write) -> std::fmt::Result {
write!(
w,
"{}{}: {}",
self.name,
if self.optional { "*" } else { "" },
self.type_name
)
}
#[must_use]
pub fn to_aaml(&self) -> String {
let mut s = String::new();
self.to_aaml_writer(&mut s).unwrap();
s
}
}
#[cfg(feature = "builder-extras")]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum BuiltInType {
I32,
F64,
String,
Bool,
Color,
Vector2,
Vector3,
Vector4,
Quaternion,
Matrix3x3,
Matrix4x4,
DateTime,
Duration,
Year,
Day,
Hour,
Minute,
Kilogram,
Meter,
Schema,
Custom(String),
List(Box<Self>),
}
#[cfg(feature = "builder-extras")]
impl std::fmt::Display for BuiltInType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::I32 => write!(f, "i32"),
Self::F64 => write!(f, "f64"),
Self::String => write!(f, "string"),
Self::Bool => write!(f, "bool"),
Self::Color => write!(f, "color"),
Self::Vector2 => write!(f, "math::vector2"),
Self::Vector3 => write!(f, "math::vector3"),
Self::Vector4 => write!(f, "math::vector4"),
Self::Quaternion => write!(f, "math::quaternion"),
Self::Matrix3x3 => write!(f, "math::matrix3x3"),
Self::Matrix4x4 => write!(f, "math::matrix4x4"),
Self::DateTime => write!(f, "time::datetime"),
Self::Duration => write!(f, "time::duration"),
Self::Year => write!(f, "time::year"),
Self::Day => write!(f, "time::day"),
Self::Hour => write!(f, "time::hour"),
Self::Minute => write!(f, "time::minute"),
Self::Kilogram => write!(f, "physics::kilogram"),
Self::Meter => write!(f, "physics::meter"),
Self::Schema => write!(f, "schema"),
Self::Custom(s) => write!(f, "{s}"),
Self::List(inner) => write!(f, "list<{inner}>"),
}
}
}
#[cfg(feature = "builder-extras")]
impl From<&str> for BuiltInType {
fn from(s: &str) -> Self {
match s {
"i32" => Self::I32,
"f64" => Self::F64,
"string" => Self::String,
"bool" => Self::Bool,
"color" => Self::Color,
"math::vector2" => Self::Vector2,
"math::vector3" => Self::Vector3,
"math::vector4" => Self::Vector4,
"math::quaternion" => Self::Quaternion,
"math::matrix3x3" => Self::Matrix3x3,
"math::matrix4x4" => Self::Matrix4x4,
"time::datetime" => Self::DateTime,
"time::duration" => Self::Duration,
"time::year" => Self::Year,
"time::day" => Self::Day,
"time::hour" => Self::Hour,
"time::minute" => Self::Minute,
"physics::kilogram" => Self::Kilogram,
"physics::meter" => Self::Meter,
"schema" => Self::Schema,
_ => s
.strip_prefix("list<")
.and_then(|s| s.strip_suffix('>'))
.map_or_else(
|| Self::Custom(s.to_string()),
|inner| Self::List(Box::new(Self::from(inner))),
),
}
}
}
#[cfg(feature = "builder-extras")]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct InlineObject {
fields: Vec<(String, String)>,
}
#[cfg(feature = "builder-extras")]
impl InlineObject {
#[must_use]
pub const fn new() -> Self {
Self { fields: Vec::new() }
}
#[must_use]
pub fn with_field(mut self, key: &str, value: &str) -> Self {
self.fields.push((key.to_string(), value.to_string()));
self
}
pub fn add_field(&mut self, key: &str, value: &str) -> &mut Self {
self.fields.push((key.to_string(), value.to_string()));
self
}
#[must_use]
pub fn fields(&self) -> &[(String, String)] {
&self.fields
}
#[must_use]
pub fn to_aaml(&self) -> String {
self.to_string()
}
}
#[cfg(feature = "builder-extras")]
impl Default for InlineObject {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "builder-extras")]
impl std::fmt::Display for InlineObject {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{ ")?;
for (i, (k, v)) in self.fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{k} = {v}")?;
}
write!(f, " }}")
}
}
#[cfg(feature = "builder-extras")]
impl From<Vec<(String, String)>> for InlineObject {
fn from(fields: Vec<(String, String)>) -> Self {
Self { fields }
}
}
#[cfg(feature = "builder-extras")]
impl From<InlineObject> for String {
fn from(obj: InlineObject) -> Self {
obj.to_string()
}
}
#[cfg(feature = "builder-extras")]
pub fn parse_inline_to_map(
s: &str,
) -> Result<std::collections::HashMap<String, String>, crate::error::AamlError> {
let pairs = crate::aaml::parsing::parse_inline_object(s)?;
Ok(pairs.into_iter().collect())
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct AAMBuilder {
buffer: String,
}
impl AAMBuilder {
#[must_use]
pub const fn new() -> Self {
Self {
buffer: String::new(),
}
}
#[must_use]
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 {
self.push_sep();
self.buffer.push_str("@schema ");
self.buffer.push_str(name);
self.buffer.push_str(" { ");
let mut first = true;
for field in fields {
if !first {
self.buffer.push_str(", ");
}
field.to_aaml_writer(&mut self.buffer).unwrap();
first = false;
}
self.buffer.push_str(" }");
self
}
pub fn schema_multiline(
&mut self,
name: &str,
fields: impl IntoIterator<Item = SchemaField>,
) -> &mut Self {
self.push_sep();
write!(&mut self.buffer, "@schema {name} {{").unwrap();
for field in fields {
write!(&mut self.buffer, "\n ").unwrap();
field.to_aaml_writer(&mut self.buffer).unwrap();
}
self.buffer.push_str("\n}");
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
}
#[cfg(feature = "builder-extras")]
pub fn type_alias_builtin(&mut self, alias: &str, builtin: &BuiltInType) -> &mut Self {
self.type_alias(alias, &builtin.to_string())
}
#[cfg(feature = "builder-extras")]
pub fn add_inline(&mut self, key: &str, obj: &InlineObject) -> &mut Self {
self.add_line(key, &obj.to_string())
}
#[cfg(feature = "builder-extras")]
pub fn add_inline_str(&mut self, key: &str, inline_str: &str) -> &mut Self {
self.add_line(key, inline_str)
}
#[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())
}
#[must_use]
pub fn build(self) -> String {
self.buffer
}
#[must_use]
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)
}
}