headless_lms_mcp/engine/course_structure/
mod.rs1mod editor;
5mod fields;
6mod model;
7mod outline;
8mod paths;
9
10use std::collections::{BTreeMap, HashMap};
11
12use headless_lms_models::url_redirections::CreatedRedirect;
13use headless_lms_utils::document_schema_processor::GutenbergBlock;
14use serde::Serialize;
15use uuid::Uuid;
16
17use editor::StructureEditor;
18pub use model::{CourseStructure, StructureChapter, StructureModule, StructurePage};
19pub(crate) use model::{EntityRef, ListRef, Loc, RENDERED_KEYS, parse_structure_path};
20pub use outline::{StructureOutline, outline};
21pub use paths::LinkToOldPath;
22use paths::find_links_to_old_paths;
23
24use super::error::{EditError, Warning};
25use super::op::Op;
26use super::{CreatedIds, PageRef};
27use crate::ids::IdGenerator;
28
29#[derive(Debug, Clone, PartialEq, Serialize)]
31pub struct ChapterRenumber {
32 pub chapter_id: Uuid,
33 pub from: i32,
34 pub to: i32,
35}
36
37#[derive(Debug, Clone, PartialEq, Serialize)]
39pub struct PageCopy {
40 pub source_page_id: Uuid,
41 pub new_page_id: Uuid,
42}
43
44#[derive(Debug, Clone, PartialEq)]
46pub struct StructureResult {
47 pub structure: CourseStructure,
48 pub created: Vec<CreatedIds>,
49 pub labels: BTreeMap<String, Uuid>,
50 pub warnings: Vec<Warning>,
51 pub changed_paths: Vec<CreatedRedirect>,
54 pub renumbered_chapters: Vec<ChapterRenumber>,
55 pub new_pages: HashMap<Uuid, Vec<GutenbergBlock>>,
57 pub page_copies: Vec<PageCopy>,
59 pub page_pairings: BTreeMap<Uuid, Uuid>,
62}
63
64impl StructureResult {
65 pub fn links_to_old_paths(&self, pages: &[PageRef], course_slug: &str) -> Vec<LinkToOldPath> {
67 find_links_to_old_paths(pages, course_slug, &self.changed_paths, &self.structure)
68 }
69}
70
71pub fn apply(
74 doc: &CourseStructure,
75 ops: &[Op],
76 course_is_live: bool,
77 ids: &dyn IdGenerator,
78) -> Result<StructureResult, EditError> {
79 let mut editor = StructureEditor::new(doc, ops, course_is_live, ids);
80 for (index, op) in ops.iter().enumerate() {
81 editor.ids.set_op(index);
82 let first_warning = editor.warnings.len();
83 editor.apply_op(op).map_err(|e| e.at_op(index))?;
84 for warning in &mut editor.warnings[first_warning..] {
85 warning.op = Some(index);
86 }
87 }
88 editor.finish(doc)
89}
90
91#[cfg(test)]
92mod tests;