1use crate::document::Document;
3use anyhow::Context;
4use bevy::prelude::{
5 BevyError, Children, Entity, Event, EventReader, Name, Query, Transform, With,
6};
7use bevy::prelude::{Commands, default};
8use data::{Element, Layer, Level, Project};
9use serialization::serialize_to;
10use std::{fs::File, path::PathBuf};
11use utils::{AsyncComponent, report_progress};
12
13#[derive(Event, Debug)]
22pub struct SaveProjectEvent {
23 pub(crate) project: Entity,
25 pub(crate) output: PathBuf,
27}
28
29#[derive(Event, Debug)]
31pub struct SaveProjectCompleteEvent {
32 pub project: Entity,
34 #[allow(
36 dead_code,
37 reason = "Temporarily until editor and status reporting is implemented"
38 )]
39 pub output: PathBuf, }
41
42impl SaveProjectEvent {
43 #[must_use = "This event does nothing unless you dispatch it"]
45 pub fn new(project: Entity, output: PathBuf) -> Self {
46 Self { project, output }
47 }
48}
49
50#[utils::bevy_system]
52pub fn handle_save_project(
53 mut commands: Commands,
54 mut events: EventReader<SaveProjectEvent>,
55 project_query: Query<(&Name, &Children), With<Project>>,
56 level_query: Query<(&Level, &Name, &Children)>,
57 layer_query: Query<(&Layer, &Name, &Transform, &Children)>,
58 object_query: Query<(&Element, &Name, &Transform)>,
59) -> Result<(), BevyError> {
60 let Some(event) = events.read().next() else {
61 return Ok(());
62 };
63
64 let project = project_query.get(event.project)?;
65
66 let entity = event.project;
67 let output = event.output.clone();
68 let document = Document::new(project, level_query, layer_query, object_query);
69 commands.spawn(AsyncComponent::new_io(
70 async move |sender| {
71 let file = File::create(output.clone()).with_context(|| {
72 format!("Failed to open {} for writing savefile", output.display())
73 })?;
74 serialize_to(&document, &default(), file)?;
75
76 report_progress(
78 &sender,
79 SaveProjectCompleteEvent {
80 project: entity,
81 output,
82 },
83 )?;
84 Ok(())
85 },
86 |_, _| {
87 },
89 ));
90
91 Ok(())
92}