use crate::executor::VirtualMachine;
use shape_runtime::context::ExecutionContext;
use shape_value::{HeapKind, KindedSlot, NativeKind, VMError};
#[inline]
fn surface(method: &str) -> VMError {
VMError::NotImplemented(format!(
"phase-2c — Content.{}(): MethodHandler ABI needs kinded migration \
(cluster E-builtins-backlog, Wave 5b template); receiver kind \
NativeKind::Ptr(HeapKind::Content), dispatch via \
slot.as_heap_value() + HeapValue::Content match per ADR-006 \
§2.7.6 / Q8. Runtime delegators (border/series/title/etc.) also \
depend on the shape-runtime crate-boundary kinded redesign per \
playbook §8 cross-cluster cascade.",
method
))
}
#[inline]
fn recv_content<'a>(args: &'a [KindedSlot], method: &str) -> Result<&'a shape_value::content::ContentNode, VMError> {
if args.is_empty() {
return Err(VMError::RuntimeError(format!(
"Content.{}(): no receiver",
method
)));
}
if args[0].kind != NativeKind::Ptr(HeapKind::Content) {
return Err(VMError::RuntimeError(format!(
"Content.{}(): expected Content receiver, got kind {:?}",
method, args[0].kind
)));
}
let bits = args[0].slot.raw();
if bits == 0 {
return Err(VMError::RuntimeError(format!(
"Content.{}(): null Content receiver",
method
)));
}
let node: &shape_value::content::ContentNode =
unsafe { &*(bits as *const shape_value::content::ContentNode) };
Ok(node)
}
#[inline]
fn content_slot(node: shape_value::content::ContentNode) -> KindedSlot {
KindedSlot::from_content(std::sync::Arc::new(node))
}
#[inline]
fn parse_border_style(s: &str) -> Result<shape_value::content::BorderStyle, VMError> {
use shape_value::content::BorderStyle;
match s.to_ascii_lowercase().as_str() {
"rounded" => Ok(BorderStyle::Rounded),
"sharp" => Ok(BorderStyle::Sharp),
"heavy" => Ok(BorderStyle::Heavy),
"double" => Ok(BorderStyle::Double),
"minimal" => Ok(BorderStyle::Minimal),
"none" => Ok(BorderStyle::None),
other => Err(VMError::RuntimeError(format!(
"Content.border(): unknown border style '{}' — expected one of \
rounded / sharp / heavy / double / minimal / none",
other
))),
}
}
pub fn v2_content_bold(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("bold"))
}
pub fn v2_content_italic(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("italic"))
}
pub fn v2_content_underline(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("underline"))
}
pub fn v2_content_dim(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("dim"))
}
pub fn v2_content_fg(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("fg"))
}
pub fn v2_content_bg(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("bg"))
}
pub fn v2_content_to_string(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("toString"))
}
pub fn v2_content_border(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "border")?;
if args.len() != 2 {
return Err(VMError::RuntimeError(format!(
"Content.border(style) requires exactly 1 argument, got {}",
args.len().saturating_sub(1)
)));
}
let style_str = args[1].as_str().ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.border(): style argument must be a string, got kind {:?}",
args[1].kind
))
})?;
let style = parse_border_style(style_str)?;
match node {
shape_value::content::ContentNode::Table(t) => {
let mut new_table = t.clone();
new_table.border = style;
Ok(content_slot(shape_value::content::ContentNode::Table(
new_table,
)))
}
other => Err(VMError::RuntimeError(format!(
"Content.border() is only valid on Table receivers (got {})",
describe_content_variant(other)
))),
}
}
#[inline]
fn describe_content_variant(node: &shape_value::content::ContentNode) -> &'static str {
match node {
shape_value::content::ContentNode::Text(_) => "Text",
shape_value::content::ContentNode::Table(_) => "Table",
shape_value::content::ContentNode::Code { .. } => "Code",
shape_value::content::ContentNode::Chart(_) => "Chart",
shape_value::content::ContentNode::KeyValue(_) => "KeyValue",
shape_value::content::ContentNode::Fragment(_) => "Fragment",
}
}
pub fn v2_content_headers(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "headers")?;
if args.len() != 2 {
return Err(VMError::RuntimeError(format!(
"Content.headers(arr) requires exactly 1 argument, got {}",
args.len().saturating_sub(1)
)));
}
let headers = crate::executor::vm_impl::builtins::read_string_array(&args[1])
.ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.headers(): argument must be Array<string>, got kind \
{:?}",
args[1].kind
))
})?;
match node {
shape_value::content::ContentNode::Table(t) => {
let mut new_table = t.clone();
new_table.headers = headers;
Ok(content_slot(shape_value::content::ContentNode::Table(
new_table,
)))
}
other => Err(VMError::RuntimeError(format!(
"Content.headers() is only valid on Table receivers (got {})",
describe_content_variant(other)
))),
}
}
pub fn v2_content_row(
vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "row")?;
if args.len() != 2 {
return Err(VMError::RuntimeError(format!(
"Content.row(arr) requires exactly 1 argument, got {}",
args.len().saturating_sub(1)
)));
}
use crate::executor::v2_handlers::v2_array_detect::{
as_v2_typed_array, read_element,
};
if args[1].kind != NativeKind::Ptr(HeapKind::TypedArray) {
return Err(VMError::RuntimeError(format!(
"Content.row(): cells argument must be an Array, got kind {:?}",
args[1].kind
)));
}
let view = as_v2_typed_array(args[1].slot.raw(), args[1].kind)
.ok_or_else(|| {
VMError::RuntimeError(
"Content.row(): cells array has invalid v2 header".to_string(),
)
})?;
let formatter = crate::executor::printing::ValueFormatter::new(
&vm.program.type_schema_registry,
);
let mut cells: Vec<shape_value::content::ContentNode> =
Vec::with_capacity(view.len as usize);
for i in 0..view.len {
let (bits, kind) = read_element(&view, i).ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.row(): failed to read cell at index {}",
i
))
})?;
let cell_slot = KindedSlot::new(shape_value::ValueSlot::from_raw(bits), kind);
let rendered = formatter.format_kinded(&cell_slot);
drop(cell_slot);
cells.push(shape_value::content::ContentNode::plain(rendered));
}
match node {
shape_value::content::ContentNode::Table(t) => {
let mut new_table = t.clone();
new_table.rows.push(cells);
Ok(content_slot(shape_value::content::ContentNode::Table(
new_table,
)))
}
other => Err(VMError::RuntimeError(format!(
"Content.row() is only valid on Table receivers (got {})",
describe_content_variant(other)
))),
}
}
pub fn v2_content_language(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "language")?;
if args.len() != 2 {
return Err(VMError::RuntimeError(format!(
"Content.language(s) requires exactly 1 argument, got {}",
args.len().saturating_sub(1)
)));
}
let lang = args[1].as_str().ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.language(): argument must be a string, got kind {:?}",
args[1].kind
))
})?;
match node {
shape_value::content::ContentNode::Code { source, .. } => {
Ok(content_slot(shape_value::content::ContentNode::Code {
language: Some(lang.to_string()),
source: source.clone(),
}))
}
other => Err(VMError::RuntimeError(format!(
"Content.language() is only valid on Code receivers (got {})",
describe_content_variant(other)
))),
}
}
pub fn v2_content_source(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "source")?;
if args.len() != 2 {
return Err(VMError::RuntimeError(format!(
"Content.source(s) requires exactly 1 argument, got {}",
args.len().saturating_sub(1)
)));
}
let src = args[1].as_str().ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.source(): argument must be a string, got kind {:?}",
args[1].kind
))
})?;
match node {
shape_value::content::ContentNode::Code { language, .. } => {
Ok(content_slot(shape_value::content::ContentNode::Code {
language: language.clone(),
source: src.to_string(),
}))
}
other => Err(VMError::RuntimeError(format!(
"Content.source() is only valid on Code receivers (got {})",
describe_content_variant(other)
))),
}
}
pub fn v2_content_pair(
vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "pair")?;
if args.len() != 3 {
return Err(VMError::RuntimeError(format!(
"Content.pair(key, value) requires exactly 2 arguments, got {}",
args.len().saturating_sub(1)
)));
}
let key = args[1].as_str().ok_or_else(|| {
VMError::RuntimeError(format!(
"Content.pair(): key argument must be a string, got kind {:?}",
args[1].kind
))
})?;
let formatter = crate::executor::printing::ValueFormatter::new(
&vm.program.type_schema_registry,
);
let rendered = formatter.format_kinded(&args[2]);
let value_node = shape_value::content::ContentNode::plain(rendered);
match node {
shape_value::content::ContentNode::KeyValue(pairs) => {
let mut new_pairs = pairs.clone();
new_pairs.push((key.to_string(), value_node));
Ok(content_slot(
shape_value::content::ContentNode::KeyValue(new_pairs),
))
}
other => Err(VMError::RuntimeError(format!(
"Content.pair() is only valid on KeyValue receivers (got {})",
describe_content_variant(other)
))),
}
}
pub fn v2_content_build(
_vm: &mut VirtualMachine,
args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
let node = recv_content(args, "build")?;
Ok(content_slot(node.clone()))
}
pub fn v2_content_max_rows(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("max_rows"))
}
pub fn v2_content_max_rows_camel(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("maxRows"))
}
pub fn v2_content_series(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("series"))
}
pub fn v2_content_title(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("title"))
}
pub fn v2_content_x_label(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("x_label"))
}
pub fn v2_content_x_label_camel(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("xLabel"))
}
pub fn v2_content_y_label(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("y_label"))
}
pub fn v2_content_y_label_camel(
_vm: &mut VirtualMachine,
_args: &[KindedSlot],
_ctx: Option<&mut ExecutionContext>,
) -> Result<KindedSlot, VMError> {
Err(surface("yLabel"))
}