use crate::core::{Core, failed};
use crate::event::Event;
use crate::failure::FailureCode;
use crate::port::{DriverCapabilities, PortError};
use crate::schema::FieldValue;
impl Core {
pub(crate) fn insert_row(
&mut self,
table: &str,
values: &[crate::schema::FieldValue],
) -> Vec<Event> {
let Some(db) = self.state.database.as_ref() else {
return failed(
FailureCode::NoDatabase,
"no database is open — open a database first",
);
};
if !db.capabilities.write_rows {
return failed(
FailureCode::Capability(crate::availability::DisabledReason::WriteRows),
DriverCapabilities::unsupported(&db.driver, "write rows for this source"),
);
}
let Some(def) = db
.tables
.iter()
.find(|t| t.name.eq_ignore_ascii_case(table))
else {
return failed(
FailureCode::NotFound,
PortError::TableNotFound(table.to_owned()).to_string(),
);
};
let canonical = def.name.clone();
let prepared = match crate::field_expressions::prepare_insert(def, values) {
Ok(prepared) => prepared,
Err(reason) => return failed(FailureCode::InvalidInput, reason),
};
match self.port.insert_row(&canonical, &prepared) {
Err(e) => failed(FailureCode::Port, e.to_string()),
Ok(()) => {
self.record_insert(&canonical);
vec![Event::RowInserted { table: canonical }]
}
}
}
pub(crate) fn update_row(
&mut self,
table: &str,
row: usize,
values: &[FieldValue],
) -> Vec<Event> {
let Some(db) = self.state.database.as_ref() else {
return failed(
FailureCode::NoDatabase,
"no database is open — open a database first",
);
};
if !db.capabilities.write_rows {
return failed(
FailureCode::Capability(crate::availability::DisabledReason::WriteRows),
DriverCapabilities::unsupported(&db.driver, "write rows for this source"),
);
}
let Some(def) = db
.tables
.iter()
.find(|t| t.name.eq_ignore_ascii_case(table))
else {
return failed(
FailureCode::NotFound,
PortError::TableNotFound(table.to_owned()).to_string(),
);
};
let canonical = def.name.clone();
let fields = def.fields.clone();
if values.len() != fields.len() {
return failed(
FailureCode::InvalidInput,
format!(
"update {:?}: expected {} values, found {}",
canonical,
fields.len(),
values.len()
),
);
}
let current = match self
.port
.read_rows(&canonical, crate::port::RowRange { start: row, len: 1 })
{
Ok(rows) if rows.len() == 1 => rows.into_iter().next().expect("length checked"),
Ok(_) => {
return failed(
FailureCode::OutOfRange,
format!("row {row} of {canonical:?} is out of range"),
);
}
Err(e) => return failed(FailureCode::Port, e.to_string()),
};
let prepared = match crate::field_expressions::prepare_update(def, ¤t, values) {
Ok(prepared) => prepared,
Err(reason) => return failed(FailureCode::InvalidInput, reason),
};
match self.port.update_row(&canonical, row, &prepared) {
Err(e) => failed(FailureCode::Port, e.to_string()),
Ok(()) => {
self.record_update(&canonical, ¤t, &prepared);
vec![Event::RowUpdated {
table: canonical,
row,
}]
}
}
}
pub(crate) fn delete_row(&mut self, table: &str, row: usize) -> Vec<Event> {
let Some(db) = self.state.database.as_ref() else {
return failed(
FailureCode::NoDatabase,
"no database is open — open a database first",
);
};
if !db.capabilities.write_rows {
return failed(
FailureCode::Capability(crate::availability::DisabledReason::WriteRows),
DriverCapabilities::unsupported(&db.driver, "write rows for this source"),
);
}
let Some(def) = db
.tables
.iter()
.find(|t| t.name.eq_ignore_ascii_case(table))
else {
return failed(
FailureCode::NotFound,
PortError::TableNotFound(table.to_owned()).to_string(),
);
};
let canonical = def.name.clone();
let current = match self
.port
.read_rows(&canonical, crate::port::RowRange { start: row, len: 1 })
{
Ok(rows) if rows.len() == 1 => rows.into_iter().next().expect("length checked"),
Ok(_) => {
return failed(
FailureCode::OutOfRange,
format!("row {row} of {canonical:?} is out of range"),
);
}
Err(e) => return failed(FailureCode::Port, e.to_string()),
};
match self.port.delete_row(&canonical, row) {
Err(e) => failed(FailureCode::Port, e.to_string()),
Ok(()) => {
self.record_delete(&canonical, ¤t);
vec![Event::RowDeleted {
table: canonical,
row,
}]
}
}
}
}