orgize/src/headline.rs

1220 lines
34 KiB
Rust

use indextree::NodeId;
use std::borrow::Cow;
use std::ops::RangeInclusive;
use std::usize;
use crate::{
config::ParseConfig,
elements::{Element, Title},
parsers::{parse_container, Container, OwnedArena},
validate::{ValidationError, ValidationResult},
Org,
};
/// Represents the document in `Org` struct.
///
/// Each `Org` struct only has one `Document`.
#[derive(Copy, Clone, Debug)]
pub struct Document {
doc_n: NodeId,
sec_n: Option<NodeId>,
}
impl Document {
pub(crate) fn from_org(org: &Org) -> Document {
let sec_n = org.arena[org.root]
.first_child()
.and_then(|n| match org[n] {
Element::Section => Some(n),
Element::Headline { .. } => None,
_ => unreachable!("Document should only contains section and headline."),
});
Document {
doc_n: org.root,
sec_n,
}
}
/// Returns the ID of the section element of this document,
/// or `None` if it has no section.
pub fn section_node(self) -> Option<NodeId> {
self.sec_n
}
/// Returns an iterator of this document's children.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// ** h1
/// ** h2
/// *** h2_1
/// *** h2_2
/// ** h3
/// "#,
/// );
///
/// let d = org.document();
///
/// let mut iter = d.children(&org);
///
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h1");
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h2");
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h3");
/// assert!(iter.next().is_none());
/// ```
pub fn children<'a>(self, org: &'a Org) -> impl Iterator<Item = Headline> + 'a {
self.doc_n
.children(&org.arena)
// skip section if exists
.skip(if self.sec_n.is_some() { 1 } else { 0 })
.map(move |n| match org[n] {
Element::Headline { level } => Headline::from_node(n, level, org),
_ => unreachable!(),
})
}
/// Returns the first child of this document, or `None` if it has no child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// ** h1
/// ** h2
/// *** h2_1
/// *** h2_2
/// ** h3
/// "#,
/// );
///
/// let d = org.document();
///
/// assert_eq!(d.first_child(&org).unwrap().title(&org).raw, "h1");
/// ```
///
/// ```rust
/// # use orgize::Org;
/// #
/// let org = Org::new();
///
/// assert!(org.document().first_child(&org).is_none());
/// ```
pub fn first_child(self, org: &Org) -> Option<Headline> {
self.doc_n
.children(&org.arena)
// skip section if exists
.nth(if self.sec_n.is_some() { 1 } else { 0 })
.map(move |n| match org[n] {
Element::Headline { level } => Headline::from_node(n, level, org),
_ => unreachable!(),
})
}
/// Returns the last child of this document, or `None` if it has no child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let d = org.document();
///
/// assert_eq!(d.last_child(&org).unwrap().title(&org).raw, "h1_3");
/// ```
///
/// ```rust
/// # use orgize::Org;
/// #
/// let org = Org::new();
///
/// assert!(org.document().last_child(&org).is_none());
/// ```
pub fn last_child(self, org: &Org) -> Option<Headline> {
org.arena[self.doc_n]
.last_child()
.and_then(|n| match org[n] {
Element::Headline { level } => Some(Headline::from_node(n, level, org)),
Element::Section => None,
_ => unreachable!("Document should only contains section and headline."),
})
}
/// Changes the section content of this document.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// ** h1_1
/// ** h1_2
/// "#,
/// );
///
/// let mut d = org.document();
///
/// d.set_section_content("s", &mut org);
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// s
/// ** h1_1
/// ** h1_2
/// "#,
/// );
/// ```
pub fn set_section_content<'a, S>(&mut self, content: S, org: &mut Org<'a>)
where
S: Into<Cow<'a, str>>,
{
if let Some(sec_n) = self.sec_n {
let children: Vec<_> = sec_n.children(&org.arena).collect();
for child in children {
child.detach(&mut org.arena);
}
} else {
let sec_n = org.arena.new_node(Element::Section);
self.sec_n = Some(sec_n);
self.doc_n.prepend(sec_n, &mut org.arena);
}
match content.into() {
Cow::Borrowed(content) => parse_container(
&mut org.arena,
Container::Block {
node: self.sec_n.unwrap(),
content,
},
&ParseConfig::default(),
),
Cow::Owned(ref content) => parse_container(
&mut OwnedArena::new(&mut org.arena),
Container::Block {
node: self.sec_n.unwrap(),
content,
},
&ParseConfig::default(),
),
}
org.debug_validate();
}
/// Appends a new child to this document.
///
/// Returns an error if the given new child was already attached,
/// or the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// ***** h1
/// **** h2
/// *** h3
/// "#,
/// );
///
/// let d = org.document();
///
/// let mut h4 = Headline::new(
/// Title {
/// raw: "h4".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be smaller than or equal to 3
/// h4.set_level(4, &mut org).unwrap();
/// assert!(d.append(h4, &mut org).is_err());
///
/// h4.set_level(2, &mut org).unwrap();
/// assert!(d.append(h4, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// ***** h1
/// **** h2
/// *** h3
/// ** h4
/// "#,
/// );
///
/// // cannot append an attached headline
/// assert!(d.append(h4, &mut org).is_err());
/// ```
pub fn append(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(last) = self.last_child(org) {
hdl.check_level(1..=last.lvl)?;
} else {
hdl.check_level(1..=usize::max_value())?;
}
self.doc_n.append(hdl.hdl_n, &mut org.arena);
org.debug_validate();
Ok(())
}
/// Prepends a new child to this document.
///
/// Returns an error if the given new child was already attached,
/// or the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// ** h2
/// ** h3
/// "#,
/// );
///
/// let d = org.document();
///
/// let mut h1 = Headline::new(
/// Title {
/// raw: "h1".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be greater than 2
/// h1.set_level(1, &mut org).unwrap();
/// assert!(d.prepend(h1, &mut org).is_err());
///
/// h1.set_level(4, &mut org).unwrap();
/// assert!(d.prepend(h1, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// **** h1
/// ** h2
/// ** h3
/// "#,
/// );
///
/// // cannot prepend an attached headline
/// assert!(d.prepend(h1, &mut org).is_err());
/// ```
pub fn prepend(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(first) = self.first_child(org) {
hdl.check_level(first.lvl..=usize::MAX)?;
} else {
hdl.check_level(1..=usize::MAX)?;
}
if let Some(sec_n) = self.sec_n {
sec_n.insert_after(hdl.hdl_n, &mut org.arena);
} else {
self.doc_n.prepend(hdl.hdl_n, &mut org.arena);
}
org.debug_validate();
Ok(())
}
}
/// Represents a headline in `Org` struct.
///
/// Each `Org` has zero or more `Headline`s.
#[derive(Copy, Clone, Debug)]
pub struct Headline {
lvl: usize,
hdl_n: NodeId,
ttl_n: NodeId,
sec_n: Option<NodeId>,
}
impl Headline {
/// Creates a new detached Headline.
pub fn new<'a>(ttl: Title<'a>, org: &mut Org<'a>) -> Headline {
let lvl = ttl.level;
let hdl_n = org.arena.new_node(Element::Headline { level: ttl.level });
let ttl_n = org.arena.new_node(Element::Document { pre_blank: 0 }); // placeholder
hdl_n.append(ttl_n, &mut org.arena);
match ttl.raw {
Cow::Borrowed(content) => parse_container(
&mut org.arena,
Container::Inline {
node: ttl_n,
content,
},
&ParseConfig::default(),
),
Cow::Owned(ref content) => parse_container(
&mut OwnedArena::new(&mut org.arena),
Container::Inline {
node: ttl_n,
content,
},
&ParseConfig::default(),
),
}
org[ttl_n] = Element::Title(ttl);
Headline {
lvl,
hdl_n,
ttl_n,
sec_n: None,
}
}
pub(crate) fn from_node(hdl_n: NodeId, lvl: usize, org: &Org) -> Headline {
let ttl_n = org.arena[hdl_n].first_child().unwrap();
let sec_n = org.arena[ttl_n].next_sibling().and_then(|n| match org[n] {
Element::Section => Some(n),
_ => None,
});
Headline {
lvl,
hdl_n,
ttl_n,
sec_n,
}
}
/// Returns the level of this headline.
pub fn level(self) -> usize {
self.lvl
}
/// Returns the ID of the headline element of this headline.
pub fn headline_node(self) -> NodeId {
self.hdl_n
}
/// Returns the ID of the title element of this headline.
pub fn title_node(self) -> NodeId {
self.ttl_n
}
/// Returns the ID of the section element of this headline, or `None` if it has no section.
pub fn section_node(self) -> Option<NodeId> {
self.sec_n
}
/// Returns a reference to the title element of this headline.
pub fn title<'a: 'b, 'b>(self, org: &'b Org<'a>) -> &'b Title<'a> {
match &org[self.ttl_n] {
Element::Title(title) => title,
_ => unreachable!(),
}
}
/// Returns a mutual reference to the title element of this headline.
///
/// Don't change the level and content of the `&mut Titile` directly.
/// Alternatively, uses [`Headline::set_level`] and [`Headline::set_title_content`].
///
/// [`Headline::set_level`]: #method.set_level
/// [`Headline::set_title_content`]: #method.set_title_content
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse("* h1");
///
/// let h1 = org.headlines().nth(0).unwrap();
///
/// h1.title_mut(&mut org).priority = Some('A');
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// "* [#A] h1\n",
/// );
/// ```
pub fn title_mut<'a: 'b, 'b>(self, org: &'b mut Org<'a>) -> &'b mut Title<'a> {
match &mut org[self.ttl_n] {
Element::Title(title) => title,
_ => unreachable!(),
}
}
/// Changes the level of this headline.
///
/// Returns an error if this headline is attached and the given new level
/// doesn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ****** h1_1
/// *** h1_2
/// ** h1_3
/// "#,
/// );
///
/// let mut h1_2 = org.headlines().nth(2).unwrap();
///
/// // level must be greater than or equal to 2, and smaller than or equal to 6
/// assert!(h1_2.set_level(42, &mut org).is_err());
///
/// assert!(h1_2.set_level(5, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// ****** h1_1
/// ***** h1_2
/// ** h1_3
/// "#,
/// );
///
/// // detached headline's levels can be changed freely
/// let mut new_headline = Headline::new(
/// Title {
/// raw: "new".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
/// new_headline.set_level(42, &mut org).unwrap();
/// ```
pub fn set_level(&mut self, lvl: usize, org: &mut Org) -> ValidationResult<()> {
if !self.is_detached(org) {
let min = self
.next(&org)
.or_else(|| self.parent(&org))
.map(|hdl| hdl.lvl)
.unwrap_or(1);
let max = self
.previous(&org)
.map(|hdl| hdl.lvl)
.unwrap_or(usize::max_value());
if !(min..=max).contains(&lvl) {
return Err(ValidationError::HeadlineLevelMismatch {
range: min..=max,
at: self.hdl_n,
});
}
}
self.lvl = lvl;
self.title_mut(org).level = lvl;
if let Element::Headline { level } = &mut org[self.hdl_n] {
*level = lvl;
}
Ok(())
}
/// Changes the title content of this headline.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// "#,
/// );
///
/// let h1 = org.headlines().nth(0).unwrap();
/// let h1_1 = org.headlines().nth(1).unwrap();
///
/// h1.set_title_content("H1", &mut org);
/// h1_1.set_title_content(String::from("*H1_1*"), &mut org);
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * H1
/// ** *H1_1*
/// "#,
/// );
/// ```
pub fn set_title_content<'a, S>(self, content: S, org: &mut Org<'a>)
where
S: Into<Cow<'a, str>>,
{
let content = content.into();
let children: Vec<_> = self.ttl_n.children(&org.arena).collect();
for child in children {
child.detach(&mut org.arena);
}
match &content {
Cow::Borrowed(content) => parse_container(
&mut org.arena,
Container::Inline {
node: self.ttl_n,
content,
},
&ParseConfig::default(),
),
Cow::Owned(ref content) => parse_container(
&mut OwnedArena::new(&mut org.arena),
Container::Inline {
node: self.ttl_n,
content,
},
&ParseConfig::default(),
),
}
self.title_mut(org).raw = content;
org.debug_validate();
}
/// Changes the section content of this headline.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// s1_1
/// "#,
/// );
///
/// let mut h1 = org.headlines().nth(0).unwrap();
/// let mut h1_1 = org.headlines().nth(1).unwrap();
///
/// h1.set_section_content("s1", &mut org);
/// h1_1.set_section_content(String::from("*s1_1*"), &mut org);
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// s1
/// ** h1_1
/// *s1_1*
/// "#,
/// );
/// ```
pub fn set_section_content<'a, S>(&mut self, content: S, org: &mut Org<'a>)
where
S: Into<Cow<'a, str>>,
{
if let Some(sec_n) = self.sec_n {
let children: Vec<_> = sec_n.children(&org.arena).collect();
for child in children {
child.detach(&mut org.arena);
}
} else {
let sec_n = org.arena.new_node(Element::Section);
self.sec_n = Some(sec_n);
self.ttl_n.insert_after(sec_n, &mut org.arena);
}
match content.into() {
Cow::Borrowed(content) => parse_container(
&mut org.arena,
Container::Block {
node: self.sec_n.unwrap(),
content,
},
&ParseConfig::default(),
),
Cow::Owned(ref content) => parse_container(
&mut OwnedArena::new(&mut org.arena),
Container::Block {
node: self.sec_n.unwrap(),
content,
},
&ParseConfig::default(),
),
}
org.debug_validate();
}
/// Returns the parent of this headline, or `None` if it is detached or attached to the document.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1 = org.headlines().nth(0).unwrap();
/// let h1_1 = org.headlines().nth(1).unwrap();
/// let h1_2_1 = org.headlines().nth(3).unwrap();
///
/// assert_eq!(h1_1.parent(&org).unwrap().title(&org).raw, "h1");
/// assert_eq!(h1_2_1.parent(&org).unwrap().title(&org).raw, "h1_2");
///
/// assert!(h1.parent(&org).is_none());
///
/// // detached headline have no parent
/// assert!(Headline::new(Title::default(), &mut org).parent(&org).is_none());
/// ```
pub fn parent(self, org: &Org) -> Option<Headline> {
org.arena[self.hdl_n].parent().and_then(|n| match org[n] {
Element::Headline { level } => Some(Headline::from_node(n, level, org)),
Element::Document { .. } => None,
_ => unreachable!(),
})
}
/// Returns an iterator of this headline's children.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1 = org.headlines().nth(0).unwrap();
///
/// let mut iter = h1.children(&org);
///
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h1_1");
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h1_2");
/// assert_eq!(iter.next().unwrap().title(&org).raw, "h1_3");
/// assert!(iter.next().is_none());
/// ```
pub fn children<'a>(self, org: &'a Org) -> impl Iterator<Item = Headline> + 'a {
self.hdl_n
.children(&org.arena)
// skip title and section
.skip(if self.sec_n.is_some() { 2 } else { 1 })
.filter_map(move |n| match org[n] {
Element::Headline { level } => Some(Headline::from_node(n, level, org)),
_ => unreachable!(),
})
}
/// Returns the first child of this headline, or `None` if it has no child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1_1 = org.headlines().nth(1).unwrap();
/// let h1_2 = org.headlines().nth(2).unwrap();
/// let h1_3 = org.headlines().nth(5).unwrap();
///
/// assert_eq!(h1_2.first_child(&org).unwrap().title(&org).raw, "h1_2_1");
///
/// assert!(h1_1.first_child(&org).is_none());
/// assert!(h1_3.first_child(&org).is_none());
/// ```
pub fn first_child(self, org: &Org) -> Option<Headline> {
self.hdl_n
.children(&org.arena)
// skip title and section
.nth(if self.sec_n.is_some() { 2 } else { 1 })
.map(|n| match org[n] {
Element::Headline { level } => Headline::from_node(n, level, org),
_ => unreachable!(),
})
}
/// Returns the last child of this headline, or `None` if it has no child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1_1 = org.headlines().nth(1).unwrap();
/// let h1_2 = org.headlines().nth(2).unwrap();
/// let h1_3 = org.headlines().nth(5).unwrap();
///
/// assert_eq!(h1_2.last_child(&org).unwrap().title(&org).raw, "h1_2_2");
///
/// assert!(h1_1.last_child(&org).is_none());
/// assert!(h1_3.last_child(&org).is_none());
/// ```
pub fn last_child(self, org: &Org) -> Option<Headline> {
org.arena[self.hdl_n]
.last_child()
.and_then(|n| match org[n] {
Element::Headline { level } => Some(Headline::from_node(n, level, org)),
Element::Section | Element::Title(_) => None,
_ => unreachable!("Headline should only contains section and headline."),
})
}
/// Returns the previous sibling of this headline, or `None` if it is a first child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1_1 = org.headlines().nth(1).unwrap();
/// let h1_2 = org.headlines().nth(2).unwrap();
/// let h1_2_1 = org.headlines().nth(3).unwrap();
///
/// assert_eq!(h1_2.previous(&org).unwrap().title(&org).raw, "h1_1");
///
/// assert!(h1_1.previous(&org).is_none());
/// assert!(h1_2_1.previous(&org).is_none());
/// ```
pub fn previous(self, org: &Org) -> Option<Headline> {
org.arena[self.hdl_n]
.previous_sibling()
.and_then(|n| match org[n] {
Element::Headline { level } => Some(Headline::from_node(n, level, org)),
Element::Title(_) | Element::Section => None,
_ => unreachable!(),
})
}
/// Returns the next sibling of this headline, or `None` if it is a last child.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1_2 = org.headlines().nth(2).unwrap();
/// let h1_2_2 = org.headlines().nth(4).unwrap();
/// let h1_3 = org.headlines().nth(5).unwrap();
///
/// assert_eq!(h1_2.next(&org).unwrap().title(&org).raw, "h1_3");
///
/// assert!(h1_3.next(&org).is_none());
/// assert!(h1_2_2.next(&org).is_none());
/// ```
pub fn next(self, org: &Org) -> Option<Headline> {
org.arena[self.hdl_n].next_sibling().map(|n| match org[n] {
Element::Headline { level } => Headline::from_node(n, level, org),
_ => unreachable!(),
})
}
/// Detaches this headline from arena.
///
/// ```rust
/// # use orgize::Org;
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ** h1_2
/// *** h1_2_1
/// *** h1_2_2
/// ** h1_3
/// "#,
/// );
///
/// let h1_2 = org.headlines().nth(2).unwrap();
///
/// h1_2.detach(&mut org);
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// ** h1_1
/// ** h1_3
/// "#,
/// );
/// ```
pub fn detach(self, org: &mut Org) {
self.hdl_n.detach(&mut org.arena);
}
/// Returns `true` if this headline is detached.
pub fn is_detached(self, org: &Org) -> bool {
org.arena[self.hdl_n].parent().is_none()
}
/// Appends a new child to this headline.
///
/// Returns an error if the given new child was already attached, or
/// the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ***** h1_1_1
/// "#,
/// );
///
/// let h1_1 = org.headlines().nth(1).unwrap();
///
/// let mut h1_1_2 = Headline::new(
/// Title {
/// raw: "h1_1_2".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be greater than 2, and smaller than or equal to 5
/// h1_1_2.set_level(2, &mut org).unwrap();
/// assert!(h1_1.append(h1_1_2, &mut org).is_err());
/// h1_1_2.set_level(6, &mut org).unwrap();
/// assert!(h1_1.append(h1_1_2, &mut org).is_err());
///
/// h1_1_2.set_level(4, &mut org).unwrap();
/// assert!(h1_1.append(h1_1_2, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// ** h1_1
/// ***** h1_1_1
/// **** h1_1_2
/// "#,
/// );
///
/// // cannot append an attached headline
/// assert!(h1_1.append(h1_1_2, &mut org).is_err());
/// ```
pub fn append(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(last) = self.last_child(org) {
hdl.check_level(self.lvl + 1..=last.lvl)?;
} else {
hdl.check_level(self.lvl + 1..=usize::MAX)?;
}
self.hdl_n.append(hdl.hdl_n, &mut org.arena);
org.debug_validate();
Ok(())
}
/// Prepends a new child to this headline.
///
/// Returns an error if the given new child was already attached, or
/// the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// ***** h1_1_1
/// "#,
/// );
///
/// let h1_1 = org.headlines().nth(1).unwrap();
///
/// let mut h1_1_2 = Headline::new(
/// Title {
/// raw: "h1_1_2".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be greater than or equal to 5
/// h1_1_2.set_level(2, &mut org).unwrap();
/// assert!(h1_1.prepend(h1_1_2, &mut org).is_err());
///
/// h1_1_2.set_level(5, &mut org).unwrap();
/// assert!(h1_1.prepend(h1_1_2, &mut org).is_ok());
///
/// // cannot prepend an attached headline
/// assert!(h1_1.prepend(h1_1_2, &mut org).is_err());
/// ```
pub fn prepend(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(first) = self.first_child(org) {
hdl.check_level(first.lvl..=usize::MAX)?;
} else {
hdl.check_level(self.lvl + 1..=usize::MAX)?;
}
self.sec_n
.unwrap_or(self.ttl_n)
.insert_after(hdl.hdl_n, &mut org.arena);
org.debug_validate();
Ok(())
}
/// Inserts a new sibling before this headline.
///
/// Returns an error if the given new child was already attached, or
/// the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// **** h1_1_1
/// *** h1_1_3
/// "#,
/// );
///
/// let h1_1_3 = org.headlines().nth(3).unwrap();
///
/// let mut h1_1_2 = Headline::new(
/// Title {
/// raw: "h1_1_2".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be greater than or equal to 3, but smaller than or equal to 4
/// h1_1_2.set_level(2, &mut org).unwrap();
/// assert!(h1_1_3.insert_before(h1_1_2, &mut org).is_err());
/// h1_1_2.set_level(5, &mut org).unwrap();
/// assert!(h1_1_3.insert_before(h1_1_2, &mut org).is_err());
///
/// h1_1_2.set_level(4, &mut org).unwrap();
/// assert!(h1_1_3.insert_before(h1_1_2, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// ** h1_1
/// **** h1_1_1
/// **** h1_1_2
/// *** h1_1_3
/// "#,
/// );
///
/// // cannot insert an attached headline
/// assert!(h1_1_3.insert_before(h1_1_2, &mut org).is_err());
/// ```
pub fn insert_before(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(previous) = self.previous(org) {
hdl.check_level(self.lvl..=previous.lvl)?;
} else {
hdl.check_level(self.lvl..=usize::MAX)?;
}
self.hdl_n.insert_before(hdl.hdl_n, &mut org.arena);
org.debug_validate();
Ok(())
}
/// Inserts a new sibling after this headline.
///
/// Returns an error if the given new child was already attached, or
/// the given new child didn't meet the requirements.
///
/// ```rust
/// # use orgize::{elements::Title, Headline, Org};
/// #
/// let mut org = Org::parse(
/// r#"
/// * h1
/// ** h1_1
/// **** h1_1_1
/// *** h1_1_3
/// "#,
/// );
///
/// let h1_1_1 = org.headlines().nth(2).unwrap();
///
/// let mut h1_1_2 = Headline::new(
/// Title {
/// raw: "h1_1_2".into(),
/// ..Default::default()
/// },
/// &mut org,
/// );
///
/// // level must be greater than or equal to 3, but smaller than or equal to 4
/// h1_1_2.set_level(2, &mut org).unwrap();
/// assert!(h1_1_1.insert_after(h1_1_2, &mut org).is_err());
/// h1_1_2.set_level(5, &mut org).unwrap();
/// assert!(h1_1_1.insert_after(h1_1_2, &mut org).is_err());
///
/// h1_1_2.set_level(4, &mut org).unwrap();
/// assert!(h1_1_1.insert_after(h1_1_2, &mut org).is_ok());
///
/// let mut writer = Vec::new();
/// org.write_org(&mut writer).unwrap();
/// assert_eq!(
/// String::from_utf8(writer).unwrap(),
/// r#"
/// * h1
/// ** h1_1
/// **** h1_1_1
/// **** h1_1_2
/// *** h1_1_3
/// "#,
/// );
///
/// // cannot insert an attached headline
/// assert!(h1_1_1.insert_after(h1_1_2, &mut org).is_err());
/// ```
pub fn insert_after(self, hdl: Headline, org: &mut Org) -> ValidationResult<()> {
hdl.check_detached(org)?;
if let Some(next) = self.next(org) {
hdl.check_level(next.lvl..=self.lvl)?;
} else if let Some(parent) = self.parent(org) {
hdl.check_level(parent.lvl + 1..=self.lvl)?;
} else {
hdl.check_level(1..=self.lvl)?;
}
self.hdl_n.insert_after(hdl.hdl_n, &mut org.arena);
org.debug_validate();
Ok(())
}
fn check_detached(self, org: &Org) -> ValidationResult<()> {
if !self.is_detached(org) {
Err(ValidationError::ExpectedDetached { at: self.hdl_n })
} else {
Ok(())
}
}
fn check_level(self, range: RangeInclusive<usize>) -> ValidationResult<()> {
if !range.contains(&self.lvl) {
Err(ValidationError::HeadlineLevelMismatch {
range,
at: self.hdl_n,
})
} else {
Ok(())
}
}
}
impl Org<'_> {
/// Returns the `Document`.
pub fn document(&self) -> Document {
Document::from_org(self)
}
/// Returns an iterator of `Headline`s.
pub fn headlines(&self) -> impl Iterator<Item = Headline> + '_ {
self.root
.descendants(&self.arena)
.skip(1)
.filter_map(move |node| match self[node] {
Element::Headline { level } => Some(Headline::from_node(node, level, self)),
_ => None,
})
}
}