use std::fmt;
use semver::Version;
use crate::{ident::Ident, Interface, Render, RenderOpts, World};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub struct Package {
name: PackageName,
items: Vec<PackageItem>,
}
impl Package {
pub fn new(name: PackageName) -> Self {
Self {
name,
items: vec![],
}
}
pub fn name(&self) -> &PackageName {
&self.name
}
pub fn set_name(&mut self, name: impl Into<PackageName>) {
self.name = name.into();
}
pub fn interface(&mut self, interface: Interface) {
self.items.push(PackageItem::Interface(interface))
}
pub fn world(&mut self, world: World) {
self.items.push(PackageItem::World(world))
}
pub fn item(&mut self, item: impl Into<PackageItem>) {
self.items.push(item.into());
}
pub fn items(&self) -> &[PackageItem] {
&self.items
}
pub fn items_mut(&mut self) -> &mut Vec<PackageItem> {
&mut self.items
}
}
impl Render for Package {
fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
write!(f, "{}package {};\n", opts.spaces(), self.name)?;
for item in &self.items {
write!(f, "\n")?;
match item {
PackageItem::Interface(interface) => {
if let Some(docs) = &interface.docs {
docs.render(f, opts)?;
}
write!(f, "{}interface {} {{", opts.spaces(), interface.name)?;
if !interface.uses.is_empty() || !interface.items.is_empty() {
write!(f, "\n")?;
interface.uses.render(f, &opts.indent())?;
interface.items.render(f, &opts.indent())?;
write!(f, "{}}}\n", opts.spaces())?;
} else {
write!(f, "}}\n")?;
}
}
PackageItem::World(world) => {
world.render(f, opts)?;
}
}
}
Ok(())
}
}
impl fmt::Display for Package {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.render(f, &RenderOpts::default())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub enum PackageItem {
Interface(Interface),
World(World),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
pub struct PackageName {
namespace: String,
name: Ident,
version: Option<Version>,
}
impl PackageName {
pub fn new(
namespace: impl Into<String>,
name: impl Into<Ident>,
version: Option<Version>,
) -> Self {
Self {
namespace: namespace.into(),
name: name.into(),
version,
}
}
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn set_namespace(&mut self, namespace: impl Into<String>) {
self.namespace = namespace.into();
}
pub fn name(&self) -> &Ident {
&self.name
}
pub fn set_name(&mut self, name: impl Into<Ident>) {
self.name = name.into()
}
pub fn version(&self) -> Option<&Version> {
self.version.as_ref()
}
pub fn set_version(&mut self, version: Option<impl Into<Version>>) {
self.version = version.map(|v| v.into())
}
}
impl From<PackageName> for String {
fn from(name: PackageName) -> String {
name.to_string()
}
}
impl fmt::Display for PackageName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.namespace, self.name)?;
if let Some(version) = &self.version {
write!(f, "@{version}")?;
}
Ok(())
}
}