@@ -3,8 +3,8 @@ use std::cell::RefCell;
3
3
use crate :: internals:: * ;
4
4
use proc_macro2:: Span ;
5
5
use proc_macro2:: TokenStream ;
6
- use quote:: quote_spanned;
7
- use quote :: { quote , ToTokens } ;
6
+ use quote:: { quote , quote_spanned, ToTokens } ;
7
+ use syn :: parse :: Parse ;
8
8
use syn:: parse:: ParseStream ;
9
9
use syn:: punctuated:: Punctuated ;
10
10
use syn:: spanned:: Spanned as _;
@@ -74,6 +74,8 @@ pub(crate) struct TypeAttr {
74
74
pub ( crate ) item : Option < syn:: Path > ,
75
75
/// `#[rune(constructor)]`.
76
76
pub ( crate ) constructor : bool ,
77
+ /// Protocols to "derive"
78
+ pub ( crate ) protocols : Vec < TypeProtocol > ,
77
79
/// Parsed documentation.
78
80
pub ( crate ) docs : Vec < syn:: Expr > ,
79
81
}
@@ -113,6 +115,60 @@ pub(crate) struct FieldProtocol {
113
115
custom : Option < syn:: Path > ,
114
116
}
115
117
118
+ pub ( crate ) struct TypeProtocol {
119
+ protocol : syn:: Ident ,
120
+ handler : Option < syn:: Path > ,
121
+ }
122
+
123
+ impl TypeProtocol {
124
+ pub fn expand ( & self ) -> TokenStream {
125
+ if let Some ( handler) = & self . handler {
126
+ let protocol = & self . protocol ;
127
+ return quote_spanned ! { protocol. span( ) =>
128
+ module. associated_function( rune:: runtime:: Protocol :: #protocol, #handler) ?;
129
+ } ;
130
+ }
131
+ match self . protocol . to_string ( ) . as_str ( ) {
132
+ "ADD" => quote_spanned ! { self . protocol. span( ) =>
133
+ module. associated_function( rune:: runtime:: Protocol :: ADD , |this: Self , other: Self | this + other) ?;
134
+ } ,
135
+ "STRING_DISPLAY" => quote_spanned ! { self . protocol. span( ) =>
136
+ module. associated_function( rune:: runtime:: Protocol :: STRING_DISPLAY , |this: & Self , buf: & mut String | {
137
+ use :: std:: fmt:: Write as _;
138
+ :: std:: write!( buf, "{this}" )
139
+ } ) ?;
140
+ } ,
141
+ "STRING_DEBUG" => quote_spanned ! { self . protocol. span( ) =>
142
+ module. associated_function( rune:: runtime:: Protocol :: STRING_DEBUG , |this: & Self , buf: & mut String | {
143
+ use :: std:: fmt:: Write as _;
144
+ :: std:: write!( buf, "{this:?}" )
145
+ } ) ?;
146
+ } ,
147
+ _ => syn:: Error :: new_spanned (
148
+ & self . protocol ,
149
+ format ! (
150
+ "`{}` is not a protocol supported for automatic generation on a type" ,
151
+ self . protocol
152
+ ) ,
153
+ )
154
+ . to_compile_error ( ) ,
155
+ }
156
+ }
157
+ }
158
+
159
+ impl Parse for TypeProtocol {
160
+ fn parse ( input : ParseStream ) -> syn:: Result < Self > {
161
+ Ok ( Self {
162
+ protocol : input. parse ( ) ?,
163
+ handler : if input. parse :: < Token ! [ =] > ( ) . is_ok ( ) {
164
+ Some ( input. parse ( ) ?)
165
+ } else {
166
+ None
167
+ } ,
168
+ } )
169
+ }
170
+ }
171
+
116
172
#[ derive( Default ) ]
117
173
pub ( crate ) struct Context {
118
174
pub ( crate ) errors : RefCell < Vec < syn:: Error > > ,
@@ -445,6 +501,12 @@ impl Context {
445
501
attr. install_with = Some ( parse_path_compat ( meta. input ) ?) ;
446
502
} else if meta. path == CONSTRUCTOR {
447
503
attr. constructor = true ;
504
+ } else if meta. path == PROTOCOLS {
505
+ // Parse `#[rune(protocols(<protocol>,*))]`
506
+ let protocols;
507
+ syn:: parenthesized!( protocols in meta. input) ;
508
+ attr. protocols
509
+ . extend ( protocols. parse_terminated ( TypeProtocol :: parse, Token ! [ , ] ) ?) ;
448
510
} else {
449
511
return Err ( syn:: Error :: new_spanned (
450
512
& meta. path ,
0 commit comments