use crate::bindings as ll_bindings;
use crate::sys;
use crate::SizeType;
use crate::{tsk_id_t, tsk_size_t, ProvenanceId};
#[derive(Eq, Debug)]
pub struct ProvenanceTableRow {
pub id: ProvenanceId,
pub timestamp: String,
pub record: String,
}
impl PartialEq for ProvenanceTableRow {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}
impl std::fmt::Display for ProvenanceTableRow {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"id: {}, timestamp: {}, record: {}",
self.id, self.timestamp, self.record,
)
}
}
fn make_provenance_row(table: &ProvenanceTable, pos: tsk_id_t) -> Option<ProvenanceTableRow> {
Some(ProvenanceTableRow {
id: pos.into(),
timestamp: table.timestamp(pos)?.to_string(),
record: table.record(pos)?.to_string(),
})
}
type ProvenanceTableRefIterator<'a> = crate::table_iterator::TableIterator<&'a ProvenanceTable>;
type ProvenanceTableIterator = crate::table_iterator::TableIterator<ProvenanceTable>;
impl<'a> Iterator for ProvenanceTableRefIterator<'a> {
type Item = ProvenanceTableRow;
fn next(&mut self) -> Option<Self::Item> {
let rv = make_provenance_row(self.table, self.pos);
self.pos += 1;
rv
}
}
impl Iterator for ProvenanceTableIterator {
type Item = ProvenanceTableRow;
fn next(&mut self) -> Option<Self::Item> {
let rv = make_provenance_row(&self.table, self.pos);
self.pos += 1;
rv
}
}
#[derive(Debug)]
pub struct ProvenanceTableRowView<'a> {
table: &'a ProvenanceTable,
pub id: ProvenanceId,
pub timestamp: &'a str,
pub record: &'a str,
}
impl<'a> ProvenanceTableRowView<'a> {
fn new(table: &'a ProvenanceTable) -> Self {
Self {
table,
id: ProvenanceId::NULL,
timestamp: "",
record: "",
}
}
}
impl<'a> PartialEq for ProvenanceTableRowView<'a> {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}
impl Eq for ProvenanceTableRowView<'_> {}
impl<'a> PartialEq<ProvenanceTableRow> for ProvenanceTableRowView<'a> {
fn eq(&self, other: &ProvenanceTableRow) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}
impl PartialEq<ProvenanceTableRowView<'_>> for ProvenanceTableRow {
fn eq(&self, other: &ProvenanceTableRowView) -> bool {
self.id == other.id && self.timestamp == other.timestamp && self.record == other.record
}
}
impl<'a> streaming_iterator::StreamingIterator for ProvenanceTableRowView<'a> {
type Item = Self;
row_lending_iterator_get!();
fn advance(&mut self) {
self.id = (i32::from(self.id) + 1).into();
self.record = self.table.record(self.id).unwrap_or("");
self.timestamp = self.table.timestamp(self.id).unwrap_or("");
}
}
#[derive(Debug)]
#[repr(transparent)]
pub struct ProvenanceTable {
table_: sys::LLProvenanceTableRef,
}
impl ProvenanceTable {
pub(crate) fn new_from_table(
provenances: *mut ll_bindings::tsk_provenance_table_t,
) -> Result<Self, crate::TskitError> {
let table_ = sys::LLProvenanceTableRef::new_from_table(provenances)?;
Ok(ProvenanceTable { table_ })
}
pub(crate) fn as_ref(&self) -> &ll_bindings::tsk_provenance_table_t {
self.table_.as_ref()
}
pub fn num_rows(&self) -> SizeType {
self.as_ref().num_rows.into()
}
pub fn timestamp<P: Into<ProvenanceId> + Copy>(&self, row: P) -> Option<&str> {
let timestamp_slice = sys::tsk_ragged_column_access(
row.into(),
self.as_ref().timestamp,
self.num_rows(),
self.as_ref().timestamp_offset,
self.as_ref().timestamp_length,
);
match timestamp_slice {
Some(tstamp) => std::str::from_utf8(tstamp).ok(),
None => None,
}
}
pub fn record<P: Into<ProvenanceId> + Copy>(&self, row: P) -> Option<&str> {
let record_slice = sys::tsk_ragged_column_access(
row.into(),
self.as_ref().record,
self.num_rows(),
self.as_ref().record_offset,
self.as_ref().record_length,
);
match record_slice {
Some(rec) => std::str::from_utf8(rec).ok(),
None => None,
}
}
pub fn row<P: Into<ProvenanceId> + Copy>(&self, row: P) -> Option<ProvenanceTableRow> {
make_provenance_row(self, row.into().into())
}
pub fn row_view<P: Into<ProvenanceId> + Copy>(&self, row: P) -> Option<ProvenanceTableRowView> {
match row.into().to_usize() {
Some(x) if (x as u64) < self.num_rows() => {
let view = ProvenanceTableRowView {
table: self,
id: row.into(),
record: self.record(row)?,
timestamp: self.timestamp(row)?,
};
Some(view)
}
_ => None,
}
}
pub fn iter(&self) -> impl Iterator<Item = ProvenanceTableRow> + '_ {
crate::table_iterator::make_table_iterator::<&ProvenanceTable>(self)
}
pub fn lending_iter(&self) -> ProvenanceTableRowView {
ProvenanceTableRowView::new(self)
}
}
build_owned_table_type!(
=> OwningProvenanceTable,
ProvenanceTable,
crate::sys::LLOwningProvenanceTable,
crate::bindings::tsk_provenance_table_t
);
impl OwningProvenanceTable {
provenance_table_add_row!(=> add_row, self, self.as_mut_ptr());
}
#[cfg(test)]
mod test_provenances {
use streaming_iterator::StreamingIterator;
#[test]
fn test_empty_record_string() {
let mut tables = crate::TableCollection::new(10.).unwrap();
let s = String::from("");
assert!(tables.add_provenance(&s).is_err());
tables.build_index().unwrap();
let mut ts = tables
.tree_sequence(crate::TreeSequenceFlags::default())
.unwrap();
assert!(ts.add_provenance(&s).is_err())
}
#[test]
fn test_add_rows() {
let records = vec!["banana".to_string(), "split".to_string()];
let mut tables = crate::TableCollection::new(10.).unwrap();
for (i, r) in records.iter().enumerate() {
let row_id = tables.add_provenance(r).unwrap();
assert!(row_id == i as crate::tsk_id_t);
assert_eq!(tables.provenances().record(row_id).unwrap(), *r);
}
assert_eq!(
usize::try_from(tables.provenances().num_rows()).unwrap(),
records.len()
);
for (i, row) in tables.provenances_iter().enumerate() {
assert_eq!(records[i], row.record);
}
for (i, row) in tables.provenances().iter().enumerate() {
assert_eq!(records[i], row.record);
}
assert!(tables.provenances().row(0).unwrap() == tables.provenances().row(0).unwrap());
assert!(tables.provenances().row(0).unwrap() != tables.provenances().row(1).unwrap());
let mut lending_iter = tables.provenances().lending_iter();
for i in [0, 1] {
if let Some(row) = lending_iter.next() {
assert_eq!(row.record, &records[i]);
let owned_row = tables.provenances().row(i as i32).unwrap();
assert_eq!(row, &owned_row);
assert_eq!(&owned_row, row);
}
}
}
}