ocs_doc_api: Phase 5-ii — typed Table create

- CreateTable(TableSpec{insertion_point, data[row][col]}) op (append-only).
- backend add_table via Table::new(insertion_point, rows, cols) + set_cell_text
  (Table::default() pre-creates 3x3, so use Table::new for exact dims).
- executor validates non-empty rectangular grid; facade create_table.
- spec Table constructor; api_reference + binding_schema regenerated; mocks + baseline.
- Test phase5ii_create_table_from_grid (3x2 grid, non-rectangular rejected)
  -> 615 host lib + 3 consistency green. Phase 5 complete.
This commit is contained in:
Sebastian committed 2026-09-06 13:02:21 +02:00
1 parent f8ae54a351
commit 240831edcd
12 files changed
+117 -7

No files matched your search

+3
View File
@@ -75,6 +75,9 @@ pub trait DocApiBackend {
/// Add an `ATTDEF` (in-block attribute definition); returns the fresh `ObjectId`.
fn add_attribute_definition(&mut self, spec: &crate::ops::AttributeDefinitionSpec) -> ApiResult<ObjectId>;
/// Add a `TABLE` (rows × columns grid); returns the fresh `ObjectId`.
fn add_table(&mut self, spec: &crate::ops::TableSpec) -> ApiResult<ObjectId>;
/// A dimension's measured value (distance for linear/radius, degrees for angular).
fn dimension_measurement(&self, id: ObjectId) -> ApiResult<f64>;
+11
View File
@@ -293,6 +293,17 @@ pub fn apply_op<B: DocApiBackend>(b: &mut B, op: Operation) -> ApiResult<Receipt
b.finalize_op();
OpOutcome::NewId(id)
}
Operation::CreateTable(spec) => {
// Validate a non-empty, rectangular grid before committing.
let cols = spec.data.first().map(|r| r.len()).unwrap_or(0);
if spec.data.is_empty() || cols == 0 || spec.data.iter().any(|r| r.len() != cols) {
return Err(ApiError::validation(name, "table needs a non-empty rectangular grid"));
}
b.push_undo(name);
let id = b.add_table(spec)?;
b.finalize_op();
OpOutcome::NewId(id)
}
};
Ok(Receipt {
outcome: Some(outcome),
+10
View File
@@ -631,6 +631,16 @@ impl EntityCollection {
Ok(Entity::new(self.session.clone(), id))
}
/// Create a TABLE from a grid of cell strings (`data[row][column]`), at a point.
pub fn create_table(&self, insertion_point: [f64; 3], data: &[Vec<String>]) -> ApiResult<Entity> {
let receipt = self.session.apply_op(Operation::CreateTable(crate::ops::TableSpec {
insertion_point,
data: data.to_vec(),
}))?;
let id = receipt.outcome.and_then(|o| o.new_id()).ok_or_else(|| ApiError::Transport("create_table returned no id".into()))?;
Ok(Entity::new(self.session.clone(), id))
}
/// Create an ATTDEF (in-block attribute definition): tag/prompt/default at a point.
pub fn create_attribute_definition(&self, tag: &str, prompt: &str, default_value: &str, insertion_point: [f64; 3], height: f64, rotation: f64) -> ApiResult<Entity> {
let receipt = self.session.apply_op(Operation::CreateAttributeDefinition(crate::ops::AttributeDefinitionSpec {
+5 -1
View File
@@ -185,7 +185,11 @@ Raster image: create_raster_image(file_path, insertion, u, v, size) places an im
## `Entity` (acadrust `Table`, collection `entities`)
Table (read-only): GetEntity kind; generic delete. No typed create/bounds in v1.
Table: create_table(insertion_point, data[row][col]) builds rows x columns of text cells; non-rectangular grid -> Validation; delete.
### Constructors (`doc.entities()`)
- **create_table**(insertion_point: [f64; 3], data: &[Vec<String>]) -> `Entity` — `CreateTable`
## Generic methods (every handle)
+3 -1
View File
@@ -7,7 +7,7 @@
use serde::{Deserialize, Serialize};
use crate::id::ObjectId;
use crate::ops::{AttributeDefinitionSpec, BoolOp, Curve2Spec, DimensionAngularSpec, DimensionRadialSpec, DimensionSpec, EntitySpec, HatchSpec, InsertSpec, MTextSpec, PlacementSpec, RasterImageSpec, SolidPrimitive, TextSpec, ViewportSpec};
use crate::ops::{AttributeDefinitionSpec, BoolOp, Curve2Spec, DimensionAngularSpec, DimensionRadialSpec, DimensionSpec, EntitySpec, HatchSpec, InsertSpec, MTextSpec, PlacementSpec, RasterImageSpec, SolidPrimitive, TableSpec, TextSpec, ViewportSpec};
/// A typed write operation (plan §5). Each variant is ONE atomic host call = one
/// undo step. Append new variants at the END only.
@@ -54,4 +54,6 @@ pub enum Operation {
CreateDimensionAngular(DimensionAngularSpec),
// attribute definitions
CreateAttributeDefinition(AttributeDefinitionSpec),
// tables
CreateTable(TableSpec),
}
+10
View File
@@ -148,6 +148,15 @@ pub struct AttributeDefinitionSpec {
pub rotation: f64,
}
/// Construction spec for a `TABLE`: an insertion point plus a grid of cell
/// strings (`data[row][column]`).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TableSpec {
pub insertion_point: [f64; 3],
/// Grid of cell text values, rows × columns.
pub data: Vec<Vec<String>>,
}
/// Construction spec for a `RASTER_IMAGE`: an image file placed at
/// `insertion_point` with per-pixel world-unit vectors `u_vector`/`v_vector` and
/// pixel `size` (width, height). The host auto-registers the ImageDefinition.
@@ -223,6 +232,7 @@ impl crate::gen::Operation {
CreateDimensionDiameter(_) => "CreateDimensionDiameter",
CreateDimensionAngular(_) => "CreateDimensionAngular",
CreateAttributeDefinition(_) => "CreateAttributeDefinition",
CreateTable(_) => "CreateTable",
}
}
}