Skip to content

Commit eb8f647

Browse files
committed
v0.3.0: no M2 at prgrm end + versioned settings
Plus some other misc. minor changes
1 parent 36b06fc commit eb8f647

17 files changed

+195
-84
lines changed

Cargo.lock

Lines changed: 20 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/Cargo.toml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
[package]
22
name = "svg2gcode"
3-
version = "0.2.3"
3+
version = "0.3.0"
44
authors = ["Sameer Puri <crates@purisa.me>"]
55
edition = "2021"
66
description = "Convert paths in SVG files to GCode for a pen plotter, laser engraver, or other machine."
77
repository = "https://github.com/sameer/svg2gcode"
88
license = "MIT"
99

10+
[features]
11+
serde = ["dep:serde", "dep:serde_repr"]
12+
1013
[dependencies]
1114
g-code = { version = "0.4.1", features = ["serde"] }
1215
lyon_geom = "1.0.5"
@@ -23,7 +26,14 @@ optional = true
2326
version = "1"
2427
features = ["derive"]
2528

29+
[dependencies.serde_repr]
30+
optional = true
31+
version = "0.1"
32+
2633
[dev-dependencies]
27-
cairo-rs = { version = "0.18", default-features = false, features = ["svg", "v1_16"] }
34+
cairo-rs = { version = "0.18", default-features = false, features = [
35+
"svg",
36+
"v1_16",
37+
] }
2838
serde_json = "1"
2939
pretty_assertions = "1.4.0"

lib/src/arc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ mod tests {
286286
#[test]
287287
#[ignore = "Creates an image file, will revise later"]
288288
fn flatten_returns_expected_arcs() {
289-
const PATH: &'static str = "M 8.0549,11.9023
289+
const PATH: &str = "M 8.0549,11.9023
290290
c
291291
0.13447,1.69916 8.85753,-5.917903 7.35159,-6.170957
292292
z";

lib/src/converter/mod.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -171,17 +171,15 @@ fn node_name(node: &Node) -> String {
171171
name
172172
}
173173

174-
#[cfg(test)]
174+
#[cfg(all(test, feature = "serde"))]
175175
mod test {
176176
use super::*;
177-
#[cfg(feature = "serde")]
178177
use svgtypes::LengthUnit;
179178

180179
#[test]
181-
#[cfg(feature = "serde")]
182180
fn serde_conversion_options_is_correct() {
183181
let default_struct = ConversionOptions::default();
184-
let default_json = "{\"dimensions\":[null,null]}";
182+
let default_json = r#"{"dimensions":[null,null]}"#;
185183

186184
assert_eq!(
187185
serde_json::to_string(&default_struct).unwrap(),
@@ -194,14 +192,13 @@ mod test {
194192
}
195193

196194
#[test]
197-
#[cfg(feature = "serde")]
198195
fn serde_conversion_options_with_single_dimension_is_correct() {
199196
let mut r#struct = ConversionOptions::default();
200197
r#struct.dimensions[0] = Some(Length {
201198
number: 4.,
202199
unit: LengthUnit::Mm,
203200
});
204-
let json = "{\"dimensions\":[{\"number\":4.0,\"unit\":\"Mm\"},null]}";
201+
let json = r#"{"dimensions":[{"number":4.0,"unit":"Mm"},null]}"#;
205202

206203
assert_eq!(serde_json::to_string(&r#struct).unwrap(), json);
207204
assert_eq!(
@@ -211,7 +208,6 @@ mod test {
211208
}
212209

213210
#[test]
214-
#[cfg(feature = "serde")]
215211
fn serde_conversion_options_with_both_dimensions_is_correct() {
216212
let mut r#struct = ConversionOptions::default();
217213
r#struct.dimensions = [
@@ -224,8 +220,7 @@ mod test {
224220
unit: LengthUnit::In,
225221
}),
226222
];
227-
let json =
228-
"{\"dimensions\":[{\"number\":4.0,\"unit\":\"Mm\"},{\"number\":10.5,\"unit\":\"In\"}]}";
223+
let json = r#"{"dimensions":[{"number":4.0,"unit":"Mm"},{"number":10.5,"unit":"In"}]}"#;
229224

230225
assert_eq!(serde_json::to_string(&r#struct).unwrap(), json);
231226
assert_eq!(

lib/src/lib.rs

Lines changed: 101 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
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)
52
mod arc;
63
/// Converts an SVG to an internal representation
@@ -19,12 +16,82 @@ pub use machine::{Machine, MachineConfig, SupportedFunctionality};
1916
pub use postprocess::PostprocessConfig;
2017
pub 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)]
2422
pub 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

Comments
 (0)