From ff0cbc3a137fac332b29a50fc1f5b954143ccc9c Mon Sep 17 00:00:00 2001 From: "Dylan M. Taylor" Date: Fri, 13 Nov 2026 09:32:56 -0500 Subject: [PATCH] Parse IDML index topics in shared model --- crates/idml/src/model/mod.rs | 5 +-- crates/idml/src/model/story.rs | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/crates/idml/src/model/mod.rs b/crates/idml/src/model/mod.rs index 54cf11e44..75d6f8074 100644 --- a/crates/idml/src/model/mod.rs +++ b/crates/idml/src/model/mod.rs @@ -46,8 +46,9 @@ pub use spread::{ }; pub use story::{ Ace, AnchoredObjectSetting, AnchoredObjectValues, CONTAINERS, Cell, CellOptions, Container, ContainerKind, ContentPart, - IndexCrossReferenceValues, IndexValues, PageReference, Range, SavedCrossReference, SavedCrossReferencePart, Story, StoryAnchor, StoryPreference, - Table, TableChild, TableColumn, TableLayout, TableRow, TextNode, TrackedChangeKind, stories, + IndexCrossReferenceValues, IndexDocument, IndexReference, IndexTopicPath, IndexValues, PageReference, Range, SavedCrossReference, + SavedCrossReferencePart, Story, StoryAnchor, StoryPreference, Table, TableChild, TableColumn, TableLayout, TableRow, TextNode, TrackedChangeKind, + stories, }; use crate::value::{matrix, nums}; diff --git a/crates/idml/src/model/story.rs b/crates/idml/src/model/story.rs index a5e1b23ff..2c8567aa3 100644 --- a/crates/idml/src/model/story.rs +++ b/crates/idml/src/model/story.rs @@ -2,7 +2,7 @@ //! anchored page items, hyperlink and change containers). use craft_xml::{El, Local, Node}; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, HashMap}; use super::{PageItem, Values, tidy}; @@ -206,6 +206,60 @@ pub struct IndexCrossReferenceValues { pub target: String, } +/// A topic path and its sort keys, as they are saved on nested IDML `Topic` elements. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct IndexTopicPath { + pub path: Vec, + pub sort: Vec, +} + +/// An IDML index cross-reference as stored under an index topic. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct IndexReference { + pub from: IndexTopicPath, + pub referenced_topic: Option, + pub reference_type: Option, +} + +/// Parsed IDML index topics and cross-references. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct IndexDocument { + pub topics: HashMap, + pub cross_references: Vec, +} + +impl IndexDocument { + pub fn from_el(element: &El) -> Self { + let mut result = Self::default(); + let mut stack: Vec<(&El, IndexTopicPath)> = + element.elements().filter(|child| child.local() == "Topic").map(|topic| (topic, IndexTopicPath::default())).collect(); + while let Some((topic, prefix)) = stack.pop() { + let name = topic.attr("Name").unwrap_or_default().to_string(); + let mut current = prefix.clone(); + current.path.push(name); + if let Some(key) = topic.attr("SortOrder").filter(|key| !key.is_empty()) { + current.sort.resize(prefix.path.len(), String::new()); + current.sort.push(key.to_string()); + } + if let Some(self_id) = topic.attr("Self") { + result.topics.insert(self_id.to_string(), current.clone()); + } + for child in topic.elements() { + if child.local() == "CrossReference" { + result.cross_references.push(IndexReference { + from: current.clone(), + referenced_topic: child.attr("ReferencedTopic").map(str::to_string), + reference_type: child.attr("CrossReferenceType").map(str::to_string), + }); + } else if child.local() == "Topic" { + stack.push((child, current.clone())); + } + } + } + result + } +} + impl IndexValues { /// Builds the IDML `Self` value for a topic path. pub fn topic_self(path: &[String]) -> String {