1use crate::project::{EditProject, SCHEMA_VERSION};
15
16pub const SCREENPROJ_EXTENSION: &str = "screenproj";
18
19pub fn to_screenproj(project: &EditProject) -> Result<String, String> {
26 serde_json::to_string_pretty(project).map_err(|e| format!("serialize project: {e}"))
27}
28
29pub fn from_screenproj(json: &str) -> Result<EditProject, String> {
35 let project: EditProject =
36 serde_json::from_str(json).map_err(|e| format!("parse project: {e}"))?;
37 if project.schema_version != SCHEMA_VERSION {
38 return Err(format!(
39 "unsupported .screenproj schema version {} (this build reads {SCHEMA_VERSION})",
40 project.schema_version
41 ));
42 }
43 Ok(project)
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use crate::clip::ClipRef;
50 use crate::ops::EditOp;
51 use crate::project::SCHEMA_VERSION;
52 use crate::zoom::{ZoomId, ZoomSegment};
53 use std::path::PathBuf;
54
55 #[test]
56 fn round_trips_an_edited_project() {
57 let mut p = EditProject::from_recording(ClipRef::new(
58 PathBuf::from("/tmp/rec.mp4"),
59 1920,
60 1080,
61 30,
62 900,
63 ));
64 p.apply(&EditOp::Split { at: 300 }).unwrap();
67 p.apply(&EditOp::SetSpeed {
68 index: 0,
69 timescale: 2.0,
70 })
71 .unwrap();
72 p.apply(&EditOp::AddZoom {
73 zoom: ZoomSegment::manual(ZoomId(0), 100, 200, 1.6),
74 })
75 .unwrap();
76
77 let json = to_screenproj(&p).expect("serialize");
78 let back = from_screenproj(&json).expect("deserialize");
79 assert_eq!(back, p, "project round-trips identically");
80 assert_eq!(back.schema_version, SCHEMA_VERSION);
81 }
82
83 #[test]
84 fn malformed_json_errors() {
85 assert!(from_screenproj("not valid json {").is_err());
86 assert!(from_screenproj("[1, 2, 3]").is_err());
89 }
90
91 #[test]
92 fn screenproj_is_pretty_and_versioned() {
93 let p = EditProject::from_recording(ClipRef::new(
94 PathBuf::from("/tmp/a.mp4"),
95 640,
96 480,
97 30,
98 100,
99 ));
100 let json = to_screenproj(&p).unwrap();
101 assert!(json.contains('\n'), "pretty-printed (multi-line)");
102 assert!(json.contains("schema_version"));
103 }
104
105 #[test]
106 fn rejects_a_future_schema_version() {
107 let p =
108 EditProject::from_recording(ClipRef::new(PathBuf::from("/tmp/a.mp4"), 64, 64, 30, 10));
109 let json = to_screenproj(&p).unwrap();
110 assert!(from_screenproj(&json).is_ok(), "current version loads");
111 let bumped = json.replace("\"schema_version\": 1", "\"schema_version\": 999");
113 assert!(from_screenproj(&bumped).is_err(), "future version rejected");
114 }
115}