pub spv_info: Option<Box<SpvInfo>>,
}
+pub trait ItemInfo {
+ fn label(&self) -> Cow<'static, str>;
+ fn command_name(&self) -> Option<&str>;
+ fn subtype(&self) -> Option<String>;
+ fn is_shown(&self) -> bool;
+ fn spv_info(&self) -> Option<&SpvInfo>;
+ fn iter_in_order(&self) -> ItemRefIterator<'_, Self>
+ where
+ Self: Sized;
+ fn kind(&self) -> ItemKind;
+ fn class(&self) -> Class {
+ match self.kind() {
+ ItemKind::Graph => Class::Graphs,
+ ItemKind::Image => Class::Other,
+ ItemKind::Heading => Class::OutlineHeaders,
+ ItemKind::Message(severity) => match severity {
+ Severity::Note => Class::Notes,
+ Severity::Error | Severity::Warning => Class::Warnings,
+ },
+ ItemKind::PageBreak => Class::Other,
+ ItemKind::Table => match self.label().as_ref() {
+ "Warnings" => Class::Warnings,
+ "Notes" => Class::Notes,
+ _ => Class::Tables,
+ },
+ ItemKind::Text => match self.label().as_ref() {
+ "Title" => Class::Headings,
+ "Log" => Class::Logs,
+ "Page Title" => Class::PageTitle,
+ _ => Class::Texts,
+ },
+ }
+ }
+
+ type Child: AsRef<Self>;
+ fn children(&self) -> &[Self::Child];
+}
+
+impl ItemInfo for Item {
+ fn label(&self) -> Cow<'static, str> {
+ match &self.label {
+ Some(label) => Cow::from(label.clone()),
+ None => self.details.label(),
+ }
+ }
+
+ fn command_name(&self) -> Option<&str> {
+ self.command_name.as_deref()
+ }
+
+ fn subtype(&self) -> Option<String> {
+ self.details
+ .as_table()
+ .map(|table| table.subtype().display(table).to_string())
+ }
+
+ /// Should the item be shown?
+ ///
+ /// This always returns true for headings because their contents are always
+ /// shown (although headings can be collapsed in an outline view).
+ fn is_shown(&self) -> bool {
+ self.details.is_heading() || self.show
+ }
+
+ fn spv_info(&self) -> Option<&SpvInfo> {
+ self.spv_info.as_deref()
+ }
+
+ fn iter_in_order(&self) -> ItemRefIterator<'_, Item> {
+ ItemRefIterator::new(self)
+ }
+
+ type Child = Arc<Item>;
+ fn children(&self) -> &[Self::Child] {
+ self.details.children()
+ }
+
+ fn kind(&self) -> ItemKind {
+ self.details.kind()
+ }
+}
+
impl Item {
pub fn new(details: impl Into<Details>) -> Self {
let details = details.into();
}
}
- pub fn label(&self) -> Cow<'static, str> {
- match &self.label {
- Some(label) => Cow::from(label.clone()),
- None => self.details.label(),
- }
- }
-
pub fn subtype(&self) -> Option<String> {
self.details
.as_table()
}
}
- /// Should the item be shown?
- ///
- /// This always returns true for headings because their contents are always
- /// shown (although headings can be collapsed in an outline view).
- pub fn is_shown(&self) -> bool {
- self.details.is_heading() || self.show
- }
-
- pub fn iter_in_order(&self) -> ItemRefIterator<'_> {
- ItemRefIterator::new(self)
+ pub fn cursor(self: Arc<Self>) -> ItemCursor<Self> {
+ ItemCursor::new(self)
}
}
Graph,
Image,
Heading,
- Message,
+ Message(Severity),
PageBreak,
Table,
Text,
ItemKind::Graph => "graph",
ItemKind::Image => "image",
ItemKind::Heading => "heading",
- ItemKind::Message => "message",
+ ItemKind::Message(_) => "message",
ItemKind::PageBreak => "page break",
ItemKind::Table => "table",
ItemKind::Text => "text",
Details::Graph => ItemKind::Graph,
Details::Image(_) => ItemKind::Image,
Details::Heading(_) => ItemKind::Heading,
- Details::Message(_) => ItemKind::Message,
+ Details::Message(diagnostic) => ItemKind::Message(diagnostic.severity),
Details::PageBreak => ItemKind::PageBreak,
Details::Table(_) => ItemKind::Table,
Details::Text(_) => ItemKind::Text,
}
}
-pub struct ItemRefIterator<'a> {
- next: Option<&'a Item>,
- stack: Vec<(&'a Item, usize)>,
+pub struct ItemRefIterator<'a, T> {
+ next: Option<&'a T>,
+ stack: Vec<(&'a T, usize)>,
}
-impl<'a> ItemRefIterator<'a> {
- pub fn without_hidden(self) -> impl Iterator<Item = &'a Item> {
+impl<'a, T> ItemRefIterator<'a, T> {
+ pub fn without_hidden(self) -> impl Iterator<Item = &'a T>
+ where
+ T: ItemInfo,
+ {
self.filter(|item| item.is_shown())
}
- pub fn new(start: &'a Item) -> Self {
+ pub fn new(start: &'a T) -> Self {
Self {
next: Some(start),
stack: Vec::new(),
}
}
-impl<'a> Iterator for ItemRefIterator<'a> {
- type Item = &'a Item;
+impl<'a, T> Iterator for ItemRefIterator<'a, T>
+where
+ T: ItemInfo,
+{
+ type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
let cur = self.next.take()?;
- if let Some(first_child) = cur.details.children().first() {
- self.next = Some(&*first_child);
+ if let Some(first_child) = cur.children().first() {
+ self.next = Some(first_child.as_ref());
self.stack.push((cur, 1));
} else {
while let Some((item, index)) = self.stack.pop() {
- if let Some(child) = item.details.children().get(index) {
- self.next = Some(&*child);
+ if let Some(child) = item.children().get(index) {
+ self.next = Some(child.as_ref());
self.stack.push((item, index + 1));
return Some(cur);
}
}
}
-pub struct ItemCursor {
- cur: Option<Arc<Item>>,
- stack: Vec<(Arc<Item>, usize)>,
+pub struct ItemCursor<T>
+where
+ T: ItemInfo,
+ T::Child: Clone,
+{
+ cur: Option<T::Child>,
+ stack: Vec<(T::Child, usize)>,
include_hidden: bool,
}
-impl ItemCursor {
- pub fn new(start: Arc<Item>) -> Self {
+impl<T> ItemCursor<T>
+where
+ T: ItemInfo,
+ T::Child: Clone,
+{
+ pub fn new(start: T::Child) -> Self {
Self {
- cur: start.is_shown().then_some(start),
+ cur: start.as_ref().is_shown().then_some(start),
stack: Vec::new(),
include_hidden: false,
}
}
- pub fn with_hidden(start: Arc<Item>) -> Self {
+ pub fn with_hidden(start: T::Child) -> Self {
Self {
cur: Some(start),
stack: Vec::new(),
}
}
- pub fn cur(&self) -> Option<&Arc<Item>> {
+ pub fn cur(&self) -> Option<&T::Child> {
self.cur.as_ref()
}
pub fn next(&mut self) {
- fn inner(this: &mut ItemCursor) {
+ fn inner<T>(this: &mut ItemCursor<T>)
+ where
+ T: ItemInfo,
+ T::Child: Clone,
+ {
let Some(cur) = this.cur.take() else {
return;
};
- if let Some(first_child) = cur.details.children().first() {
+ if let Some(first_child) = cur.as_ref().children().first() {
this.cur = Some(first_child.clone());
this.stack.push((cur, 1));
} else {
while let Some((item, index)) = this.stack.pop() {
- if let Some(child) = item.details.children().get(index) {
+ if let Some(child) = item.as_ref().children().get(index) {
this.cur = Some(child.clone());
this.stack.push((item, index + 1));
return;
inner(self);
while let Some(cur) = &self.cur
- && !cur.is_shown()
+ && !cur.as_ref().is_shown()
{
inner(self);
}
// Returns the label for the heading with the given `level` in the stack
// above the current item. Level 0 is the top level. Levels without a
// label are skipped.
- pub fn heading(&self, level: usize) -> Option<&str> {
+ pub fn heading(&self, level: usize) -> Option<Cow<'static, str>> {
self.stack
.iter()
- .filter_map(|(item, _index)| item.label.as_ref())
+ .map(|(item, _index)| item.as_ref().label())
+ .filter(|label| !label.is_empty())
.nth(level)
- .map(|s| s.as_str())
}
}
}
}
-impl Item {
- fn class(&self) -> Class {
- let label = self.label.as_ref().map(|s| s.as_str());
- match &self.details {
- Details::Graph => Class::Graphs,
- Details::Image(_) => Class::Other,
- Details::Heading(_) => Class::OutlineHeaders,
- Details::Message(diagnostic) => match diagnostic.severity {
- Severity::Note => Class::Notes,
- Severity::Error | Severity::Warning => Class::Warnings,
- },
- Details::PageBreak => Class::Other,
- Details::Table(_) => match label {
- Some("Warnings") => Class::Warnings,
- Some("Notes") => Class::Notes,
- _ => Class::Tables,
- },
- Details::Text(_) => match label {
- Some("Title") => Class::Headings,
- Some("Log") => Class::Logs,
- Some("Page Title") => Class::PageTitle,
- _ => Class::Texts,
- },
- }
- }
-}
-
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Selection {
/// - `None`: Include all objects.