I remember seeing a custom derive that would automatically generate as_some_variant() and is_some_variant() methods for a particular enum, but now I can't remember its name.
Given something like this:
#[derive(AsVariant, IsVariant)]
enum Value {
String(String),
Null,
...
}
I'm hoping to generate something like the following:
Writing these methods by hand isn't feasible because in practice the Value type is generated by a macro_rules! macro and there are many different enums that may be introduced on an ad-hoc basis[1].
I'm doing AST manipulation and need an easy way to represent things like type DefinitionSite = FunctionDefinition | LetStatement | StructDefinition | Whatever. ↩︎
Yeah I know about that assist and use it elsewhere, but that won't work in this situation because rust-analyzer doesn't show assists for items inside a macro invocation and it won't automatically keep them in sync when I add/remove fields.
For reference, I have several of these macro invocations and manually keeping 20+ sets of is_xxx() and as_xxx() methods in sync with the definition isn't practical.
ast_node_union! {
/// The place a name can first be introduced.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum NameDefinitionSite {
Function(ast::NamedFunctionDefinition),
Module(ast::NamedModuleDefinition),
Constant(ast::AssignmentStatement),
Parameter(ast::Parameter),
}
}
You can't create new identifiers like is_function() or as_module() from a macro_rules! macro, though. That's why I was looking for a custom derive that can do it for me.
For reference, here is the full macro definition.
macro_rules! ast_node_union {
(
$(#[$meta:meta])*
$vis:vis enum $name:ident {
$(
$variant:ident($ty:ty)
),*
$(,)?
}
) => {
$(#[$meta])*
$vis enum $name {
$($variant($ty)),*
}
impl rowan::ast::AstNode for $name {
type Language = scad_syntax::OpenSCAD;
fn can_cast(kind: scad_syntax::SyntaxKind) -> bool
where
Self: Sized,
{
$( <$ty>::can_cast(kind) )||*
}
fn cast(node: rowan::SyntaxNode<Self::Language>) -> Option<Self>
where
Self: Sized,
{
$(
if let Some(node) = <$ty>::cast(node.clone()) {
return Some($name::$variant(node));
}
)*
None
}
fn syntax(&self) -> &rowan::SyntaxNode<Self::Language> {
match self {
$(
$name::$variant(n) => n.syntax(),
)*
}
}
}
$(
impl From<$ty> for $name {
fn from(node: $ty) -> Self {
$name::$variant(node)
}
}
impl TryFrom<$name> for $ty {
type Error = $crate::macros::ConversionFailed;
fn try_from(value: $name) -> Result<$ty, Self::Error> {
match value {
$name::$variant(v) => Ok(v),
_ => Err($crate::macros::ConversionFailed),
}
}
}
)*
};
}
Yeah, I might do that in the meantime. I was hoping to find the crate I had used in the past though, because it also had some other things I'd like to use.