use tablo_core::dom::row;
use super::{Attrs, attr_value, elements, is_selected, open_tags, strip_text};
use crate::protocol::controls::{DisabledAction, disabled_actions};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Row {
pub select_value: Option<String>,
pub cells: Vec<String>,
pub actions: RowActions,
pub links: Vec<String>,
pub copies: Vec<String>,
pub color: Option<String>,
pub edits: Vec<CellEdit>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CellEdit {
pub column: String,
pub action: String,
pub value: Option<String>,
pub version: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RowActions {
pub view: Option<String>,
pub edit: Option<String>,
pub delete_action: Option<String>,
pub links: Vec<String>,
pub disabled: Vec<DisabledAction>,
}
pub fn rows(html: &str) -> Vec<Row> {
let mut out = Vec::new();
for (_, attrs, inner) in elements(html, "tr") {
let Some(id) = attr_value(&attrs, "id") else {
continue;
};
if !id.starts_with("row-") {
continue;
}
out.push(row(&attrs, &inner));
}
out
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Group {
pub label: String,
pub rows: Vec<Row>,
}
pub fn groups(html: &str) -> Vec<Group> {
let mut out: Vec<Group> = Vec::new();
for (_, attrs, inner) in elements(html, "tr") {
if let Some(label) = attr_value(&attrs, "data-group") {
out.push(Group {
label,
rows: Vec::new(),
});
} else if attr_value(&attrs, "id").is_some_and(|id| id.starts_with("row-"))
&& let Some(group) = out.last_mut()
{
group.rows.push(row(&attrs, &inner));
}
}
out
}
pub fn row_keys(html: &str) -> Vec<String> {
rows(html)
.into_iter()
.filter_map(|row| row.select_value)
.collect()
}
pub fn row_actions(html: &str, key: &str) -> Option<RowActions> {
rows(html).into_iter().find_map(|row| {
let matches = row.select_value.as_deref() == Some(key)
|| row
.actions
.view
.as_deref()
.is_some_and(|href| href_contains_key(href, key))
|| row
.actions
.edit
.as_deref()
.is_some_and(|href| href_contains_key(href, key))
|| row
.actions
.delete_action
.as_deref()
.is_some_and(|action| href_contains_key(action, key));
matches.then_some(row.actions)
})
}
fn row(row_attrs: &Attrs, row_html: &str) -> Row {
let mut select_value = None;
for (tag, attrs, _) in open_tags(row_html) {
if tag == "input" && attr_value(&attrs, "data-row-select").is_some() {
select_value = attr_value(&attrs, "value");
break;
}
}
let mut cells = Vec::new();
for (_, _, inner) in elements(row_html, "td") {
cells.push(strip_text(&inner));
}
let mut view_href = None;
let mut edit_href = None;
let mut delete_action = None;
let mut row_links = Vec::new();
let mut links = Vec::new();
let mut copies = Vec::new();
for (_, attrs, _) in open_tags(row_html) {
if attr_value(&attrs, "data-cell-link").is_some() {
links.extend(attr_value(&attrs, "href"));
}
if let Some(value) = attr_value(&attrs, "data-copy") {
copies.push(value);
}
match attr_value(&attrs, "data-row-action").as_deref() {
Some(row::VIEW) => view_href = attr_value(&attrs, "href"),
Some(row::EDIT) => edit_href = attr_value(&attrs, "href"),
Some(row::DELETE) => delete_action = attr_value(&attrs, "formaction"),
Some(row::LINK) => row_links.extend(attr_value(&attrs, "href")),
_ => {}
}
}
Row {
select_value,
cells,
actions: RowActions {
view: view_href,
edit: edit_href,
delete_action,
links: row_links,
disabled: disabled_actions(row_html),
},
links,
copies,
color: attr_value(row_attrs, "data-row-color"),
edits: cell_edits(row_html),
}
}
fn cell_edits(row_html: &str) -> Vec<CellEdit> {
elements(row_html, "form")
.into_iter()
.filter_map(|(_, attrs, inner)| {
let column = attr_value(&attrs, "data-cell-edit")?;
let action = attr_value(&attrs, "action").unwrap_or_default();
let mut value = None;
let mut version = None;
for (tag, attrs, _) in open_tags(&inner) {
let name = attr_value(&attrs, "name");
let kind = attr_value(&attrs, "type");
match (tag.as_str(), name.as_deref(), kind.as_deref()) {
("input", Some("record_version"), _) => version = attr_value(&attrs, "value"),
("input", Some(name), Some("checkbox")) if name == column => {
let checked = attrs.iter().any(|(attr, _)| attr == "checked");
value = Some(checked.to_string());
}
("input", Some(name), kind) if name == column && kind != Some("hidden") => {
value = attr_value(&attrs, "value");
}
_ => {}
}
}
for (_, attrs, _) in open_tags(&inner) {
if is_selected(&attrs) {
value = attr_value(&attrs, "value");
}
}
Some(CellEdit {
column,
action,
value,
version,
})
})
.collect()
}
fn href_contains_key(href: &str, key: &str) -> bool {
href.split(['?', '#', '/', '&'])
.any(|segment| segment == key)
}