From c4dc24fdfc1cb98d60dfc4f20db79b3f465fe96a Mon Sep 17 00:00:00 2001 From: Anders Eie Date: Sat, 23 Apr 2022 10:01:04 +0200 Subject: [PATCH] Added support for associated constants on traits and impls --- src/associated_constant.rs | 42 +++++++++++++++++++++++++++++++++ src/impl.rs | 30 ++++++++++++++++++++++++ src/lib.rs | 2 ++ src/trait.rs | 29 +++++++++++++++++++++++ tests/codegen.rs | 48 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 151 insertions(+) create mode 100644 src/associated_constant.rs diff --git a/src/associated_constant.rs b/src/associated_constant.rs new file mode 100644 index 0000000..764578e --- /dev/null +++ b/src/associated_constant.rs @@ -0,0 +1,42 @@ +use std::fmt::{self, Write}; +use crate::formatter::Formatter; + +/// Defines an associated constant for use in impls and traits +#[derive(Clone, Debug)] +pub struct AssociatedConstant { + name: String, + datatype: crate::r#type::Type, + value: Option, +} + +impl AssociatedConstant { + /// Returns an associated constant with the given name and datatype + pub fn new(name: &str, datatype: Datatype) -> Self + where + Datatype: Into + { + Self { + name: name.into(), + datatype: datatype.into(), + value: None, + } + } + + /// Adds a value expression to the associated constant + pub fn value(&mut self, expression: &str) -> &mut Self { + self.value = Some(expression.to_string()); + self + } + + /// Formats the scope using the given formatter. + pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { + let value_expression = match &self.value { + Some(expression) => format!(" = {};", expression), + None => ";".to_string(), + }; + write!(fmt, "const {}: ", self.name)?; + self.datatype.fmt(fmt)?; + write!(fmt, "{}", value_expression)?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/impl.rs b/src/impl.rs index 6e411b5..31b8e40 100644 --- a/src/impl.rs +++ b/src/impl.rs @@ -4,6 +4,7 @@ use crate::bound::Bound; use crate::field::Field; use crate::formatter::{fmt_bounds, fmt_generics, Formatter}; use crate::function::Function; +use crate::associated_constant::AssociatedConstant; use crate::r#type::Type; @@ -19,6 +20,9 @@ pub struct Impl { /// If implementing a trait impl_trait: Option, + /// Associated constants + associated_constants: Vec, + /// Associated types assoc_tys: Vec, @@ -40,6 +44,7 @@ impl Impl { target: target.into(), generics: vec![], impl_trait: None, + associated_constants: vec![], assoc_tys: vec![], bounds: vec![], fns: vec![], @@ -73,6 +78,24 @@ impl Impl { self } + /// Push an associated constant to the impl block + pub fn push_associated_constant(&mut self, constant: AssociatedConstant) -> &mut Self + { + self.associated_constants.push(constant); + self + } + + /// Create an associated constant for the impl block + pub fn new_associated_constant(&mut self, name: &str, datatype: Type) -> &mut AssociatedConstant + where + Type: Into + { + self.push_associated_constant( + AssociatedConstant::new(name, datatype)); + + self.associated_constants.last_mut().unwrap() + } + /// Add a macro to the impl block (e.g. `"#[async_trait]"`) pub fn r#macro(&mut self, r#macro: &str) -> &mut Self { self.macros.push(r#macro.to_string()); @@ -147,6 +170,13 @@ impl Impl { } } + if !self.associated_constants.is_empty() { + for constant in &self.associated_constants { + constant.fmt(fmt)?; + write!(fmt, "\n")?; + } + } + for (i, func) in self.fns.iter().enumerate() { if i != 0 || !self.assoc_tys.is_empty() { write!(fmt, "\n")?; diff --git a/src/lib.rs b/src/lib.rs index 70b8675..afa1cee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,7 @@ //! println!("{}", scope.to_string()); //! ``` +mod associated_constant; mod associated_type; mod block; mod body; @@ -48,6 +49,7 @@ mod r#trait; mod r#type; +pub use associated_constant::*; pub use associated_type::*; pub use block::*; pub use field::*; diff --git a/src/trait.rs b/src/trait.rs index 7a8c069..76f3212 100644 --- a/src/trait.rs +++ b/src/trait.rs @@ -1,5 +1,6 @@ use std::fmt::{self, Write}; +use crate::AssociatedConstant; use crate::associated_type::AssociatedType; use crate::bound::Bound; use crate::formatter::{fmt_bound_rhs, Formatter}; @@ -14,6 +15,7 @@ pub struct Trait { type_def: TypeDef, parents: Vec, associated_tys: Vec, + associated_constants: Vec, fns: Vec, macros: Vec, } @@ -25,6 +27,7 @@ impl Trait { type_def: TypeDef::new(name), parents: vec![], associated_tys: vec![], + associated_constants: vec![], fns: vec![], macros: vec![], } @@ -88,6 +91,25 @@ impl Trait { self.associated_tys.last_mut().unwrap() } + /// Push an associated constant to the trait block + pub fn push_associated_constant(&mut self, constant: AssociatedConstant) -> &mut Self + { + self.associated_constants.push(constant); + self + } + + + /// Create an associated constant for the trait block + pub fn new_associated_constant(&mut self, name: &str, datatype: Type) -> &mut AssociatedConstant + where + Type: Into + { + self.push_associated_constant( + AssociatedConstant::new(name, datatype)); + + self.associated_constants.last_mut().unwrap() + } + /// Push a new function definition, returning a mutable reference to it. pub fn new_fn(&mut self, name: &str) -> &mut Function { let mut func = Function::new(name); @@ -126,6 +148,13 @@ impl Trait { } } + if !self.associated_constants.is_empty() { + for constant in &self.associated_constants { + constant.fmt(fmt)?; + write!(fmt, "\n")?; + } + } + for (i, func) in self.fns.iter().enumerate() { if i != 0 || !assoc.is_empty() { write!(fmt, "\n")?; diff --git a/tests/codegen.rs b/tests/codegen.rs index 77e7346..5f5815a 100644 --- a/tests/codegen.rs +++ b/tests/codegen.rs @@ -603,5 +603,53 @@ enum IpAddrKind { V6, }"#; + assert_eq!(scope.to_string(), &expect[1..]); +} + +#[test] +fn impl_with_associated_constants() { + let mut scope = Scope::new(); + + let constant1 = AssociatedConstant::new("BAR", "usize"); + let mut constant2 = AssociatedConstant::new("BAZ", "i16"); + constant2.value("22"); + + let current_impl = scope.new_impl("Foo") + .push_associated_constant(constant1) + .push_associated_constant(constant2); + + current_impl.new_associated_constant("QUX", "usize").value("43"); + + let expect = r#" +impl Foo { + const BAR: usize; + const BAZ: i16 = 22; + const QUX: usize = 43; +}"#; + + assert_eq!(scope.to_string(), &expect[1..]); +} + +#[test] +fn trait_with_associated_constants() { + let mut scope = Scope::new(); + + let constant1 = AssociatedConstant::new("BAR", "usize"); + let mut constant2 = AssociatedConstant::new("BAZ", "i16"); + constant2.value("22"); + + let current_trait = scope.new_trait("Foo") + .push_associated_constant(constant1) + .push_associated_constant(constant2); + + current_trait.new_associated_constant("QUX", "usize").value("43"); + + let expect = r#" +trait Foo { + const BAR: usize; + const BAZ: i16 = 22; + const QUX: usize = 43; +}"#; + assert_eq!(scope.to_string(), &expect[1..]); } \ No newline at end of file