use super::extraction::BoundingBox;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "api", derive(utoipa::ToSchema))]
pub struct Table {
pub cells: Vec<Vec<String>>,
pub markdown: String,
pub page_number: usize,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub bounding_box: Option<BoundingBox>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "api", derive(utoipa::ToSchema))]
pub struct TableCell {
pub content: String,
#[serde(default = "default_span")]
pub row_span: usize,
#[serde(default = "default_span")]
pub col_span: usize,
#[serde(default)]
pub is_header: bool,
}
fn default_span() -> usize {
1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_table_with_bounding_box_serialization() {
let table = Table {
cells: vec![
vec!["A".to_string(), "B".to_string()],
vec!["C".to_string(), "D".to_string()],
],
markdown: "| A | B |\n|---|---|\n| C | D |".to_string(),
page_number: 1,
bounding_box: Some(BoundingBox {
x0: 50.0,
y0: 100.0,
x1: 500.0,
y1: 700.0,
}),
};
let json = serde_json::to_string(&table).unwrap();
assert!(json.contains("\"bounding_box\""));
assert!(json.contains("\"x0\":50.0"));
assert!(json.contains("\"y1\":700.0"));
let deserialized: Table = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.page_number, 1);
assert!(deserialized.bounding_box.is_some());
let bbox = deserialized.bounding_box.unwrap();
assert_eq!(bbox.x0, 50.0);
assert_eq!(bbox.y0, 100.0);
assert_eq!(bbox.x1, 500.0);
assert_eq!(bbox.y1, 700.0);
}
#[test]
fn test_table_without_bounding_box_serialization() {
let table = Table {
cells: vec![vec!["X".to_string()]],
markdown: "| X |".to_string(),
page_number: 2,
bounding_box: None,
};
let json = serde_json::to_string(&table).unwrap();
assert!(!json.contains("bounding_box"));
let deserialized: Table = serde_json::from_str(&json).unwrap();
assert!(deserialized.bounding_box.is_none());
}
#[test]
fn test_table_deserialization_without_bounding_box_field() {
let json = r#"{"cells":[["A","B"]],"markdown":"| A | B |","page_number":1}"#;
let table: Table = serde_json::from_str(json).unwrap();
assert!(table.bounding_box.is_none());
assert_eq!(table.page_number, 1);
}
#[test]
fn test_table_bounding_box_clone_and_debug() {
let table = Table {
cells: vec![],
markdown: String::new(),
page_number: 1,
bounding_box: Some(BoundingBox {
x0: 10.0,
y0: 20.0,
x1: 30.0,
y1: 40.0,
}),
};
let cloned = table.clone();
assert_eq!(cloned.bounding_box, table.bounding_box);
let debug = format!("{:?}", table);
assert!(debug.contains("bounding_box"));
}
#[test]
fn test_table_bounding_box_values_preserved() {
let original = Table {
cells: vec![
vec!["Header1".to_string(), "Header2".to_string()],
vec!["Val1".to_string(), "Val2".to_string()],
],
markdown: "| Header1 | Header2 |\n|---|---|\n| Val1 | Val2 |".to_string(),
page_number: 3,
bounding_box: Some(BoundingBox {
x0: 72.0,
y0: 200.5,
x1: 540.0,
y1: 600.75,
}),
};
let json_value = serde_json::to_value(&original).unwrap();
let deserialized: Table = serde_json::from_value(json_value).unwrap();
assert_eq!(deserialized.cells, original.cells);
assert_eq!(deserialized.markdown, original.markdown);
assert_eq!(deserialized.page_number, original.page_number);
assert_eq!(deserialized.bounding_box, original.bounding_box);
}
}