@@ -9,7 +9,7 @@ use syn::{Field, Ident};
99use crate :: {
1010 attrs:: field:: FieldAttributes ,
1111 consts:: DEPRECATED_PREFIX ,
12- gen:: { version:: ContainerVersion , ToTokensExt } ,
12+ gen:: { neighbors :: Neighbors , version:: ContainerVersion , ToTokensExt } ,
1313} ;
1414
1515/// A versioned field, which contains contains common [`Field`] data and a chain
@@ -36,96 +36,29 @@ impl ToTokensExt for VersionedField {
3636 // The code generation then depends on the relation to other
3737 // versions (with actions).
3838
39- // TODO (@Techassi): Make this more robust by also including
40- // the container versions in the action chain. I'm not happy
41- // with the follwoing code at all. It serves as a good first
42- // implementation to get something out of the door.
43- match chain. get ( & container_version. inner ) {
44- Some ( action) => match action {
45- FieldStatus :: Added ( field_ident) => {
46- let field_type = & self . inner . ty ;
47-
48- Some ( quote ! {
49- pub #field_ident: #field_type,
50- } )
51- }
52- FieldStatus :: Renamed { from : _, to } => {
53- let field_type = & self . inner . ty ;
54-
55- Some ( quote ! {
56- pub #to: #field_type,
57- } )
58- }
59- FieldStatus :: Deprecated ( field_ident) => {
60- let field_type = & self . inner . ty ;
61-
62- Some ( quote ! {
63- #[ deprecated]
64- pub #field_ident: #field_type,
65- } )
66- }
67- } ,
68- None => {
69- // Generate field if the container version is not
70- // included in the action chain. First we check the
71- // earliest field action version.
72- if let Some ( ( version, action) ) = chain. first_key_value ( ) {
73- if container_version. inner < * version {
74- match action {
75- FieldStatus :: Added ( _) => return None ,
76- FieldStatus :: Renamed { from, to : _ } => {
77- let field_type = & self . inner . ty ;
78-
79- return Some ( quote ! {
80- pub #from: #field_type,
81- } ) ;
82- }
83- FieldStatus :: Deprecated ( field_ident) => {
84- let field_type = & self . inner . ty ;
85-
86- return Some ( quote ! {
87- pub #field_ident: #field_type,
88- } ) ;
89- }
90- }
91- }
92- }
93-
94- // Check the container version against the latest
95- // field action version.
96- if let Some ( ( version, action) ) = chain. last_key_value ( ) {
97- if container_version. inner > * version {
98- match action {
99- FieldStatus :: Added ( field_ident) => {
100- let field_type = & self . inner . ty ;
101-
102- return Some ( quote ! {
103- pub #field_ident: #field_type,
104- } ) ;
105- }
106- FieldStatus :: Renamed { from : _, to } => {
107- let field_type = & self . inner . ty ;
108-
109- return Some ( quote ! {
110- pub #to: #field_type,
111- } ) ;
112- }
113- FieldStatus :: Deprecated ( field_ident) => {
114- let field_type = & self . inner . ty ;
115-
116- return Some ( quote ! {
117- #[ deprecated]
118- pub #field_ident: #field_type,
119- } ) ;
120- }
121- }
122- }
123- }
124-
125- // TODO (@Techassi): Handle versions which are in between
126- // versions defined in field actions.
127- None
128- }
39+ let field_type = & self . inner . ty ;
40+
41+ match chain
42+ . get ( & container_version. inner )
43+ . expect ( "internal error: chain must contain container version" )
44+ {
45+ FieldStatus :: Added ( field_ident) => Some ( quote ! {
46+ pub #field_ident: #field_type,
47+ } ) ,
48+ FieldStatus :: Renamed { _from : _, to } => Some ( quote ! {
49+ pub #to: #field_type,
50+ } ) ,
51+ FieldStatus :: Deprecated {
52+ ident : field_ident,
53+ note,
54+ } => Some ( quote ! {
55+ #[ deprecated = #note]
56+ pub #field_ident: #field_type,
57+ } ) ,
58+ FieldStatus :: NotPresent => None ,
59+ FieldStatus :: NoChange ( field_ident) => Some ( quote ! {
60+ pub #field_ident: #field_type,
61+ } ) ,
12962 }
13063 }
13164 None => {
@@ -144,11 +77,16 @@ impl ToTokensExt for VersionedField {
14477}
14578
14679impl VersionedField {
80+ /// Create a new versioned field by creating a status chain for each version
81+ /// defined in an action in the field attribute.
82+ ///
83+ /// This chain will get extended by the versions defined on the container by
84+ /// calling the [`VersionedField::insert_container_versions`] function.
14785 pub ( crate ) fn new ( field : Field , attrs : FieldAttributes ) -> Result < Self , Error > {
148- // Constructing the change chain requires going through the actions from
86+ // Constructing the action chain requires going through the actions from
14987 // the end, because the base struct always represents the latest (most
15088 // up-to-date) version of that struct. That's why the following code
151- // needs to go through the changes in reverse order, as otherwise it is
89+ // needs to go through the actions in reverse order, as otherwise it is
15290 // impossible to extract the field ident for each version.
15391
15492 // Deprecating a field is always the last state a field can end up in. For
@@ -160,7 +98,13 @@ impl VersionedField {
16098 let mut actions = BTreeMap :: new ( ) ;
16199
162100 let ident = field. ident . as_ref ( ) . unwrap ( ) ;
163- actions. insert ( * deprecated. since , FieldStatus :: Deprecated ( ident. clone ( ) ) ) ;
101+ actions. insert (
102+ * deprecated. since ,
103+ FieldStatus :: Deprecated {
104+ ident : ident. clone ( ) ,
105+ note : deprecated. note . to_string ( ) ,
106+ } ,
107+ ) ;
164108
165109 // When the field is deprecated, any rename which occured beforehand
166110 // requires access to the field ident to infer the field ident for
@@ -175,7 +119,7 @@ impl VersionedField {
175119 actions. insert (
176120 * rename. since ,
177121 FieldStatus :: Renamed {
178- from : from. clone ( ) ,
122+ _from : from. clone ( ) ,
179123 to : ident,
180124 } ,
181125 ) ;
@@ -201,7 +145,7 @@ impl VersionedField {
201145 actions. insert (
202146 * rename. since ,
203147 FieldStatus :: Renamed {
204- from : from. clone ( ) ,
148+ _from : from. clone ( ) ,
205149 to : ident,
206150 } ,
207151 ) ;
@@ -241,11 +185,58 @@ impl VersionedField {
241185 } )
242186 }
243187 }
188+
189+ /// Inserts container versions not yet present in the status chain.
190+ ///
191+ /// When intially creating a new [`VersionedField`], the code doesn't have
192+ /// access to the versions defined on the container. This function inserts
193+ /// all non-present container versions and decides which status and ident
194+ /// is the right fit based on the status neighbors.
195+ ///
196+ /// This continous chain ensures that when generating code (tokens), each
197+ /// field can lookup the status for a requested version.
198+ pub ( crate ) fn insert_container_versions ( & mut self , versions : & Vec < ContainerVersion > ) {
199+ if let Some ( chain) = & mut self . chain {
200+ for version in versions {
201+ if chain. contains_key ( & version. inner ) {
202+ continue ;
203+ }
204+
205+ match chain. get_neighbors ( & version. inner ) {
206+ ( None , Some ( _) ) => chain. insert ( version. inner , FieldStatus :: NotPresent ) ,
207+ ( Some ( status) , None ) => {
208+ let ident = match status {
209+ FieldStatus :: Added ( ident) => ident,
210+ FieldStatus :: Renamed { _from : _, to } => to,
211+ FieldStatus :: Deprecated { ident, note : _ } => ident,
212+ FieldStatus :: NoChange ( ident) => ident,
213+ FieldStatus :: NotPresent => unreachable ! ( ) ,
214+ } ;
215+
216+ chain. insert ( version. inner , FieldStatus :: NoChange ( ident. clone ( ) ) )
217+ }
218+ ( Some ( status) , Some ( _) ) => {
219+ let ident = match status {
220+ FieldStatus :: Added ( ident) => ident,
221+ FieldStatus :: Renamed { _from : _, to } => to,
222+ FieldStatus :: NoChange ( ident) => ident,
223+ _ => unreachable ! ( ) ,
224+ } ;
225+
226+ chain. insert ( version. inner , FieldStatus :: NoChange ( ident. clone ( ) ) )
227+ }
228+ _ => unreachable ! ( ) ,
229+ } ;
230+ }
231+ }
232+ }
244233}
245234
246235#[ derive( Debug ) ]
247236pub ( crate ) enum FieldStatus {
248237 Added ( Ident ) ,
249- Renamed { from : Ident , to : Ident } ,
250- Deprecated ( Ident ) ,
238+ Renamed { _from : Ident , to : Ident } ,
239+ Deprecated { ident : Ident , note : String } ,
240+ NoChange ( Ident ) ,
241+ NotPresent ,
251242}
0 commit comments