1use std::collections::BTreeMap;
8
9#[derive(Debug, Clone, Copy, PartialEq, Default)]
11#[cfg_attr(
12 feature = "serde",
13 derive(serde::Serialize, serde::Deserialize),
14 serde(rename_all = "camelCase")
15)]
16pub struct FreeformPoint {
17 pub x: f64,
19 pub y: f64,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Default)]
25#[cfg_attr(
26 feature = "serde",
27 derive(serde::Serialize, serde::Deserialize),
28 serde(rename_all = "camelCase")
29)]
30pub struct FreeformSize {
31 pub width: f64,
33 pub height: f64,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq)]
39#[cfg_attr(
40 feature = "serde",
41 derive(serde::Serialize, serde::Deserialize),
42 serde(rename_all = "camelCase")
43)]
44pub struct FreeformTransform {
45 pub a: f64,
47 pub b: f64,
49 pub c: f64,
51 pub d: f64,
53 pub tx: f64,
55 pub ty: f64,
57}
58
59impl Default for FreeformTransform {
60 fn default() -> Self {
61 Self { a: 1.0, b: 0.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0 }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Default)]
67#[cfg_attr(
68 feature = "serde",
69 derive(serde::Serialize, serde::Deserialize),
70 serde(rename_all = "camelCase")
71)]
72pub struct FreeformFrame {
73 pub x: f64,
75 pub y: f64,
77 pub w: f64,
79 pub h: f64,
81 pub rotation: f64,
83}
84
85#[derive(Debug, Clone, PartialEq, Default)]
87#[cfg_attr(
88 feature = "serde",
89 derive(serde::Serialize, serde::Deserialize),
90 serde(rename_all = "camelCase")
91)]
92pub struct FreeformGeometry {
93 pub frame: Option<FreeformFrame>,
95 pub transform: Option<FreeformTransform>,
97 pub anchor: Option<FreeformPoint>,
99 pub horizontal_flip: Option<bool>,
101 pub vertical_flip: Option<bool>,
103 pub width_valid: Option<bool>,
105 pub height_valid: Option<bool>,
107}
108
109#[derive(Debug, Clone, PartialEq)]
111#[cfg_attr(
112 feature = "serde",
113 derive(serde::Serialize, serde::Deserialize),
114 serde(rename_all = "camelCase")
115)]
116pub struct FreeformColor {
117 pub color_space: String,
119 pub red: f64,
121 pub green: f64,
123 pub blue: f64,
125 pub alpha: f64,
127 pub hex: String,
129}
130
131#[derive(Debug, Clone, PartialEq)]
133#[cfg_attr(
134 feature = "serde",
135 derive(serde::Serialize, serde::Deserialize),
136 serde(rename_all = "camelCase")
137)]
138pub struct FreeformGradientStop {
139 pub fraction: f64,
141 pub inflection: Option<f64>,
143 pub color: FreeformColor,
145}
146
147#[derive(Debug, Clone, PartialEq)]
149#[cfg_attr(
150 feature = "serde",
151 derive(serde::Serialize, serde::Deserialize),
152 serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")
153)]
154pub enum FreeformPaint {
155 Solid { color: FreeformColor },
157 LinearGradient {
159 start: FreeformPoint,
161 end: FreeformPoint,
163 stops: Vec<FreeformGradientStop>,
165 },
166 RadialGradient {
168 center: FreeformPoint,
170 radius: f64,
172 stops: Vec<FreeformGradientStop>,
174 },
175 Image {
177 asset_id: String,
179 technique: Option<String>,
181 },
182 Unknown { raw_data: Vec<u8> },
184}
185
186#[derive(Debug, Clone, PartialEq)]
188#[cfg_attr(
189 feature = "serde",
190 derive(serde::Serialize, serde::Deserialize),
191 serde(rename_all = "camelCase")
192)]
193pub struct FreeformStrokeStyle {
194 pub paint: FreeformPaint,
196 pub width: f64,
198 pub dash: Vec<f64>,
200 pub dash_offset: Option<f64>,
202 pub cap: Option<String>,
204 pub join: Option<String>,
206 pub miter_limit: Option<f64>,
208 pub tail_end: Option<String>,
210 pub head_end: Option<String>,
212}
213
214#[derive(Debug, Clone, PartialEq)]
216#[cfg_attr(
217 feature = "serde",
218 derive(serde::Serialize, serde::Deserialize),
219 serde(rename_all = "camelCase")
220)]
221pub struct FreeformShadow {
222 pub kind: String,
224 pub color: FreeformColor,
226 pub offset_x: f64,
228 pub offset_y: f64,
230 pub radius: f64,
232 pub opacity: Option<f64>,
234 pub angle: Option<f64>,
236}
237
238#[derive(Debug, Clone, PartialEq, Default)]
240#[cfg_attr(
241 feature = "serde",
242 derive(serde::Serialize, serde::Deserialize),
243 serde(rename_all = "camelCase")
244)]
245pub struct FreeformStyle {
246 pub opacity: Option<f64>,
248 pub fill: Option<FreeformPaint>,
250 pub stroke: Option<FreeformStrokeStyle>,
252 pub shadows: Vec<FreeformShadow>,
254}
255
256#[derive(Debug, Clone, Copy, PartialEq, Eq)]
258#[cfg_attr(
259 feature = "serde",
260 derive(serde::Serialize, serde::Deserialize),
261 serde(rename_all = "camelCase")
262)]
263pub enum FreeformInkPointRole {
264 SplineControl,
266 RenderedSample,
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Default)]
272#[cfg_attr(
273 feature = "serde",
274 derive(serde::Serialize, serde::Deserialize),
275 serde(rename_all = "camelCase")
276)]
277pub struct FreeformInkPoint {
278 pub x: f64,
280 pub y: f64,
282 pub time_offset: Option<f64>,
284 pub width: Option<f64>,
286 pub height: Option<f64>,
288 pub opacity: Option<f64>,
290 pub force: Option<f64>,
292 pub azimuth: Option<f64>,
294 pub altitude: Option<f64>,
296 pub secondary_scale: Option<f64>,
298 pub threshold: Option<f64>,
300}
301
302#[derive(Debug, Clone, Copy, PartialEq)]
304#[cfg_attr(
305 feature = "serde",
306 derive(serde::Serialize, serde::Deserialize),
307 serde(rename_all = "camelCase")
308)]
309pub struct FreeformInkRange {
310 pub start: f64,
312 pub end: f64,
314}
315
316#[derive(Debug, Clone, PartialEq)]
318#[cfg_attr(
319 feature = "serde",
320 derive(serde::Serialize, serde::Deserialize),
321 serde(rename_all = "camelCase")
322)]
323pub struct FreeformInkStroke {
324 pub ink_type: String,
326 pub ink_identifier: String,
328 pub color: Option<FreeformColor>,
330 pub transform: FreeformTransform,
332 pub point_role: FreeformInkPointRole,
334 pub points: Vec<FreeformInkPoint>,
336 pub visible_ranges: Option<Vec<FreeformInkRange>>,
338 pub random_seed: Option<u32>,
340 pub raw_data: Vec<u8>,
342}
343
344#[derive(Debug, Clone, PartialEq, Default)]
346#[cfg_attr(
347 feature = "serde",
348 derive(serde::Serialize, serde::Deserialize),
349 serde(rename_all = "camelCase")
350)]
351pub struct FreeformDrawing {
352 pub required_content_version: Option<u32>,
354 pub bounds: Option<FreeformFrame>,
356 pub strokes: Vec<FreeformInkStroke>,
358}
359
360#[derive(Debug, Clone, PartialEq)]
362#[cfg_attr(
363 feature = "serde",
364 derive(serde::Serialize, serde::Deserialize),
365 serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")
366)]
367pub enum FreeformPathCommand {
368 Move { point: FreeformPoint },
370 Line { point: FreeformPoint },
372 Quadratic { control: FreeformPoint, point: FreeformPoint },
374 Cubic {
376 control_1: FreeformPoint,
378 control_2: FreeformPoint,
380 point: FreeformPoint,
382 },
383 Close,
385}
386
387#[derive(Debug, Clone, PartialEq, Default)]
389#[cfg_attr(
390 feature = "serde",
391 derive(serde::Serialize, serde::Deserialize),
392 serde(rename_all = "camelCase")
393)]
394pub struct FreeformPath {
395 pub commands: Vec<FreeformPathCommand>,
397 pub natural_size: Option<FreeformSize>,
399 pub raw_data: Vec<u8>,
401}
402
403#[derive(Debug, Clone, PartialEq)]
405#[cfg_attr(
406 feature = "serde",
407 derive(serde::Serialize, serde::Deserialize),
408 serde(tag = "kind", content = "value", rename_all = "camelCase")
409)]
410pub enum TsuValue {
411 Null,
413 Bool(bool),
415 Integer(i64),
417 Real(f64),
419 String(String),
421 Data(Vec<u8>),
423 Array(Vec<Self>),
425 Dictionary(BTreeMap<String, Self>),
427}
428
429#[derive(Debug, Clone, PartialEq)]
431#[cfg_attr(
432 feature = "serde",
433 derive(serde::Serialize, serde::Deserialize),
434 serde(rename_all = "camelCase")
435)]
436pub struct TsuEntry {
437 pub class_name: String,
439 pub hints: BTreeMap<String, TsuValue>,
441}
442
443#[derive(Debug, Clone, PartialEq, Default)]
445#[cfg_attr(
446 feature = "serde",
447 derive(serde::Serialize, serde::Deserialize),
448 serde(rename_all = "camelCase")
449)]
450pub struct FreeformTextRun {
451 pub start: usize,
453 pub end: usize,
455 pub font_name: Option<String>,
457 pub font_size: Option<f64>,
459 pub bold: Option<bool>,
461 pub italic: Option<bool>,
463 pub underline: Option<String>,
465 pub strikethrough: Option<String>,
467 pub fill: Option<FreeformPaint>,
469 pub paragraph_alignment: Option<String>,
471 pub writing_direction: Option<String>,
473 pub hyperlink: Option<String>,
475 pub list_style: Option<String>,
477}
478
479#[derive(Debug, Clone, PartialEq, Default)]
481#[cfg_attr(
482 feature = "serde",
483 derive(serde::Serialize, serde::Deserialize),
484 serde(rename_all = "camelCase")
485)]
486pub struct FreeformText {
487 pub plain: String,
489 pub runs: Vec<FreeformTextRun>,
491 pub inset: Option<FreeformFrame>,
493 pub vertical_alignment: Option<String>,
495 pub shrink_to_fit: Option<bool>,
497}
498
499#[derive(Debug, Clone, PartialEq)]
501#[cfg_attr(
502 feature = "serde",
503 derive(serde::Serialize, serde::Deserialize),
504 serde(rename_all = "camelCase")
505)]
506pub struct FreeformConnectorEndpoint {
507 pub item_id: Option<String>,
509 pub magnet: Option<String>,
511 pub normalized_position: Option<FreeformPoint>,
513 pub point: Option<FreeformPoint>,
515 pub line_end: Option<String>,
517}
518
519#[derive(Debug, Clone, PartialEq, Default)]
521#[cfg_attr(
522 feature = "serde",
523 derive(serde::Serialize, serde::Deserialize),
524 serde(rename_all = "camelCase")
525)]
526pub struct FreeformTableCell {
527 pub id: Option<String>,
529 pub row: Option<usize>,
531 pub column: Option<usize>,
533 pub row_span: Option<usize>,
535 pub column_span: Option<usize>,
537 pub style: FreeformStyle,
539 pub text: Option<FreeformText>,
541 pub anchored_item_ids: Vec<String>,
543}
544
545#[derive(Debug, Clone, PartialEq)]
547#[cfg_attr(
548 feature = "serde",
549 derive(serde::Serialize, serde::Deserialize),
550 serde(rename_all = "camelCase")
551)]
552pub struct FreeformAsset {
553 pub id: String,
555 pub filename: Option<String>,
557 pub uti: Option<String>,
559 pub bytes: Option<Vec<u8>>,
561 pub premium: Option<bool>,
563 pub intrinsic_size: Option<FreeformSize>,
565 pub raw_descriptor: Vec<u8>,
567}
568
569#[derive(Debug, Clone, PartialEq)]
571#[cfg_attr(
572 feature = "serde",
573 derive(serde::Serialize, serde::Deserialize),
574 serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")
575)]
576pub enum FreeformItemKind {
577 Shape { preset: Option<String>, path: Option<FreeformPath> },
579 TextBox { text: Option<FreeformText> },
581 StickyNote { text: Option<FreeformText> },
583 Connector {
585 tail: FreeformConnectorEndpoint,
587 head: FreeformConnectorEndpoint,
589 routing: Option<String>,
591 path: Option<FreeformPath>,
593 },
594 Table {
596 row_heights: Vec<f64>,
598 column_widths: Vec<f64>,
600 cells: Vec<FreeformTableCell>,
602 },
603 Image {
605 asset_id: Option<String>,
607 crop: Option<FreeformFrame>,
609 mask: Option<FreeformPath>,
611 },
612 Media { asset_id: Option<String>, media_type: Option<String> },
614 File { asset_id: Option<String>, filename: Option<String> },
616 Url { url: Option<String>, title: Option<String> },
618 Usdz { asset_id: Option<String>, spatial_transform: Option<Vec<f64>> },
620 Group {
622 child_ids: Vec<String>,
624 counter_transform: Option<FreeformTransform>,
626 },
627 Ink { strokes: Vec<FreeformInkStroke> },
629 Unknown,
631}
632
633#[derive(Debug, Clone, Copy, PartialEq, Eq)]
636#[cfg_attr(
637 feature = "serde",
638 derive(serde::Serialize, serde::Deserialize),
639 serde(rename_all = "camelCase")
640)]
641pub struct FreeformRecordRange {
642 pub offset: usize,
644 pub length: usize,
646}
647
648#[derive(Debug, Clone, PartialEq)]
650#[cfg_attr(
651 feature = "serde",
652 derive(serde::Serialize, serde::Deserialize),
653 serde(rename_all = "camelCase")
654)]
655pub struct FreeformBoardItem {
656 pub index: usize,
658 pub uuid: String,
660 pub parent_id: Option<String>,
662 pub class_name: Option<String>,
664 pub hints: BTreeMap<String, TsuValue>,
666 pub geometry: FreeformGeometry,
668 pub style: FreeformStyle,
670 pub kind: FreeformItemKind,
672 pub record_ranges: Vec<FreeformRecordRange>,
674 pub raw_data: Vec<u8>,
678}
679
680#[derive(Debug, Clone, PartialEq, Eq, Default)]
682#[cfg_attr(
683 feature = "serde",
684 derive(serde::Serialize, serde::Deserialize),
685 serde(tag = "kind", rename_all = "camelCase", rename_all_fields = "camelCase")
686)]
687pub enum FreeformCompatibility {
688 #[default]
690 Unknown,
691 Supported { version: u64 },
693 Unsupported { minimum_version: u64 },
695}
696
697#[derive(Debug, Clone, PartialEq, Default)]
699#[cfg_attr(
700 feature = "serde",
701 derive(serde::Serialize, serde::Deserialize),
702 serde(rename_all = "camelCase")
703)]
704pub struct FreeformNative {
705 pub paste_id: String,
707 pub compatibility: FreeformCompatibility,
709 pub items: Vec<FreeformBoardItem>,
711 pub assets: BTreeMap<String, FreeformAsset>,
713 pub raw_manifest: Vec<u8>,
715 pub raw_index: Vec<u8>,
717 pub raw_archive: Vec<u8>,
719}
720
721#[derive(Debug, Clone, Copy, PartialEq, Eq)]
723#[cfg_attr(
724 feature = "serde",
725 derive(serde::Serialize, serde::Deserialize),
726 serde(rename_all = "camelCase")
727)]
728pub enum FreeformDecodeErrorKind {
729 Invalid,
731 Incomplete,
733 UnsupportedVersion,
735 CorrelationMismatch,
737}
738
739#[derive(Debug, Clone, PartialEq, Eq)]
741#[cfg_attr(
742 feature = "serde",
743 derive(serde::Serialize, serde::Deserialize),
744 serde(rename_all = "camelCase")
745)]
746pub struct FreeformDecodeError {
747 pub kind: FreeformDecodeErrorKind,
749 pub message: String,
751}
752
753impl FreeformDecodeError {
754 pub fn invalid(message: impl Into<String>) -> Self {
756 Self { kind: FreeformDecodeErrorKind::Invalid, message: message.into() }
757 }
758
759 pub fn incomplete(message: impl Into<String>) -> Self {
761 Self { kind: FreeformDecodeErrorKind::Incomplete, message: message.into() }
762 }
763
764 pub fn unsupported(message: impl Into<String>) -> Self {
766 Self { kind: FreeformDecodeErrorKind::UnsupportedVersion, message: message.into() }
767 }
768
769 pub fn correlation(message: impl Into<String>) -> Self {
771 Self { kind: FreeformDecodeErrorKind::CorrelationMismatch, message: message.into() }
772 }
773}
774
775impl std::fmt::Display for FreeformDecodeError {
776 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
777 write!(f, "freeform decode: {}", self.message)
778 }
779}
780
781impl std::error::Error for FreeformDecodeError {}
782
783#[derive(Debug, Clone, PartialEq, Eq)]
785#[cfg_attr(
786 feature = "serde",
787 derive(serde::Serialize, serde::Deserialize),
788 serde(tag = "status", content = "value", rename_all = "camelCase")
789)]
790#[derive(Default)]
791pub enum FreeformTier<T> {
792 #[default]
794 Absent,
795 Decoded(T),
797 Failed(FreeformDecodeError),
799}
800
801#[derive(Debug, Clone, PartialEq, Eq)]
803#[cfg_attr(
804 feature = "serde",
805 derive(serde::Serialize, serde::Deserialize),
806 serde(rename_all = "camelCase")
807)]
808pub struct FreeformFlavor {
809 pub uti: String,
811 pub bytes: Vec<u8>,
813}
814
815#[derive(Debug, Clone, PartialEq, Eq, Default)]
817#[cfg_attr(
818 feature = "serde",
819 derive(serde::Serialize, serde::Deserialize),
820 serde(rename_all = "camelCase")
821)]
822pub struct FreeformBlobs {
823 pub change_count: Option<i64>,
825 pub flavors: Vec<FreeformFlavor>,
827}
828
829#[derive(Debug, Clone, PartialEq, Eq, Default)]
831#[cfg_attr(
832 feature = "serde",
833 derive(serde::Serialize, serde::Deserialize),
834 serde(rename_all = "camelCase")
835)]
836pub struct FreeformNativeMetadata {
837 pub paste_id: Option<String>,
839 pub raw_data: Vec<u8>,
841}
842
843#[derive(Debug, Clone, PartialEq, Eq)]
845#[cfg_attr(
846 feature = "serde",
847 derive(serde::Serialize, serde::Deserialize),
848 serde(rename_all = "camelCase")
849)]
850pub struct FreeformRender {
851 pub uti: String,
853 pub bytes: Vec<u8>,
855}
856
857#[derive(Debug, Clone, PartialEq, Eq)]
859#[cfg_attr(
860 feature = "serde",
861 derive(serde::Serialize, serde::Deserialize),
862 serde(rename_all = "camelCase")
863)]
864pub struct FreeformDiagnostic {
865 pub source: String,
867 pub message: String,
869}
870
871#[derive(Debug, Clone, PartialEq, Default)]
873#[cfg_attr(
874 feature = "serde",
875 derive(serde::Serialize, serde::Deserialize),
876 serde(rename_all = "camelCase")
877)]
878pub struct FreeformPasteboard {
879 pub drawing: FreeformTier<FreeformDrawing>,
881 pub native: FreeformTier<FreeformNative>,
883 pub manifest: FreeformTier<Vec<TsuEntry>>,
885 pub metadata: FreeformTier<FreeformNativeMetadata>,
887 pub assets: BTreeMap<String, FreeformAsset>,
889 pub renders: Vec<FreeformRender>,
891 pub state: BTreeMap<String, Vec<u8>>,
893 pub styles: Vec<FreeformFlavor>,
895 pub text: Vec<FreeformFlavor>,
897 pub unknown_flavors: Vec<FreeformFlavor>,
899 pub diagnostics: Vec<FreeformDiagnostic>,
901}