forked from 1b-agent/opencadstudio-upstream
Complete Dimension host model coverage for all nine subtypes
Adds a hand-written flat Dimension model to the adapter (manifest manual_kinds hook) and a synthetic-property section to the host coverage policy. The host derives actual_measurement and the base definition point on scripted create/edit. staged_python_dimension_lifecycle_over_real_ipc passes the full gate for every subtype including DWG and DXF reopen. Python ints are now accepted for coordinates and angles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a86d5bc727
commit
6b4c86fd7a
13 files changed
+1281
-49
No files matched your search
@@ -94,7 +94,7 @@ The host-owned catalog classifies all 48 entity variants and tracks every
|
||||
traced field, including fields marked `unmapped`. Query it with
|
||||
`doc.coverage()` or `doc.coverage("Line")`; each row includes source field,
|
||||
type, access, and current validation status. Geometry edits remain limited to
|
||||
the 24 mapped kinds.
|
||||
the 25 mapped kinds.
|
||||
For any entity, `entity.snapshot` returns a detached dictionary of its raw
|
||||
CAD fields, including `snapshot["common"]` and kind-specific nested values.
|
||||
It can inspect an unmapped kind; editing the dictionary does
|
||||
|
||||
@@ -25,6 +25,10 @@ use serde::Deserialize;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Manifest {
|
||||
/// Kinds whose payload is a nested enum: converted by hand-written
|
||||
/// `<snake>_to_dict`/`<snake>_from_dict`/`<snake>_apply` functions.
|
||||
#[serde(default)]
|
||||
pub manual_kinds: Vec<String>,
|
||||
/// Entity kinds exposed to `ocs.add`/`ocs.update`/`entity_to_dict`. The
|
||||
/// central, easy-to-widen filter for Phase 2 (see the plan doc).
|
||||
pub type_filter: Vec<String>,
|
||||
@@ -164,6 +168,9 @@ fn helper_closure(manifest: &Manifest, registry: &TypeRegistry) -> Vec<String> {
|
||||
let mut queue: Vec<String> = Vec::new();
|
||||
|
||||
for kind in &manifest.type_filter {
|
||||
if manifest.manual_kinds.contains(kind) {
|
||||
continue;
|
||||
}
|
||||
let Some(info) = get(registry, kind) else {
|
||||
panic!("entity_manifest.json: unknown entity kind {kind}");
|
||||
};
|
||||
@@ -683,6 +690,13 @@ fn resolve_entity_fields(
|
||||
fn gen_entity_to_dict(manifest: &Manifest, registry: &TypeRegistry) -> String {
|
||||
let mut arms = String::new();
|
||||
for kind in &manifest.type_filter {
|
||||
if manifest.manual_kinds.contains(kind) {
|
||||
arms.push_str(&format!(
|
||||
" acadrust::EntityType::{kind}(value) => {}_to_dict(vm, value),\n",
|
||||
snake_case(kind)
|
||||
));
|
||||
continue;
|
||||
}
|
||||
let info = get(registry, kind).unwrap();
|
||||
let var = "value";
|
||||
let fields = resolve_entity_fields(manifest, registry, kind, info, var, Mode::Add);
|
||||
@@ -734,6 +748,13 @@ fn gen_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> String {
|
||||
if manifest.overrides.get(kind).is_some_and(|override_def| override_def.update_only) {
|
||||
continue;
|
||||
}
|
||||
if manifest.manual_kinds.contains(kind) {
|
||||
arms.push_str(&format!(
|
||||
" \"{kind}\" => acadrust::EntityType::{kind}({}_from_dict(dict, vm)?),\n",
|
||||
snake_case(kind)
|
||||
));
|
||||
continue;
|
||||
}
|
||||
let info = get(registry, kind).unwrap();
|
||||
let var = "value";
|
||||
let fields = resolve_entity_fields(manifest, registry, kind, info, var, Mode::Add);
|
||||
@@ -792,6 +813,13 @@ pub(crate) fn dict_to_entity(dict: &rustpython_vm::builtins::PyDictRef, vm: &Vir
|
||||
fn gen_apply_dict_to_entity(manifest: &Manifest, registry: &TypeRegistry) -> String {
|
||||
let mut arms = String::new();
|
||||
for kind in &manifest.type_filter {
|
||||
if manifest.manual_kinds.contains(kind) {
|
||||
arms.push_str(&format!(
|
||||
" acadrust::EntityType::{kind}(existing_value) => acadrust::EntityType::{kind}({}_apply(existing_value, dict, vm)?),\n",
|
||||
snake_case(kind)
|
||||
));
|
||||
continue;
|
||||
}
|
||||
let info = get(registry, kind).unwrap();
|
||||
let var = "value";
|
||||
let fields = resolve_entity_fields(manifest, registry, kind, info, var, Mode::Update);
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
"AttributeEntity",
|
||||
"Hatch",
|
||||
"Leader",
|
||||
"MLine"
|
||||
"MLine",
|
||||
"Dimension"
|
||||
],
|
||||
"manual_kinds": ["Dimension"],
|
||||
"base_fields": ["handle", "layer", "owner_handle"],
|
||||
"overrides": {
|
||||
"Point": {
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
// Hand-written Dimension model, `include!`d next to the generated CRUD code.
|
||||
//
|
||||
// `EntityType::Dimension` wraps a nine-variant enum, so the generator cannot
|
||||
// map it. Python sees one flat dict: `subtype` (fixed at creation), the
|
||||
// shared base fields, and the fields of that subtype only. Fields that belong
|
||||
// to another subtype are rejected rather than ignored. `actual_measurement`
|
||||
// and the base `definition_point` are host-derived, never scripted.
|
||||
|
||||
enum DimField<'a> {
|
||||
V(&'a mut Vector3),
|
||||
F(&'a mut f64),
|
||||
B(&'a mut bool),
|
||||
}
|
||||
|
||||
const DIM_SUBTYPES: &[&str] = &[
|
||||
"Aligned", "Linear", "Radius", "Diameter", "Angular2Ln", "Angular3Pt", "Ordinate", "Arc",
|
||||
"LargeRadial",
|
||||
];
|
||||
const DIM_BASE_KEYS: &[&str] = &[
|
||||
"text", "style_name", "normal", "text_middle_point", "attachment_point", "text_rotation",
|
||||
"horizontal_direction", "flip_arrow1", "flip_arrow2", "text_user_positioned",
|
||||
];
|
||||
const DIM_READ_ONLY_KEYS: &[&str] = &["subtype", "actual_measurement"];
|
||||
|
||||
fn dimension_subtype_name(dim: &acadrust::entities::Dimension) -> &'static str {
|
||||
use acadrust::entities::Dimension as D;
|
||||
match dim {
|
||||
D::Aligned(_) => "Aligned",
|
||||
D::Linear(_) => "Linear",
|
||||
D::Radius(_) => "Radius",
|
||||
D::Diameter(_) => "Diameter",
|
||||
D::Angular2Ln(_) => "Angular2Ln",
|
||||
D::Angular3Pt(_) => "Angular3Pt",
|
||||
D::Ordinate(_) => "Ordinate",
|
||||
D::Arc(_) => "Arc",
|
||||
D::LargeRadial(_) => "LargeRadial",
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtype-specific fields in stable order.
|
||||
fn dimension_fields(dim: &mut acadrust::entities::Dimension) -> Vec<(&'static str, DimField<'_>)> {
|
||||
use acadrust::entities::Dimension as D;
|
||||
use DimField::{B, F, V};
|
||||
match dim {
|
||||
D::Aligned(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("first_point", V(&mut d.first_point)),
|
||||
("second_point", V(&mut d.second_point)),
|
||||
("ext_line_rotation", F(&mut d.ext_line_rotation)),
|
||||
],
|
||||
D::Linear(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("first_point", V(&mut d.first_point)),
|
||||
("second_point", V(&mut d.second_point)),
|
||||
("rotation", F(&mut d.rotation)),
|
||||
("ext_line_rotation", F(&mut d.ext_line_rotation)),
|
||||
],
|
||||
D::Radius(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("angle_vertex", V(&mut d.angle_vertex)),
|
||||
("leader_length", F(&mut d.leader_length)),
|
||||
],
|
||||
D::Diameter(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("angle_vertex", V(&mut d.angle_vertex)),
|
||||
("leader_length", F(&mut d.leader_length)),
|
||||
],
|
||||
D::Angular2Ln(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("dimension_arc", V(&mut d.dimension_arc)),
|
||||
("first_point", V(&mut d.first_point)),
|
||||
("second_point", V(&mut d.second_point)),
|
||||
("angle_vertex", V(&mut d.angle_vertex)),
|
||||
],
|
||||
D::Angular3Pt(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("first_point", V(&mut d.first_point)),
|
||||
("second_point", V(&mut d.second_point)),
|
||||
("angle_vertex", V(&mut d.angle_vertex)),
|
||||
],
|
||||
D::Ordinate(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("feature_location", V(&mut d.feature_location)),
|
||||
("leader_endpoint", V(&mut d.leader_endpoint)),
|
||||
("is_ordinate_type_x", B(&mut d.is_ordinate_type_x)),
|
||||
],
|
||||
D::Arc(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("first_extension_point", V(&mut d.first_extension_point)),
|
||||
("second_extension_point", V(&mut d.second_extension_point)),
|
||||
("center_point", V(&mut d.center_point)),
|
||||
("is_partial", B(&mut d.is_partial)),
|
||||
("arc_start_parameter", F(&mut d.arc_start_parameter)),
|
||||
("arc_end_parameter", F(&mut d.arc_end_parameter)),
|
||||
("has_leader", B(&mut d.has_leader)),
|
||||
("first_leader_point", V(&mut d.first_leader_point)),
|
||||
("second_leader_point", V(&mut d.second_leader_point)),
|
||||
],
|
||||
D::LargeRadial(d) => vec![
|
||||
("definition_point", V(&mut d.definition_point)),
|
||||
("chord_point", V(&mut d.chord_point)),
|
||||
("jog_angle", F(&mut d.jog_angle)),
|
||||
("override_center", V(&mut d.override_center)),
|
||||
("jog_point", V(&mut d.jog_point)),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// Fields `ocs.add()` must receive (the geometry that defines the dimension).
|
||||
fn dimension_required(subtype: &str) -> &'static [&'static str] {
|
||||
match subtype {
|
||||
"Aligned" | "Linear" => &["definition_point", "first_point", "second_point"],
|
||||
"Radius" | "Diameter" => &["definition_point", "angle_vertex"],
|
||||
"Angular2Ln" => &["definition_point", "dimension_arc", "first_point", "second_point", "angle_vertex"],
|
||||
"Angular3Pt" => &["definition_point", "first_point", "second_point", "angle_vertex"],
|
||||
"Ordinate" => &["feature_location", "leader_endpoint"],
|
||||
"Arc" => &["definition_point", "first_extension_point", "second_extension_point", "center_point"],
|
||||
"LargeRadial" => &["definition_point", "chord_point"],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
fn dimension_base_mut(dim: &mut acadrust::entities::Dimension) -> &mut acadrust::entities::DimensionBase {
|
||||
dim.base_mut()
|
||||
}
|
||||
|
||||
fn dim_attachment_point_name(value: acadrust::entities::AttachmentPointType) -> &'static str {
|
||||
use acadrust::entities::AttachmentPointType as A;
|
||||
match value {
|
||||
A::TopLeft => "TopLeft",
|
||||
A::TopCenter => "TopCenter",
|
||||
A::TopRight => "TopRight",
|
||||
A::MiddleLeft => "MiddleLeft",
|
||||
A::MiddleCenter => "MiddleCenter",
|
||||
A::MiddleRight => "MiddleRight",
|
||||
A::BottomLeft => "BottomLeft",
|
||||
A::BottomCenter => "BottomCenter",
|
||||
A::BottomRight => "BottomRight",
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_dim_attachment_point(
|
||||
value: &str,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<acadrust::entities::AttachmentPointType> {
|
||||
use acadrust::entities::AttachmentPointType as A;
|
||||
Ok(match value {
|
||||
"TopLeft" => A::TopLeft,
|
||||
"TopCenter" => A::TopCenter,
|
||||
"TopRight" => A::TopRight,
|
||||
"MiddleLeft" => A::MiddleLeft,
|
||||
"MiddleCenter" => A::MiddleCenter,
|
||||
"MiddleRight" => A::MiddleRight,
|
||||
"BottomLeft" => A::BottomLeft,
|
||||
"BottomCenter" => A::BottomCenter,
|
||||
"BottomRight" => A::BottomRight,
|
||||
other => {
|
||||
return Err(vm.new_value_error(format!("ocs: unsupported AttachmentPointType value: {other}")))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn dimension_to_dict(
|
||||
vm: &VirtualMachine,
|
||||
dimension: &acadrust::entities::Dimension,
|
||||
) -> PyResult<PyObjectRef> {
|
||||
let mut dim = dimension.clone();
|
||||
let dict = vm.ctx.new_dict();
|
||||
let base = dimension.base();
|
||||
dict.set_item("kind", vm.new_pyobj("Dimension"), vm)?;
|
||||
dict.set_item("handle", vm.new_pyobj(base.common.handle.value()), vm)?;
|
||||
dict.set_item("layer", vm.new_pyobj(base.common.layer.clone()), vm)?;
|
||||
dict.set_item("owner_handle", vm.new_pyobj(base.common.owner_handle.value()), vm)?;
|
||||
dict.set_item("subtype", vm.new_pyobj(dimension_subtype_name(dimension)), vm)?;
|
||||
dict.set_item("actual_measurement", vm.new_pyobj(base.actual_measurement), vm)?;
|
||||
dict.set_item("text", vm.new_pyobj(base.text.clone()), vm)?;
|
||||
dict.set_item("style_name", vm.new_pyobj(base.style_name.clone()), vm)?;
|
||||
dict.set_item("normal", vector3_to_py_dict(vm, &base.normal)?, vm)?;
|
||||
dict.set_item("text_middle_point", vector3_to_py_dict(vm, &base.text_middle_point)?, vm)?;
|
||||
dict.set_item("attachment_point", vm.new_pyobj(dim_attachment_point_name(base.attachment_point)), vm)?;
|
||||
dict.set_item("text_rotation", vm.new_pyobj(base.text_rotation), vm)?;
|
||||
dict.set_item("horizontal_direction", vm.new_pyobj(base.horizontal_direction), vm)?;
|
||||
dict.set_item("flip_arrow1", vm.new_pyobj(base.flip_arrow1), vm)?;
|
||||
dict.set_item("flip_arrow2", vm.new_pyobj(base.flip_arrow2), vm)?;
|
||||
dict.set_item("text_user_positioned", vm.new_pyobj(base.text_user_positioned), vm)?;
|
||||
for (name, field) in dimension_fields(&mut dim) {
|
||||
let value: PyObjectRef = match field {
|
||||
DimField::V(v) => vector3_to_py_dict(vm, v)?,
|
||||
DimField::F(v) => vm.new_pyobj(*v),
|
||||
DimField::B(v) => vm.new_pyobj(*v),
|
||||
};
|
||||
dict.set_item(name, value, vm)?;
|
||||
}
|
||||
Ok(dict.into())
|
||||
}
|
||||
|
||||
/// Merge `dict` onto `dim`. With `creating`, geometry the subtype needs must
|
||||
/// be present so a script cannot silently default a point to the origin.
|
||||
fn dimension_merge(
|
||||
dim: &mut acadrust::entities::Dimension,
|
||||
dict: &rustpython_vm::builtins::PyDictRef,
|
||||
creating: bool,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let subtype = dimension_subtype_name(dim);
|
||||
let applicable: Vec<&'static str> =
|
||||
dimension_fields(&mut dim.clone()).into_iter().map(|(name, _)| name).collect();
|
||||
for key in dict.keys_vec() {
|
||||
let key = key.try_into_value::<String>(vm)?;
|
||||
let key = key.as_str();
|
||||
if matches!(key, "kind" | "handle" | "layer" | "owner_handle") || DIM_BASE_KEYS.contains(&key) {
|
||||
continue;
|
||||
}
|
||||
if key == "subtype" {
|
||||
let value = dict.get_item("subtype", vm)?.try_into_value::<String>(vm)?;
|
||||
if value != subtype {
|
||||
return Err(vm.new_value_error("Dimension.subtype is read-only".to_owned()));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if key == "actual_measurement" {
|
||||
return Err(vm.new_value_error("Dimension.actual_measurement is read-only".to_owned()));
|
||||
}
|
||||
if !applicable.contains(&key) {
|
||||
let known = [
|
||||
"definition_point", "first_point", "second_point", "angle_vertex", "dimension_arc",
|
||||
"feature_location", "leader_endpoint", "first_extension_point", "second_extension_point",
|
||||
"center_point", "first_leader_point", "second_leader_point", "chord_point",
|
||||
"override_center", "jog_point", "rotation", "ext_line_rotation", "leader_length",
|
||||
"arc_start_parameter", "arc_end_parameter", "jog_angle", "is_ordinate_type_x",
|
||||
"is_partial", "has_leader",
|
||||
];
|
||||
return Err(vm.new_value_error(if known.contains(&key) {
|
||||
format!("Dimension.{key} does not apply to the {subtype} subtype")
|
||||
} else {
|
||||
format!("ocs: Dimension property {key:?} is outside the editable schema")
|
||||
}));
|
||||
}
|
||||
}
|
||||
if creating {
|
||||
for name in dimension_required(subtype) {
|
||||
if dict.get_item_opt(*name, vm)?.is_none() {
|
||||
return Err(vm.new_value_error(format!("ocs.add: Dimension {subtype} requires {name}")));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (name, field) in dimension_fields(dim) {
|
||||
let Some(value) = dict.get_item_opt(name, vm)? else { continue };
|
||||
match field {
|
||||
DimField::V(target) => *target = py_to_vector3_dict(value, vm)?,
|
||||
DimField::F(target) => *target = py_number_to_f64(value, vm)?,
|
||||
DimField::B(target) => *target = value.try_into_value::<bool>(vm)?,
|
||||
}
|
||||
}
|
||||
let base = dimension_base_mut(dim);
|
||||
if let Some(v) = dict.get_item_opt("text", vm)? { base.text = v.try_into_value::<String>(vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("style_name", vm)? { base.style_name = v.try_into_value::<String>(vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("normal", vm)? { base.normal = py_to_vector3_dict(v, vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("text_middle_point", vm)? { base.text_middle_point = py_to_vector3_dict(v, vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("attachment_point", vm)? {
|
||||
base.attachment_point = parse_dim_attachment_point(&v.try_into_value::<String>(vm)?, vm)?;
|
||||
}
|
||||
if let Some(v) = dict.get_item_opt("text_rotation", vm)? { base.text_rotation = py_number_to_f64(v, vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("horizontal_direction", vm)? { base.horizontal_direction = py_number_to_f64(v, vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("flip_arrow1", vm)? { base.flip_arrow1 = v.try_into_value::<bool>(vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("flip_arrow2", vm)? { base.flip_arrow2 = v.try_into_value::<bool>(vm)?; }
|
||||
if let Some(v) = dict.get_item_opt("text_user_positioned", vm)? { base.text_user_positioned = v.try_into_value::<bool>(vm)?; }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dimension_from_dict(
|
||||
dict: &rustpython_vm::builtins::PyDictRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<acadrust::entities::Dimension> {
|
||||
use acadrust::entities::*;
|
||||
let subtype = match dict.get_item_opt("subtype", vm)? {
|
||||
Some(v) => v.try_into_value::<String>(vm)?,
|
||||
None => {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"ocs.add: Dimension requires subtype, one of {DIM_SUBTYPES:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
let mut dim = match subtype.as_str() {
|
||||
"Aligned" => Dimension::Aligned(DimensionAligned::default()),
|
||||
"Linear" => Dimension::Linear(DimensionLinear::default()),
|
||||
"Radius" => Dimension::Radius(DimensionRadius::default()),
|
||||
"Diameter" => Dimension::Diameter(DimensionDiameter::default()),
|
||||
"Angular2Ln" => Dimension::Angular2Ln(DimensionAngular2Ln::default()),
|
||||
"Angular3Pt" => Dimension::Angular3Pt(DimensionAngular3Pt::default()),
|
||||
"Ordinate" => Dimension::Ordinate(DimensionOrdinate::default()),
|
||||
"Arc" => Dimension::Arc(DimensionArc::default()),
|
||||
"LargeRadial" => Dimension::LargeRadial(DimensionLargeRadial::default()),
|
||||
other => {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"ocs.add: unsupported Dimension subtype {other:?}, expected one of {DIM_SUBTYPES:?}"
|
||||
)))
|
||||
}
|
||||
};
|
||||
dimension_merge(&mut dim, dict, true, vm)?;
|
||||
let base = dimension_base_mut(&mut dim);
|
||||
base.common.handle = acadrust::Handle::new(get_opt_u64(dict, "handle", vm)?);
|
||||
base.common.layer = get_opt_string(dict, "layer", "0", vm)?;
|
||||
base.common.owner_handle = acadrust::Handle::new(get_opt_u64(dict, "owner_handle", vm)?);
|
||||
Ok(dim)
|
||||
}
|
||||
|
||||
fn dimension_apply(
|
||||
existing: &acadrust::entities::Dimension,
|
||||
dict: &rustpython_vm::builtins::PyDictRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<acadrust::entities::Dimension> {
|
||||
let mut dim = existing.clone();
|
||||
dimension_merge(&mut dim, dict, false, vm)?;
|
||||
let base = dimension_base_mut(&mut dim);
|
||||
if let Some(v) = dict.get_item_opt("layer", vm)? {
|
||||
if !vm.is_none(&v) {
|
||||
base.common.layer = v.try_into_value::<String>(vm)?;
|
||||
}
|
||||
}
|
||||
if let Some(owner) = dict.get_item_opt("owner_handle", vm)? {
|
||||
if owner.try_into_value::<u64>(vm)? != base.common.owner_handle.value() {
|
||||
return Err(vm.new_value_error("owner_handle is read-only after creation".to_owned()));
|
||||
}
|
||||
}
|
||||
Ok(dim)
|
||||
}
|
||||
@@ -3127,10 +3127,15 @@ mod ocs {
|
||||
match dict.get_item_opt(key, vm)? {
|
||||
None => Ok(0.0),
|
||||
Some(v) if vm.is_none(&v) => Ok(0.0),
|
||||
Some(v) => v.try_into_value::<f64>(vm),
|
||||
Some(v) => py_number_to_f64(v, vm),
|
||||
}
|
||||
}
|
||||
|
||||
/// Accept a Python int or float; `try_into_value::<f64>` rejects ints.
|
||||
fn py_number_to_f64(value: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
Ok(value.try_float(vm)?.to_f64())
|
||||
}
|
||||
|
||||
fn get_opt_bool(
|
||||
dict: &rustpython_vm::builtins::PyDictRef,
|
||||
key: &str,
|
||||
@@ -3186,6 +3191,7 @@ mod ocs {
|
||||
}
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/entity_crud.rs"));
|
||||
include!("dimension_model.rs.inc");
|
||||
|
||||
// ════════════════════════════════════════════════════════════════════
|
||||
// Phase 1 — wiring the generic conversion functions above into `ocs`.
|
||||
@@ -4640,6 +4646,23 @@ mod tests {
|
||||
current = None;
|
||||
}
|
||||
}
|
||||
// Hand-written kinds: keys come from the include file's dict writes
|
||||
// and per-subtype field table.
|
||||
let manual = include_str!("dimension_model.rs.inc");
|
||||
let dimension_keys = emitted.entry("Dimension".to_owned()).or_default();
|
||||
for line in manual.lines() {
|
||||
let line = line.trim();
|
||||
let key = line
|
||||
.strip_prefix("dict.set_item(\"")
|
||||
.or_else(|| {
|
||||
(line.contains("\", V(") || line.contains("\", F(") || line.contains("\", B("))
|
||||
.then(|| line.strip_prefix("(\""))
|
||||
.flatten()
|
||||
});
|
||||
if let Some(key) = key {
|
||||
dimension_keys.insert(key.split('"').next().unwrap().to_owned());
|
||||
}
|
||||
}
|
||||
for entry in &catalog.entity_kinds {
|
||||
let expected: BTreeSet<_> = entry
|
||||
.properties
|
||||
@@ -4663,7 +4686,7 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
assert_eq!(emitted.len(), 24);
|
||||
assert_eq!(emitted.len(), 25);
|
||||
}
|
||||
|
||||
#[cfg(feature = "experimental-host-model")]
|
||||
|
||||
Reference in new issue
Block a user