use std::cell::RefCell;
use std::rc::Rc;
use crate::oxml::shape::Sp as OxmlSp;
use crate::oxml::slidelayout::SldLayout as OxmlSldLayout;
#[derive(Debug, Clone)]
pub struct SlideLayoutRef {
#[allow(dead_code)]
pub(crate) idx: usize,
pub(crate) partname: String,
pub(crate) rid: String,
pub(crate) oxml: Rc<RefCell<OxmlSldLayout>>,
pub(crate) master_partname: String,
}
impl SlideLayoutRef {
pub fn partname(&self) -> &str {
&self.partname
}
pub fn rid(&self) -> &str {
&self.rid
}
pub fn name(&self) -> String {
self.oxml.borrow().name.clone()
}
pub fn set_name(&mut self, n: String) {
self.oxml.borrow_mut().name = n;
}
pub fn layout_type(&self) -> String {
self.oxml.borrow().type_.clone()
}
pub fn set_layout_type(&mut self, t: String) {
self.oxml.borrow_mut().type_ = t;
}
pub fn shapes(&self) -> Vec<OxmlSp> {
self.oxml.borrow().shapes.clone()
}
pub fn shapes_mut(&self) -> std::cell::RefMut<'_, Vec<OxmlSp>> {
std::cell::RefMut::map(self.oxml.borrow_mut(), |s| &mut s.shapes)
}
pub fn placeholders(&self) -> Vec<Placeholder> {
self.oxml
.borrow()
.shapes
.iter()
.filter(|s| s.is_placeholder)
.map(|s| Placeholder {
idx: s.ph_idx.unwrap_or(0),
ph_type: s.ph_type.clone().unwrap_or_else(|| "body".to_string()),
name: s.name.clone(),
})
.collect()
}
pub fn placeholder_indices(&self) -> Vec<usize> {
self.oxml
.borrow()
.shapes
.iter()
.enumerate()
.filter(|(_, s)| s.is_placeholder)
.map(|(i, _)| i)
.collect()
}
pub fn placeholder_index_by_ph_idx(&self, ph_idx: u32) -> Option<usize> {
self.oxml
.borrow()
.shapes
.iter()
.enumerate()
.find(|(_, s)| s.is_placeholder && s.ph_idx == Some(ph_idx))
.map(|(i, _)| i)
}
}
#[derive(Debug, Clone)]
pub struct Placeholder {
pub idx: u32,
pub ph_type: String,
pub name: String,
}
impl Placeholder {
pub fn ph_type(&self) -> &str {
&self.ph_type
}
pub fn idx(&self) -> u32 {
self.idx
}
pub fn name(&self) -> &str {
&self.name
}
}
#[derive(Debug, Default, Clone)]
pub struct SlideLayouts {
pub(crate) items: Vec<SlideLayoutRef>,
}
impl SlideLayouts {
pub fn new() -> Self {
SlideLayouts::default()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn iter(&self) -> std::slice::Iter<'_, SlideLayoutRef> {
self.items.iter()
}
pub fn get(&self, idx: usize) -> Option<&SlideLayoutRef> {
self.items.get(idx)
}
pub fn get_mut(&mut self, idx: usize) -> Option<&mut SlideLayoutRef> {
self.items.get_mut(idx)
}
pub fn at(&self, idx: usize) -> Option<SlideLayout> {
self.items.get(idx).map(|r| SlideLayout {
name: r.oxml.borrow().name.clone(),
r#type: r.oxml.borrow().type_.clone(),
})
}
pub fn push(&mut self, layout: SlideLayoutRef) {
self.items.push(layout);
}
pub fn remove(&mut self, idx: usize) -> Option<SlideLayoutRef> {
if idx < self.items.len() {
Some(self.items.remove(idx))
} else {
None
}
}
pub fn index_of(&self, rid: &str) -> Option<usize> {
self.items.iter().position(|l| l.rid == rid)
}
pub fn get_by_name(&self, name: &str) -> Option<&SlideLayoutRef> {
self.items.iter().find(|l| l.oxml.borrow().name == name)
}
pub fn get_by_name_mut(&mut self, name: &str) -> Option<&mut SlideLayoutRef> {
self.items.iter_mut().find(|l| l.oxml.borrow().name == name)
}
}
#[derive(Debug, Clone)]
pub struct SlideLayout {
pub name: String,
pub r#type: String,
}
#[cfg(test)]
#[allow(clippy::field_reassign_with_default)]
mod tests {
use super::*;
use std::cell::RefCell;
use std::rc::Rc;
fn make_layout(name: &str, rid: &str) -> SlideLayoutRef {
let oxml = OxmlSldLayout {
name: name.to_string(),
type_: "blank".to_string(),
shapes: Vec::new(),
};
SlideLayoutRef {
idx: 0,
partname: format!("/ppt/slideLayouts/{}.xml", name),
rid: rid.to_string(),
oxml: Rc::new(RefCell::new(oxml)),
master_partname: String::new(),
}
}
#[test]
fn layouts_remove_by_index() {
let mut layouts = SlideLayouts::new();
layouts.push(make_layout("Blank", "rId1"));
layouts.push(make_layout("Title", "rId2"));
layouts.push(make_layout("Content", "rId3"));
assert_eq!(layouts.len(), 3);
let removed = layouts.remove(1);
assert!(removed.is_some());
assert_eq!(removed.unwrap().rid(), "rId2");
assert_eq!(layouts.len(), 2);
assert_eq!(layouts.get(1).unwrap().rid(), "rId3");
assert!(layouts.remove(99).is_none());
}
#[test]
fn layouts_index_of_by_rid() {
let mut layouts = SlideLayouts::new();
layouts.push(make_layout("Blank", "rId1"));
layouts.push(make_layout("Title", "rId2"));
assert_eq!(layouts.index_of("rId1"), Some(0));
assert_eq!(layouts.index_of("rId2"), Some(1));
assert_eq!(layouts.index_of("rId999"), None);
}
#[test]
fn layouts_get_by_name() {
let mut layouts = SlideLayouts::new();
layouts.push(make_layout("Blank", "rId1"));
layouts.push(make_layout("Title Slide", "rId2"));
let found = layouts.get_by_name("Title Slide");
assert!(found.is_some());
assert_eq!(found.unwrap().rid(), "rId2");
assert!(layouts.get_by_name("Nonexistent").is_none());
}
#[test]
fn layout_placeholder_indices() {
let mut sp1 = OxmlSp::default();
sp1.is_placeholder = true;
sp1.ph_idx = Some(0);
sp1.ph_type = Some("title".into());
let mut sp2 = OxmlSp::default();
sp2.is_placeholder = false;
let mut sp3 = OxmlSp::default();
sp3.is_placeholder = true;
sp3.ph_idx = Some(1);
sp3.ph_type = Some("body".into());
let oxml = OxmlSldLayout {
name: "Test".into(),
type_: "obj".into(),
shapes: vec![sp1, sp2, sp3],
};
let layout = SlideLayoutRef {
idx: 0,
partname: "/ppt/slideLayouts/slideLayout1.xml".into(),
rid: "rId1".into(),
oxml: Rc::new(RefCell::new(oxml)),
master_partname: String::new(),
};
let indices = layout.placeholder_indices();
assert_eq!(indices, vec![0, 2]);
assert_eq!(layout.placeholder_index_by_ph_idx(0), Some(0));
assert_eq!(layout.placeholder_index_by_ph_idx(1), Some(2));
assert_eq!(layout.placeholder_index_by_ph_idx(99), None);
}
}