Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 24 additions & 27 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,30 @@ pub fn gerber_doc_as_str(gerber_doc: &GerberDoc) -> String {
str::from_utf8(&filevec).unwrap().to_string()
}

/// Converts a raw Gerber integer in format `fs` to the nano (6-decimal)
/// precision `CoordinateNumber` uses, rejecting formats with more than 6
/// decimals and values that overflow instead of panicking.
fn to_nano(value: i64, fs: &CoordinateFormat) -> Result<CoordinateNumber, GerberError> {
let factor = 6u8.checked_sub(fs.decimal).ok_or_else(|| {
GerberError::CoordinateFormatError(format!(
"{} decimal places are more than the supported 6",
fs.decimal
))
})?;
let nano = value
.checked_mul(10i64.pow(factor as u32))
.ok_or_else(|| GerberError::RangeError(format!("coordinate {value} is too large")))?;
CoordinateNumber::new(nano).validate(fs)
}

pub fn coordinates_from_gerber(
mut x_as_int: i64,
mut y_as_int: i64,
x_as_int: i64,
y_as_int: i64,
fs: CoordinateFormat,
) -> Result<Option<Coordinates>, GerberError> {
// we have the raw gerber string as int but now have to convert it to nano precision format
// (i.e. 6 decimal precision) as this is what CoordinateNumber uses internally
let factor = (6u8 - fs.decimal) as u32;
x_as_int *= 10i64.pow(factor);
y_as_int *= 10i64.pow(factor);
Ok(Some(Coordinates::new(
CoordinateNumber::new(x_as_int).validate(&fs)?,
CoordinateNumber::new(y_as_int).validate(&fs)?,
to_nano(x_as_int, &fs)?,
to_nano(y_as_int, &fs)?,
fs,
)))
}
Expand All @@ -51,15 +62,8 @@ pub fn partial_coordinates_from_gerber(
y_as_int: Option<i64>,
fs: CoordinateFormat,
) -> Result<Option<Coordinates>, GerberError> {
// we have the raw gerber string as int but now have to convert it to nano precision format
// (i.e. 6 decimal precision) as this is what CoordinateNumber uses internally
let factor = (6u8 - fs.decimal) as u32;
let x = x_as_int
.map(|value| CoordinateNumber::new(value * 10i64.pow(factor)).validate(&fs))
.transpose()?;
let y = y_as_int
.map(|value| CoordinateNumber::new(value * 10i64.pow(factor)).validate(&fs))
.transpose()?;
let x = x_as_int.map(|value| to_nano(value, &fs)).transpose()?;
let y = y_as_int.map(|value| to_nano(value, &fs)).transpose()?;

let coordinates = match (x, y) {
(None, None) => None,
Expand All @@ -73,15 +77,8 @@ pub fn partial_coordinates_offset_from_gerber(
y_as_int: Option<i64>,
fs: CoordinateFormat,
) -> Result<Option<CoordinateOffset>, GerberError> {
// we have the raw gerber string as int but now have to convert it to nano precision format
// (i.e. 6 decimal precision) as this is what CoordinateNumber uses internally
let factor = (6u8 - fs.decimal) as u32;
let x = x_as_int
.map(|value| CoordinateNumber::new(value * 10i64.pow(factor)).validate(&fs))
.transpose()?;
let y = y_as_int
.map(|value| CoordinateNumber::new(value * 10i64.pow(factor)).validate(&fs))
.transpose()?;
let x = x_as_int.map(|value| to_nano(value, &fs)).transpose()?;
let y = y_as_int.map(|value| to_nano(value, &fs)).transpose()?;

let coordinate_offset = match (x, y) {
(None, None) => None,
Expand Down
19 changes: 19 additions & 0 deletions tests/component_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4076,3 +4076,22 @@ fn generation_software_with_one_value_does_not_panic() {
))) if name == ".GenerationSoftware"
)));
}

/// More than 6 decimal places, or a coordinate too large for nano precision, is an
/// error on that command, not a panic.
#[test]
fn oversized_coordinate_formats_do_not_panic() {
// given
logging_init();

for gerber in [
"%FSLAX27Y27*%\n%MOMM*%\nX1Y1D02*\nM02*\n",
"%FSLAX46Y46*%\n%MOMM*%\nX999999999999999999Y1D02*\nM02*\n",
] {
// when
let doc = parse(gerber_to_reader(gerber)).unwrap();

// then
assert!(doc.commands.iter().any(Result::is_err), "{gerber}");
}
}
Loading