Add RecordSeries::get_record and support for (my_ticks 0)

This commit introduces the `get_record` method to `RecordSeries`,
allowing for the dynamic reconstruction of a full record from its
constituent fields at a given index. This enables idiomatic access to
series data using indexing like `(my_ticks 0)`.

The `push-series` function has also been updated to correctly handle the
structure of records when pushing data into a `RecordSeries`.
This commit is contained in:
Michael Schimmel
2026-03-01 18:44:46 +01:00
parent b612df6e10
commit 46b546446f
3 changed files with 58 additions and 17 deletions
+34 -11
View File
@@ -251,6 +251,25 @@ impl RecordSeries {
pub fn field(&self, key: Keyword) -> Option<Rc<std::cell::RefCell<dyn SeriesMember>>> {
self.layout.index_of(key).map(|idx| self.fields[idx].clone())
}
/// Dynamically reconstructs the full Record at the given lookback index.
/// This is an O(N) operation where N is the number of fields, meaning it breaks
/// the 0-Copy performance path, but it's essential for idiomatic AST access `(series 0)`.
pub fn get_record(&self, index: usize) -> Option<Value> {
if self.fields.is_empty() {
return None;
}
let mut vals = Vec::with_capacity(self.fields.len());
for field in &self.fields {
match field.borrow().get_value(index) {
Some(v) => vals.push(v),
None => return None, // Out of bounds or incomplete
}
}
Some(Value::Record(self.layout.clone(), Rc::new(vals)))
}
}
impl Debug for RecordSeries {
@@ -356,19 +375,23 @@ pub fn register(env: &Environment) {
}
let (series_val, record_val) = (&args[0], &args[1]);
if let Value::Object(obj) = series_val {
if let Some(record_series) = obj.as_any().downcast_ref::<RecordSeries>() {
if let Value::Record(_, values) = record_val {
// Push each value into its corresponding column (SoA push)
for (i, field_val) in values.iter().enumerate() {
if let Some(field_series) = record_series.fields.get(i) {
field_series.borrow_mut().push_value(field_val.clone(), None);
}
let record_series = if let Value::Object(obj) = series_val {
obj.as_any().downcast_ref::<RecordSeries>()
} else {
None
};
if let Some(record_series) = record_series {
if let Value::Record(_, values) = record_val {
// Push each value into its corresponding column (SoA push)
for (i, field_val) in values.iter().enumerate() {
if let Some(field_series) = record_series.fields.get(i) {
field_series.borrow_mut().push_value(field_val.clone(), None);
}
return Value::Void;
} else {
panic!("push-series expects a record as the second argument");
}
return Value::Void;
} else {
panic!("push-series expects a record as the second argument");
}
}
panic!("push-series expects a RecordSeries as the first argument")