///|
/// Append a text-named field to a struct, keeping its field order and annotations.
///
/// Returns `None` when `value` is not a struct (after unwrapping annotations).
pub fn struct_append_field(
value : @model.IonValue,
name : String,
field_value : @model.IonValue,
) -> @model.IonValue? {
match value {
@model.IonValue::Annotated(annotations, nested) =>
match struct_append_field(nested, name, field_value) {
Some(updated) => Some(@model.IonValue::Annotated(annotations, updated))
None => None
}
@model.IonValue::Struct(fields) => {
let updated : Array[(@model.IonSymbol, @model.IonValue)] = []
for field in fields {
updated.push(field)
}
updated.push((@model.IonSymbol::from_text(name), field_value))
Some(@model.IonValue::Struct(updated))
}
_ => None
}
}
///|
/// Replace the zero-based matching occurrence of a text-named struct field.
///
/// The existing field symbol is retained, so symbol IDs and field order stay intact.
/// Returns `None` for a non-struct, a negative occurrence, or no matching field.
pub fn struct_replace_field(
value : @model.IonValue,
name : String,
occurrence : Int,
field_value : @model.IonValue,
) -> @model.IonValue? {
if occurrence < 0 {
return None
}
match value {
@model.IonValue::Annotated(annotations, nested) =>
match struct_replace_field(nested, name, occurrence, field_value) {
Some(updated) => Some(@model.IonValue::Annotated(annotations, updated))
None => None
}
@model.IonValue::Struct(fields) => {
let updated : Array[(@model.IonSymbol, @model.IonValue)] = []
let mut matching = 0
let mut replaced = false
for field in fields {
match field.0.text_value() {
Some(field_name) if field_name == name && matching == occurrence => {
updated.push((field.0, field_value))
matching += 1
replaced = true
}
Some(field_name) if field_name == name => {
updated.push(field)
matching += 1
}
_ => updated.push(field)
}
}
if replaced {
Some(@model.IonValue::Struct(updated))
} else {
None
}
}
_ => None
}
}
///|
/// Remove the zero-based matching occurrence of a text-named struct field.
///
/// Other duplicate fields retain their original relative order. Returns `None` when
/// the requested occurrence is absent, negative, or `value` is not a struct.
pub fn struct_remove_field(
value : @model.IonValue,
name : String,
occurrence : Int,
) -> @model.IonValue? {
if occurrence < 0 {
return None
}
match value {
@model.IonValue::Annotated(annotations, nested) =>
match struct_remove_field(nested, name, occurrence) {
Some(updated) => Some(@model.IonValue::Annotated(annotations, updated))
None => None
}
@model.IonValue::Struct(fields) => {
let updated : Array[(@model.IonSymbol, @model.IonValue)] = []
let mut matching = 0
let mut removed = false
for field in fields {
match field.0.text_value() {
Some(field_name) if field_name == name && matching == occurrence => {
matching += 1
removed = true
}
Some(field_name) if field_name == name => {
updated.push(field)
matching += 1
}
_ => updated.push(field)
}
}
if removed {
Some(@model.IonValue::Struct(updated))
} else {
None
}
}
_ => None
}
}
///|
/// Append an element to a list, preserving any annotations on the list.
///
/// Returns `None` when `value` is not a list; s-expressions are intentionally not
/// edited by this list-specific operation.
pub fn list_append(
value : @model.IonValue,
element : @model.IonValue,
) -> @model.IonValue? {
match value {
@model.IonValue::Annotated(annotations, nested) =>
match list_append(nested, element) {
Some(updated) => Some(@model.IonValue::Annotated(annotations, updated))
None => None
}
@model.IonValue::List(elements) => {
let updated : Array[@model.IonValue] = []
for item in elements {
updated.push(item)
}
updated.push(element)
Some(@model.IonValue::List(updated))
}
_ => None
}
}
///|
/// Replace a zero-based list element without changing its length or annotations.
///
/// Returns `None` for a non-list or an out-of-bounds (including negative) index.
pub fn list_replace(
value : @model.IonValue,
index : Int,
replacement : @model.IonValue,
) -> @model.IonValue? {
if index < 0 {
return None
}
match value {
@model.IonValue::Annotated(annotations, nested) =>
match list_replace(nested, index, replacement) {
Some(updated) => Some(@model.IonValue::Annotated(annotations, updated))
None => None
}
@model.IonValue::List(elements) => {
let updated : Array[@model.IonValue] = []
let mut replaced = false
for position, item in elements {
if position == index {
updated.push(replacement)
replaced = true
} else {
updated.push(item)
}
}
if replaced {
Some(@model.IonValue::List(updated))
} else {
None
}
}
_ => None
}
}