1use anyhow::{Context, Result};
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Clone, Serialize)]
16pub struct ConfluenceSearchResult {
17 pub id: String,
19 pub title: String,
21 pub space_key: String,
23}
24
25#[derive(Debug, Clone, Serialize)]
28pub struct ConfluenceSearchResults {
29 pub results: Vec<ConfluenceSearchResult>,
31 pub total: u32,
33}
34
35#[derive(Debug, Clone, Serialize)]
39pub struct ConfluenceUserSearchResult {
40 #[serde(skip_serializing_if = "Option::is_none")]
43 pub account_id: Option<String>,
44 pub display_name: String,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 pub email: Option<String>,
49}
50
51#[derive(Debug, Clone, Serialize)]
54pub struct ConfluenceUserSearchResults {
55 pub users: Vec<ConfluenceUserSearchResult>,
57 pub total: u32,
59}
60
61#[derive(Debug, Clone, Serialize)]
67pub struct ConfluenceUserRecord {
68 pub account_id: String,
70 #[serde(skip_serializing_if = "Option::is_none")]
72 pub display_name: Option<String>,
73 #[serde(skip_serializing_if = "Option::is_none")]
75 pub email: Option<String>,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub account_type: Option<String>,
79 #[serde(skip_serializing_if = "Option::is_none")]
82 pub active: Option<bool>,
83 #[serde(skip_serializing_if = "Option::is_none")]
86 pub error: Option<String>,
87}
88
89#[derive(Debug, Clone, Serialize)]
92pub struct ConfluenceUserGetResults {
93 pub users: Vec<ConfluenceUserRecord>,
95}
96
97#[derive(Deserialize)]
98#[allow(dead_code)]
99pub(crate) struct ConfluenceContentSearchResponse {
100 pub(crate) results: Vec<ConfluenceContentSearchEntry>,
101 #[serde(default)]
102 pub(crate) size: u32,
103 #[serde(rename = "_links", default)]
104 pub(crate) links: Option<ConfluenceSearchLinks>,
105}
106
107#[derive(Deserialize, Default)]
108pub(crate) struct ConfluenceSearchLinks {
109 pub(crate) next: Option<String>,
110}
111
112#[derive(Deserialize)]
113pub(crate) struct ConfluenceContentSearchEntry {
114 pub(crate) id: String,
115 pub(crate) title: String,
116 #[serde(rename = "_expandable")]
117 pub(crate) expandable: Option<ConfluenceExpandable>,
118}
119
120#[derive(Deserialize)]
121pub(crate) struct ConfluenceExpandable {
122 pub(crate) space: Option<String>,
123}
124
125#[derive(Deserialize)]
128pub(crate) struct ConfluenceUserSearchResponse {
129 pub(crate) results: Vec<ConfluenceUserSearchEntry>,
130 #[serde(rename = "_links", default)]
131 pub(crate) links: Option<ConfluenceSearchLinks>,
132}
133
134#[derive(Deserialize)]
135pub(crate) struct ConfluenceUserSearchEntry {
136 #[serde(default)]
137 pub(crate) user: Option<ConfluenceSearchUser>,
138}
139
140#[derive(Deserialize)]
141pub(crate) struct ConfluenceSearchUser {
142 #[serde(rename = "accountId", default)]
143 pub(crate) account_id: Option<String>,
144 #[serde(rename = "displayName", default)]
145 pub(crate) display_name: Option<String>,
146 #[serde(default)]
147 pub(crate) email: Option<String>,
148 #[serde(rename = "publicName", default)]
149 pub(crate) public_name: Option<String>,
150}
151
152#[derive(Deserialize)]
158pub(crate) struct ConfluenceUserGetEntry {
159 #[serde(rename = "accountId", default)]
160 pub(crate) account_id: Option<String>,
161 #[serde(rename = "accountType", default)]
162 pub(crate) account_type: Option<String>,
163 #[serde(rename = "displayName", default)]
164 pub(crate) display_name: Option<String>,
165 #[serde(rename = "publicName", default)]
166 pub(crate) public_name: Option<String>,
167 #[serde(default)]
168 pub(crate) email: Option<String>,
169}
170
171#[derive(Deserialize)]
174pub(crate) struct ConfluencePageResponse {
175 pub(crate) id: String,
176 pub(crate) title: String,
177 pub(crate) status: String,
178 #[serde(rename = "spaceId")]
179 pub(crate) space_id: String,
180 pub(crate) version: Option<ConfluenceVersion>,
181 pub(crate) body: Option<ConfluenceBody>,
182 #[serde(rename = "parentId")]
183 pub(crate) parent_id: Option<String>,
184 #[serde(default)]
185 pub(crate) ancestors: Vec<ConfluenceAncestorEntry>,
186}
187
188#[derive(Deserialize)]
189pub(crate) struct ConfluenceAncestorEntry {
190 pub(crate) id: String,
191}
192
193#[derive(Deserialize)]
194pub(crate) struct ConfluenceVersion {
195 pub(crate) number: u32,
196}
197
198#[derive(Deserialize)]
199pub(crate) struct ConfluenceBody {
200 pub(crate) atlas_doc_format: Option<ConfluenceAtlasDoc>,
201}
202
203#[derive(Deserialize)]
204pub(crate) struct ConfluenceAtlasDoc {
205 pub(crate) value: String,
206}
207
208#[derive(Deserialize)]
211pub(crate) struct ConfluenceSpaceResponse {
212 pub(crate) key: String,
213}
214
215#[derive(Deserialize)]
216pub(crate) struct ConfluenceSpacesResponse {
217 pub(crate) results: Vec<ConfluenceSpaceEntry>,
218 #[serde(rename = "_links", default)]
219 pub(crate) links: Option<ConfluenceSpaceLinks>,
220}
221
222#[derive(Deserialize)]
223pub(crate) struct ConfluenceSpaceLinks {
224 pub(crate) next: Option<String>,
225}
226
227#[derive(Deserialize)]
228pub(crate) struct ConfluenceSpaceEntry {
229 pub(crate) id: String,
230 #[serde(default)]
231 pub(crate) key: Option<String>,
232 #[serde(default)]
233 pub(crate) name: Option<String>,
234 #[serde(rename = "type", default)]
235 pub(crate) type_: Option<String>,
236 #[serde(default)]
237 pub(crate) status: Option<String>,
238 #[serde(rename = "homepageId", default)]
239 pub(crate) homepage_id: Option<String>,
240}
241
242#[derive(Debug, Clone, Serialize)]
244pub struct ConfluenceSpace {
245 pub id: String,
247 pub key: String,
249 pub name: String,
251 #[serde(rename = "type")]
253 pub type_: String,
254 pub status: String,
256 #[serde(rename = "homepageId", skip_serializing_if = "Option::is_none")]
258 pub homepage_id: Option<String>,
259}
260
261impl From<ConfluenceSpaceEntry> for ConfluenceSpace {
262 fn from(e: ConfluenceSpaceEntry) -> Self {
263 Self {
264 id: e.id,
265 key: e.key.unwrap_or_default(),
266 name: e.name.unwrap_or_default(),
267 type_: e.type_.unwrap_or_default(),
268 status: e.status.unwrap_or_default(),
269 homepage_id: e.homepage_id,
270 }
271 }
272}
273
274#[derive(Debug, Clone, Serialize)]
281pub struct ConfluenceSpacePage {
282 pub results: Vec<ConfluenceSpace>,
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub next_cursor: Option<String>,
287}
288
289#[derive(Debug, Clone, Serialize)]
291pub struct PageSummary {
292 pub id: String,
294 pub title: String,
296 #[serde(skip_serializing_if = "String::is_empty")]
298 pub status: String,
299 #[serde(rename = "parentId", skip_serializing_if = "Option::is_none")]
301 pub parent_id: Option<String>,
302 #[serde(rename = "authorId", skip_serializing_if = "Option::is_none")]
304 pub author_id: Option<String>,
305 #[serde(rename = "createdAt", skip_serializing_if = "Option::is_none")]
307 pub created_at: Option<String>,
308}
309
310#[derive(Debug, Clone, Serialize)]
317pub struct PageSummaryPage {
318 pub results: Vec<PageSummary>,
320 #[serde(skip_serializing_if = "Option::is_none")]
322 pub next_cursor: Option<String>,
323}
324
325#[derive(Deserialize)]
328pub(crate) struct ConfluenceChildrenResponse {
329 pub(crate) results: Vec<ConfluenceChildEntry>,
330 #[serde(rename = "_links", default)]
331 pub(crate) links: Option<ConfluenceChildrenLinks>,
332}
333
334#[derive(Deserialize)]
335pub(crate) struct ConfluenceChildEntry {
336 pub(crate) id: String,
337 pub(crate) title: String,
338 #[serde(default)]
339 pub(crate) status: Option<String>,
340}
341
342#[derive(Deserialize)]
343pub(crate) struct ConfluenceChildrenLinks {
344 pub(crate) next: Option<String>,
345}
346
347#[derive(Deserialize)]
349pub(crate) struct ConfluenceSpacePagesResponse {
350 pub(crate) results: Vec<ConfluenceSpacePageEntry>,
351 #[serde(rename = "_links", default)]
352 pub(crate) links: Option<ConfluenceChildrenLinks>,
353}
354
355#[derive(Deserialize)]
356pub(crate) struct ConfluenceSpacePageEntry {
357 pub(crate) id: String,
358 pub(crate) title: String,
359 #[serde(default)]
360 pub(crate) status: Option<String>,
361 #[serde(rename = "parentId", default)]
362 pub(crate) parent_id: Option<String>,
363}
364
365#[derive(Deserialize)]
369pub(crate) struct ConfluenceSpacePagesSummaryResponse {
370 pub(crate) results: Vec<ConfluenceSpacePageSummaryEntry>,
371 #[serde(rename = "_links", default)]
372 pub(crate) links: Option<ConfluenceChildrenLinks>,
373}
374
375#[derive(Deserialize)]
376pub(crate) struct ConfluenceSpacePageSummaryEntry {
377 pub(crate) id: String,
378 pub(crate) title: String,
379 #[serde(default)]
380 pub(crate) status: Option<String>,
381 #[serde(rename = "parentId", default)]
382 pub(crate) parent_id: Option<String>,
383 #[serde(rename = "authorId", default)]
384 pub(crate) author_id: Option<String>,
385 #[serde(rename = "createdAt", default)]
386 pub(crate) created_at: Option<String>,
387}
388
389#[derive(Debug, Clone, serde::Serialize)]
391pub struct ChildPage {
392 pub id: String,
394 pub title: String,
396 #[serde(default, skip_serializing_if = "String::is_empty")]
398 pub status: String,
399 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub parent_id: Option<String>,
402 #[serde(default, skip_serializing_if = "Option::is_none")]
404 pub space_key: Option<String>,
405}
406
407#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
416#[serde(rename_all = "lowercase")]
417pub enum CommentKind {
418 Footer,
420 Inline,
422}
423
424impl CommentKind {
425 #[must_use]
428 pub fn endpoint_segment(self) -> &'static str {
429 match self {
430 Self::Footer => "footer-comments",
431 Self::Inline => "inline-comments",
432 }
433 }
434}
435
436impl std::fmt::Display for CommentKind {
437 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
438 match self {
439 Self::Footer => f.write_str("footer"),
440 Self::Inline => f.write_str("inline"),
441 }
442 }
443}
444
445#[derive(Debug, Clone)]
451pub struct InlineAnchor {
452 pub text: String,
454 pub match_index: usize,
456 pub match_count: usize,
458}
459
460#[derive(Debug, Clone, Serialize)]
462pub struct ConfluenceComment {
463 pub id: String,
465 pub author: String,
467 pub kind: CommentKind,
469 pub body_adf: Option<serde_json::Value>,
471 pub created: String,
473 #[serde(skip_serializing_if = "Option::is_none")]
477 pub inline_marker_ref: Option<String>,
478 #[serde(skip_serializing_if = "Option::is_none")]
483 pub inline_original_selection: Option<String>,
484}
485
486#[derive(Deserialize)]
487pub(crate) struct ConfluenceCommentsResponse {
488 pub(crate) results: Vec<ConfluenceCommentEntry>,
489 #[serde(rename = "_links", default)]
490 pub(crate) links: Option<ConfluenceCommentsLinks>,
491}
492
493#[derive(Deserialize)]
494pub(crate) struct ConfluenceCommentsLinks {
495 pub(crate) next: Option<String>,
496}
497
498#[derive(Deserialize)]
499pub(crate) struct ConfluenceCommentEntry {
500 pub(crate) id: String,
501 #[serde(default)]
502 pub(crate) version: Option<ConfluenceCommentVersion>,
503 #[serde(default)]
504 pub(crate) body: Option<ConfluenceCommentBody>,
505 #[serde(default)]
508 pub(crate) properties: Option<ConfluenceInlineCommentProperties>,
509}
510
511#[derive(Deserialize)]
515pub(crate) struct ConfluenceInlineCommentProperties {
516 #[serde(rename = "inlineMarkerRef", default)]
518 pub(crate) inline_marker_ref: Option<String>,
519 #[serde(rename = "inlineOriginalSelection", default)]
521 pub(crate) inline_original_selection: Option<String>,
522}
523
524#[derive(Deserialize)]
525pub(crate) struct ConfluenceCommentVersion {
526 #[serde(rename = "authorId", default)]
527 pub(crate) author_id: Option<String>,
528 #[serde(rename = "createdAt", default)]
529 pub(crate) created_at: Option<String>,
530}
531
532#[derive(Deserialize)]
533pub(crate) struct ConfluenceCommentBody {
534 pub(crate) atlas_doc_format: Option<ConfluenceAtlasDoc>,
535}
536
537#[derive(Serialize)]
538pub(crate) struct ConfluenceAddCommentRequest {
539 #[serde(rename = "pageId")]
540 pub(crate) page_id: String,
541 pub(crate) body: ConfluenceUpdateBody,
542}
543
544#[derive(Serialize)]
545pub(crate) struct ConfluenceAddInlineCommentRequest {
546 #[serde(rename = "pageId")]
547 pub(crate) page_id: String,
548 pub(crate) body: ConfluenceUpdateBody,
549 #[serde(rename = "inlineCommentProperties")]
550 pub(crate) inline_comment_properties: InlineCommentProperties,
551}
552
553#[derive(Serialize)]
554pub(crate) struct InlineCommentProperties {
555 #[serde(rename = "textSelection")]
556 pub(crate) text_selection: String,
557 #[serde(rename = "textSelectionMatchCount")]
558 pub(crate) text_selection_match_count: usize,
559 #[serde(rename = "textSelectionMatchIndex")]
560 pub(crate) text_selection_match_index: usize,
561}
562
563#[derive(Deserialize)]
566pub(crate) struct ConfluenceLabelsResponse {
567 pub(crate) results: Vec<ConfluenceLabelEntry>,
568 #[serde(rename = "_links", default)]
569 pub(crate) links: Option<ConfluenceLabelsLinks>,
570}
571
572#[derive(Deserialize)]
573pub(crate) struct ConfluenceLabelEntry {
574 pub(crate) id: String,
575 pub(crate) name: String,
576 pub(crate) prefix: String,
577}
578
579#[derive(Deserialize)]
580pub(crate) struct ConfluenceLabelsLinks {
581 pub(crate) next: Option<String>,
582}
583
584#[derive(Debug, Clone, Serialize)]
586pub struct ConfluenceLabel {
587 pub id: String,
589 pub name: String,
591 pub prefix: String,
593}
594
595#[derive(Serialize)]
596pub(crate) struct ConfluenceAddLabelEntry {
597 pub(crate) prefix: String,
598 pub(crate) name: String,
599}
600
601#[derive(Deserialize)]
604pub(crate) struct ConfluenceVersionsResponse {
605 pub(crate) results: Vec<ConfluenceVersionEntry>,
606 #[serde(rename = "_links", default)]
607 pub(crate) links: Option<ConfluenceVersionsLinks>,
608}
609
610#[derive(Deserialize)]
611pub(crate) struct ConfluenceVersionEntry {
612 pub(crate) number: u32,
613 #[serde(rename = "createdAt", default)]
614 pub(crate) created_at: Option<String>,
615 #[serde(default)]
616 pub(crate) message: Option<String>,
617 #[serde(rename = "minorEdit", default)]
618 pub(crate) minor_edit: Option<bool>,
619 #[serde(rename = "authorId", default)]
620 pub(crate) author_id: Option<String>,
621}
622
623#[derive(Deserialize)]
624pub(crate) struct ConfluenceVersionsLinks {
625 pub(crate) next: Option<String>,
626}
627
628#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct PageVersion {
635 pub number: u32,
637 #[serde(default)]
639 pub created_at: String,
640 #[serde(default)]
642 pub author_id: String,
643 #[serde(default)]
645 pub message: String,
646 #[serde(default)]
648 pub minor_edit: bool,
649}
650
651#[derive(Debug, Clone, PartialEq, Eq)]
653pub enum SinceFilter {
654 Version(u32),
656 CreatedAt(String),
660}
661
662impl SinceFilter {
663 pub fn parse(raw: &str) -> Result<Self> {
667 let trimmed = raw.trim();
668 if trimmed.is_empty() {
669 anyhow::bail!("`since` must be a version number or ISO 8601 date");
670 }
671 if trimmed.chars().all(|c| c.is_ascii_digit()) {
672 let n: u32 = trimmed
673 .parse()
674 .with_context(|| format!("Invalid version number \"{trimmed}\""))?;
675 return Ok(Self::Version(n));
676 }
677 if trimmed.contains('-') || trimmed.contains('T') {
678 return Ok(Self::CreatedAt(trimmed.to_string()));
679 }
680 anyhow::bail!(
681 "`since` must be a numeric version (e.g. \"5\") or ISO 8601 date \
682 (e.g. \"2026-01-01T00:00:00Z\"); got \"{trimmed}\""
683 );
684 }
685
686 pub(crate) fn matches(&self, version: &PageVersion) -> bool {
688 match self {
689 Self::Version(min) => version.number >= *min,
690 Self::CreatedAt(min) => {
691 if version.created_at.is_empty() {
692 false
694 } else {
695 version.created_at.as_str() >= min.as_str()
696 }
697 }
698 }
699 }
700}
701
702#[derive(Debug, Clone, Serialize)]
707pub struct PageMetadata {
708 pub id: String,
710 pub title: String,
712 pub current_version: Option<u32>,
714}
715
716#[derive(Deserialize)]
719pub(crate) struct ConfluenceAttachmentsResponse {
720 pub(crate) results: Vec<ConfluenceAttachmentEntry>,
721 #[serde(rename = "_links", default)]
722 pub(crate) links: Option<ConfluenceAttachmentLinks>,
723}
724
725#[derive(Deserialize)]
726pub(crate) struct ConfluenceAttachmentLinks {
727 pub(crate) next: Option<String>,
728}
729
730#[derive(Deserialize)]
731pub(crate) struct ConfluenceAttachmentEntry {
732 pub(crate) id: String,
733 pub(crate) title: String,
734 #[serde(rename = "mediaType", default)]
735 pub(crate) media_type: Option<String>,
736 #[serde(rename = "fileSize", default)]
737 pub(crate) file_size: Option<u64>,
738 #[serde(rename = "downloadLink", default)]
739 pub(crate) download_link: Option<String>,
740 #[serde(default)]
741 pub(crate) version: Option<ConfluenceAttachmentVersion>,
742 #[serde(rename = "pageId", default)]
743 pub(crate) page_id: Option<String>,
744 #[serde(rename = "fileId", default)]
745 pub(crate) file_id: Option<String>,
746}
747
748#[derive(Deserialize)]
749pub(crate) struct ConfluenceAttachmentVersion {
750 pub(crate) number: u32,
751}
752
753#[derive(Deserialize)]
761pub(crate) struct ConfluenceV1AttachmentResponse {
762 pub(crate) results: Vec<ConfluenceV1AttachmentEntry>,
763}
764
765#[derive(Deserialize)]
766pub(crate) struct ConfluenceV1AttachmentEntry {
767 pub(crate) id: String,
768 pub(crate) title: String,
769 #[serde(default)]
770 pub(crate) extensions: Option<ConfluenceV1AttachmentExtensions>,
771 #[serde(default)]
772 pub(crate) version: Option<ConfluenceAttachmentVersion>,
773 #[serde(default)]
774 pub(crate) container: Option<ConfluenceV1AttachmentContainer>,
775 #[serde(rename = "_links", default)]
776 pub(crate) links: Option<ConfluenceV1AttachmentEntryLinks>,
777}
778
779#[derive(Deserialize)]
780pub(crate) struct ConfluenceV1AttachmentExtensions {
781 #[serde(rename = "mediaType", default)]
782 pub(crate) media_type: Option<String>,
783 #[serde(rename = "fileSize", default)]
784 pub(crate) file_size: Option<u64>,
785 #[serde(rename = "fileId", default)]
786 pub(crate) file_id: Option<String>,
787}
788
789#[derive(Deserialize)]
790pub(crate) struct ConfluenceV1AttachmentContainer {
791 #[serde(default)]
792 pub(crate) id: Option<String>,
793}
794
795#[derive(Deserialize)]
796pub(crate) struct ConfluenceV1AttachmentEntryLinks {
797 #[serde(default)]
798 pub(crate) download: Option<String>,
799}
800
801impl From<ConfluenceV1AttachmentEntry> for ConfluenceAttachment {
802 fn from(e: ConfluenceV1AttachmentEntry) -> Self {
803 let (media_type, file_size, file_id) = match e.extensions {
804 Some(x) => (x.media_type, x.file_size, x.file_id),
805 None => (None, None, None),
806 };
807 Self {
808 id: e.id,
809 title: e.title,
810 media_type,
811 file_size,
812 download_url: e.links.and_then(|l| l.download),
813 version: e.version.map(|v| v.number),
814 page_id: e.container.and_then(|c| c.id),
815 file_id,
816 }
817 }
818}
819
820#[derive(Debug, Clone, Serialize)]
822pub struct ConfluenceAttachment {
823 pub id: String,
825 pub title: String,
827 #[serde(skip_serializing_if = "Option::is_none")]
829 pub media_type: Option<String>,
830 #[serde(skip_serializing_if = "Option::is_none")]
832 pub file_size: Option<u64>,
833 #[serde(skip_serializing_if = "Option::is_none")]
835 pub download_url: Option<String>,
836 #[serde(skip_serializing_if = "Option::is_none")]
838 pub version: Option<u32>,
839 #[serde(skip_serializing_if = "Option::is_none")]
841 pub page_id: Option<String>,
842 #[serde(skip_serializing_if = "Option::is_none")]
844 pub file_id: Option<String>,
845}
846
847impl From<ConfluenceAttachmentEntry> for ConfluenceAttachment {
848 fn from(e: ConfluenceAttachmentEntry) -> Self {
849 Self {
850 id: e.id,
851 title: e.title,
852 media_type: e.media_type,
853 file_size: e.file_size,
854 download_url: e.download_link,
855 version: e.version.map(|v| v.number),
856 page_id: e.page_id,
857 file_id: e.file_id,
858 }
859 }
860}
861
862#[derive(Debug, Clone, Serialize)]
870pub struct ConfluenceAttachmentPage {
871 pub results: Vec<ConfluenceAttachment>,
873 #[serde(skip_serializing_if = "Option::is_none")]
875 pub next_cursor: Option<String>,
876}
877
878#[derive(Serialize)]
881pub(crate) struct ConfluenceCreateRequest {
882 #[serde(rename = "spaceId")]
883 pub(crate) space_id: String,
884 pub(crate) title: String,
885 pub(crate) body: ConfluenceUpdateBody,
886 #[serde(rename = "parentId", skip_serializing_if = "Option::is_none")]
887 pub(crate) parent_id: Option<String>,
888 pub(crate) status: String,
889}
890
891#[derive(Deserialize)]
892pub(crate) struct ConfluenceCreateResponse {
893 pub(crate) id: String,
894}
895
896#[derive(Serialize)]
899pub(crate) struct ConfluenceUpdateRequest {
900 pub(crate) id: String,
901 pub(crate) status: String,
902 pub(crate) title: String,
903 pub(crate) body: ConfluenceUpdateBody,
904 pub(crate) version: ConfluenceUpdateVersion,
905}
906
907#[derive(Serialize)]
908pub(crate) struct ConfluenceUpdateBody {
909 pub(crate) representation: String,
910 pub(crate) value: String,
911}
912
913#[derive(Serialize)]
914pub(crate) struct ConfluenceUpdateVersion {
915 pub(crate) number: u32,
916 pub(crate) message: Option<String>,
917}
918
919#[derive(Debug, Clone, Copy, PartialEq, Eq)]
924pub enum MovePosition {
925 Append,
927 Before,
929 After,
931}
932
933impl MovePosition {
934 pub fn as_str(self) -> &'static str {
936 match self {
937 Self::Append => "append",
938 Self::Before => "before",
939 Self::After => "after",
940 }
941 }
942}
943
944#[derive(Debug, Clone, Serialize)]
946pub struct MovedPage {
947 pub id: String,
949 pub title: String,
951 #[serde(skip_serializing_if = "Option::is_none")]
953 pub parent_id: Option<String>,
954 pub ancestors: Vec<String>,
956}