forked from 1b-agent/opencadstudio-upstream
300 lines
10 KiB
Rust
300 lines
10 KiB
Rust
//! Reject invalid numeric inputs before the backend starts an undo operation.
|
|
use crate::{id::ObjectId, ops::*, ApiError, ApiResult};
|
|
|
|
fn finite(values: &[f64]) -> bool {
|
|
values.iter().all(|v| v.is_finite())
|
|
}
|
|
fn positive(value: f64) -> bool {
|
|
value.is_finite() && value > 0.0
|
|
}
|
|
fn direction(value: &[f64; 3]) -> bool {
|
|
finite(value) && value.iter().any(|v| *v != 0.0)
|
|
}
|
|
fn check(valid: bool, op: &'static str) -> ApiResult<()> {
|
|
if valid {
|
|
Ok(())
|
|
} else {
|
|
Err(ApiError::validation(
|
|
op,
|
|
"invalid or nonfinite geometry input",
|
|
))
|
|
}
|
|
}
|
|
|
|
fn check_payload(valid: bool, op: &'static str, reason: &'static str) -> ApiResult<()> {
|
|
if valid {
|
|
Ok(())
|
|
} else {
|
|
Err(ApiError::validation(op, reason))
|
|
}
|
|
}
|
|
|
|
fn valid_application_name(name: &str) -> bool {
|
|
!name.trim().is_empty() && !name.contains('\0')
|
|
}
|
|
|
|
fn valid_xdata_record(app: &str, record: Option<&XDataRecord>) -> bool {
|
|
valid_application_name(app)
|
|
&& record.is_none_or(|record| {
|
|
record.application_name == app
|
|
&& record.values.iter().all(|value| match value {
|
|
XDataValue::String(value)
|
|
| XDataValue::ControlString(value)
|
|
| XDataValue::LayerName(value) => !value.contains('\0'),
|
|
XDataValue::Point3D(value)
|
|
| XDataValue::Position3D(value)
|
|
| XDataValue::Displacement3D(value)
|
|
| XDataValue::Direction3D(value) => finite(value),
|
|
XDataValue::Real(value)
|
|
| XDataValue::Distance(value)
|
|
| XDataValue::ScaleFactor(value) => value.is_finite(),
|
|
XDataValue::BinaryData(_)
|
|
| XDataValue::Handle(_)
|
|
| XDataValue::Integer16(_)
|
|
| XDataValue::Integer32(_) => true,
|
|
})
|
|
})
|
|
}
|
|
|
|
fn valid_xrecord(spec: &XRecordSpec) -> bool {
|
|
!spec.name.trim().is_empty()
|
|
&& !spec.name.contains('\0')
|
|
&& spec.entries.len() <= BULK_ITEM_CAP
|
|
&& spec.entries.iter().all(|entry| {
|
|
let code = entry.code;
|
|
match &entry.value {
|
|
XRecordValue::String(value) => {
|
|
matches!(
|
|
code,
|
|
0..=4
|
|
| 6..=9
|
|
| 100..=102
|
|
| 300..=309
|
|
| 410..=419
|
|
| 430..=439
|
|
| 470..=479
|
|
| 999
|
|
| 1000..=1003
|
|
) && !value.contains('\0')
|
|
&& value.encode_utf16().count() <= u16::MAX as usize
|
|
}
|
|
XRecordValue::Point3D(value) => {
|
|
matches!(code, 10..=37 | 110..=139 | 210..=269 | 1010..=1039 | 1043..=1069)
|
|
&& finite(value)
|
|
}
|
|
XRecordValue::Double(value) => {
|
|
matches!(code, 38..=59 | 140..=149 | 460..=469 | 1040..=1042)
|
|
&& value.is_finite()
|
|
}
|
|
XRecordValue::Int16(_) => {
|
|
matches!(code, 60..=79 | 170..=179 | 270..=279 | 370..=389 | 400..=409 | 1070)
|
|
}
|
|
XRecordValue::Int32(_) => matches!(code, 80..=99 | 420..=429 | 440..=459 | 1071),
|
|
XRecordValue::Int64(_) => matches!(code, 150..=169),
|
|
XRecordValue::Byte(_) => matches!(code, 280..=289),
|
|
XRecordValue::Bool(_) => matches!(code, 290..=299),
|
|
XRecordValue::Handle(_) => {
|
|
code < 0 || matches!(code, 5 | 105 | 320..=369 | 390..=399 | 480..=481 | 1005)
|
|
}
|
|
XRecordValue::Chunk(value) => {
|
|
matches!(code, 310..=319 | 1004) && value.len() <= u8::MAX as usize
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
pub(crate) fn curve(spec: &Curve2Spec) -> ApiResult<()> {
|
|
use Curve2Spec::*;
|
|
let valid = match spec {
|
|
Line { start, end, .. } => finite(start) && finite(end) && start != end,
|
|
Circle { centre, radius, .. } => finite(centre) && positive(*radius),
|
|
Point { position, .. } => finite(position),
|
|
Arc {
|
|
centre,
|
|
radius,
|
|
start_angle,
|
|
end_angle,
|
|
..
|
|
} => finite(centre) && positive(*radius) && finite(&[*start_angle, *end_angle]),
|
|
Polyline { points, closed, .. } => {
|
|
points.len() >= if *closed { 3 } else { 2 }
|
|
&& points.len() <= BULK_ITEM_CAP
|
|
&& points.iter().all(|p| finite(p) && p[2] == points[0][2])
|
|
}
|
|
Ellipse {
|
|
centre,
|
|
major_axis,
|
|
ratio,
|
|
start,
|
|
end,
|
|
..
|
|
} => {
|
|
finite(centre)
|
|
&& direction(major_axis)
|
|
&& positive(*ratio)
|
|
&& *ratio <= 1.0
|
|
&& finite(&[*start, *end])
|
|
}
|
|
Spline {
|
|
degree,
|
|
control_points,
|
|
knots,
|
|
weights,
|
|
..
|
|
} => {
|
|
*degree > 0
|
|
&& control_points.len() <= BULK_ITEM_CAP
|
|
&& knots.len() <= BULK_ITEM_CAP
|
|
&& weights.len() <= BULK_ITEM_CAP
|
|
&& cadkernel::space::NurbsCurve3::new_strict(
|
|
*degree as usize,
|
|
control_points.clone(),
|
|
knots.clone(),
|
|
if weights.is_empty() {
|
|
vec![1.0; control_points.len()]
|
|
} else {
|
|
weights.clone()
|
|
},
|
|
)
|
|
.is_some()
|
|
}
|
|
Ray {
|
|
origin,
|
|
direction: d,
|
|
..
|
|
}
|
|
| XLine {
|
|
origin,
|
|
direction: d,
|
|
..
|
|
} => finite(origin) && direction(d),
|
|
};
|
|
check(valid, "CreateCurve")
|
|
}
|
|
|
|
pub(crate) fn operation(op: &Operation) -> ApiResult<()> {
|
|
use Operation::*;
|
|
let valid = match op {
|
|
CreateCurve(spec) => return curve(spec),
|
|
Extrude { direction: d, .. } => direction(d),
|
|
Revolve {
|
|
axis: (pivot, d),
|
|
angle,
|
|
..
|
|
} => finite(pivot) && direction(d) && angle.is_finite() && *angle != 0.0,
|
|
AddVertex { point, .. } => finite(point),
|
|
CreateInsert(s) => {
|
|
finite(&s.insert_point)
|
|
&& s.scale.is_finite()
|
|
&& s.scale != 0.0
|
|
&& s.rotation.is_finite()
|
|
}
|
|
CreateViewport(s) => {
|
|
finite(&s.center)
|
|
&& finite(&s.view_target)
|
|
&& positive(s.width)
|
|
&& positive(s.height)
|
|
&& positive(s.view_height)
|
|
}
|
|
CreateText(s) => finite(&s.insertion_point) && positive(s.height) && s.rotation.is_finite(),
|
|
CreateMText(s) => finite(&s.insertion_point) && positive(s.height),
|
|
SetXData {
|
|
id,
|
|
application_name,
|
|
record,
|
|
} => {
|
|
return check_payload(
|
|
*id != ObjectId::NULL && valid_xdata_record(application_name, record.as_ref()),
|
|
"SetXData",
|
|
"invalid XDATA record",
|
|
)
|
|
}
|
|
SetXDataMany(records) => {
|
|
return check_payload(
|
|
records.iter().all(|(id, app, record)| {
|
|
*id != ObjectId::NULL && valid_xdata_record(app, record.as_ref())
|
|
}),
|
|
"SetXDataMany",
|
|
"invalid XDATA record",
|
|
)
|
|
}
|
|
CreateXRecord(spec) => {
|
|
return check_payload(valid_xrecord(spec), "CreateXRecord", "invalid XRecord payload")
|
|
}
|
|
SetXRecord { id, spec } => {
|
|
return check_payload(
|
|
*id != ObjectId::NULL && valid_xrecord(spec),
|
|
"SetXRecord",
|
|
"invalid XRecord payload",
|
|
)
|
|
}
|
|
CreateHatch(s) => {
|
|
(3..=BULK_ITEM_CAP).contains(&s.boundary.len()) && s.boundary.iter().all(|p| finite(p))
|
|
}
|
|
CreateDimensionLinear(s) => {
|
|
finite(&s.first_point) && finite(&s.second_point) && finite(&s.definition_point)
|
|
}
|
|
CreateDimensionRadius(s) | CreateDimensionDiameter(s) => {
|
|
finite(&s.center) && finite(&s.point)
|
|
}
|
|
CreateDimensionAngular(s) | CreateDimensionAngular2Ln(s) => {
|
|
finite(&s.vertex)
|
|
&& finite(&s.first_point)
|
|
&& finite(&s.second_point)
|
|
&& finite(&s.arc_location)
|
|
}
|
|
CreateAttributeDefinition(s) => {
|
|
finite(&s.insertion_point) && positive(s.height) && s.rotation.is_finite()
|
|
}
|
|
SetViewportView {
|
|
view_target,
|
|
view_height,
|
|
..
|
|
} => finite(view_target) && positive(*view_height),
|
|
CreateRasterImage(s) => {
|
|
finite(&s.insertion_point)
|
|
&& direction(&s.u_vector)
|
|
&& direction(&s.v_vector)
|
|
&& s.size.iter().all(|v| positive(*v))
|
|
}
|
|
CreateTable(s) => {
|
|
finite(&s.insertion_point)
|
|
&& !s.data.is_empty()
|
|
&& !s.data[0].is_empty()
|
|
&& s.data
|
|
.len()
|
|
.checked_mul(s.data[0].len())
|
|
.is_some_and(|n| n <= BULK_ITEM_CAP)
|
|
&& s.data.iter().all(|r| r.len() == s.data[0].len())
|
|
}
|
|
_ => true,
|
|
};
|
|
check(valid, op.op_name())
|
|
}
|
|
|
|
pub(crate) fn solid(spec: &SolidPrimitive) -> ApiResult<()> {
|
|
use SolidPrimitive::*;
|
|
let valid = match spec {
|
|
Cuboid { origin, size } | Wedge { origin, size } => {
|
|
finite(origin) && size.iter().all(|v| positive(*v))
|
|
}
|
|
Sphere { centre, radius } => finite(centre) && positive(*radius),
|
|
Cylinder {
|
|
base,
|
|
radius,
|
|
height,
|
|
}
|
|
| Cone {
|
|
base,
|
|
radius,
|
|
height,
|
|
} => finite(base) && positive(*radius) && positive(*height),
|
|
Torus {
|
|
centre,
|
|
major_radius,
|
|
minor_radius,
|
|
} => finite(centre) && positive(*major_radius) && positive(*minor_radius),
|
|
};
|
|
check(valid, "CreateSolid")
|
|
}
|