1- #[ cfg( feature = "serde" ) ]
2- use serde:: { Deserialize , Serialize } ;
3-
41/// Approximate [Bézier curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) with [Circular arcs](https://en.wikipedia.org/wiki/Circular_arc)
52mod arc;
63/// Converts an SVG to an internal representation
@@ -19,12 +16,82 @@ pub use machine::{Machine, MachineConfig, SupportedFunctionality};
1916pub use postprocess:: PostprocessConfig ;
2017pub use turtle:: Turtle ;
2118
22- #[ cfg_attr( feature = "serde" , derive( Serialize , Deserialize ) ) ]
19+ /// A cross-platform type used to store all configuration types.
20+ #[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
2321#[ derive( Debug , Default , Clone , PartialEq ) ]
2422pub struct Settings {
2523 pub conversion : ConversionConfig ,
2624 pub machine : MachineConfig ,
2725 pub postprocess : PostprocessConfig ,
26+ #[ cfg_attr( feature = "serde" , serde( default = "Version::unknown" ) ) ]
27+ pub version : Version ,
28+ }
29+
30+ impl Settings {
31+ /// Try to automatically upgrade the supported version.
32+ ///
33+ /// This will return an error if:
34+ ///
35+ /// - Settings version is [`Version::Unknown`].
36+ /// - There are breaking changes requiring manual intervention. In which case this does a partial update to that point.
37+ pub fn try_upgrade ( & mut self ) -> Result < ( ) , ( ) > {
38+ loop {
39+ match self . version {
40+ // Compatibility for M2 by default
41+ Version :: V0 => {
42+ self . machine . end_sequence = Some ( format ! (
43+ "{} M2" ,
44+ self . machine. end_sequence. take( ) . unwrap_or_default( )
45+ ) ) ;
46+ self . version = Version :: V5 ;
47+ }
48+ Version :: V5 => break Ok ( ( ) ) ,
49+ Version :: Unknown ( _) => break Err ( ( ) ) ,
50+ }
51+ }
52+ }
53+ }
54+
55+ /// Used to control breaking change behavior for [`Settings`].
56+ ///
57+ /// There were already 3 non-breaking version bumps (V1 -> V4) so versioning starts off with [`Version::V5`].
58+ #[ cfg_attr( feature = "serde" , derive( serde:: Serialize , serde:: Deserialize ) ) ]
59+ #[ derive( Debug , Clone , PartialEq , Eq , PartialOrd , Ord ) ]
60+ pub enum Version {
61+ /// Implicitly versioned settings from before this type was introduced.
62+ V0 ,
63+ /// M2 is no longer appended to the program by default
64+ V5 ,
65+ #[ cfg_attr( feature = "serde" , serde( untagged) ) ]
66+ Unknown ( String ) ,
67+ }
68+
69+ impl Version {
70+ /// Returns the most recent [`Version`]. This is useful for asking users to upgrade externally-stored settings.
71+ pub const fn latest ( ) -> Self {
72+ Self :: V5
73+ }
74+
75+ /// Default version for old settings.
76+ pub const fn unknown ( ) -> Self {
77+ Self :: V0
78+ }
79+ }
80+
81+ impl std:: fmt:: Display for Version {
82+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
83+ match self {
84+ Version :: V0 => f. write_str ( "V0" ) ,
85+ Version :: V5 => f. write_str ( "V5" ) ,
86+ Version :: Unknown ( unknown) => f. write_str ( unknown) ,
87+ }
88+ }
89+ }
90+
91+ impl Default for Version {
92+ fn default ( ) -> Self {
93+ Self :: latest ( )
94+ }
2895}
2996
3097#[ cfg( test) ]
@@ -334,4 +401,34 @@ mod test {
334401 "# ;
335402 serde_json:: from_str :: < Settings > ( json) . unwrap ( ) ;
336403 }
404+
405+ #[ test]
406+ #[ cfg( feature = "serde" ) ]
407+ fn deserialize_v5_config_succeeds ( ) {
408+ let json = r#"
409+ {
410+ "conversion": {
411+ "tolerance": 0.002,
412+ "feedrate": 300.0,
413+ "dpi": 96.0
414+ },
415+ "machine": {
416+ "supported_functionality": {
417+ "circular_interpolation": true
418+ },
419+ "tool_on_sequence": null,
420+ "tool_off_sequence": null,
421+ "begin_sequence": null,
422+ "end_sequence": null
423+ },
424+ "postprocess": {
425+ "checksums": false,
426+ "line_numbers": false,
427+ "newline_before_comment": false
428+ },
429+ "version": "V5"
430+ }
431+ "# ;
432+ serde_json:: from_str :: < Settings > ( json) . unwrap ( ) ;
433+ }
337434}
0 commit comments