summaryrefslogtreecommitdiff
path: root/src/types/project.rs
diff options
context:
space:
mode:
authorpml68 <contact@pml68.dev>2025-01-11 01:50:16 +0100
committerpml68 <contact@pml68.dev>2025-01-11 01:50:16 +0100
commit61926598ce96bee00aafe5340af4a905759b122a (patch)
treeb79e13b3decc778cc7c66af7187c647ae0a21a52 /src/types/project.rs
parentrefactor: apply clippy suggestions (diff)
downloadiced-builder-61926598ce96bee00aafe5340af4a905759b122a.tar.gz
refactor: remove iced_drop & workspace
Diffstat (limited to 'src/types/project.rs')
-rw-r--r--src/types/project.rs165
1 files changed, 165 insertions, 0 deletions
diff --git a/src/types/project.rs b/src/types/project.rs
new file mode 100644
index 0000000..27c576b
--- /dev/null
+++ b/src/types/project.rs
@@ -0,0 +1,165 @@
+use std::path::{Path, PathBuf};
+
+extern crate fxhash;
+use fxhash::FxHashMap;
+use iced::Theme;
+use rust_format::{Edition, Formatter, RustFmt};
+use serde::{Deserialize, Serialize};
+
+use super::rendered_element::RenderedElement;
+use crate::config::Config;
+use crate::theme::{theme_from_str, theme_index, theme_to_string};
+use crate::Error;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct Project {
+ pub title: Option<String>,
+ pub theme: Option<String>,
+ pub element_tree: Option<RenderedElement>,
+ #[serde(skip)]
+ theme_cache: FxHashMap<String, String>,
+}
+
+impl Default for Project {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Project {
+ pub fn new() -> Self {
+ Self {
+ title: None,
+ theme: None,
+ element_tree: None,
+ theme_cache: FxHashMap::default(),
+ }
+ }
+
+ pub fn get_theme(&self, config: &Config) -> Theme {
+ match &self.theme {
+ Some(theme) => theme_from_str(Some(config), theme),
+ None => Theme::default(),
+ }
+ }
+
+ fn theme_code(&mut self, theme: &Theme) -> String {
+ let theme_name = theme.to_string();
+ if theme_index(&theme_name, Theme::ALL).is_none() {
+ (*self
+ .theme_cache
+ .entry(theme_name)
+ .or_insert(theme_to_string(theme)))
+ .to_string()
+ } else {
+ theme_name.replace(" ", "")
+ }
+ }
+
+ pub async fn from_path(
+ path: PathBuf,
+ config: Config,
+ ) -> Result<(PathBuf, Self), Error> {
+ let contents = tokio::fs::read_to_string(&path).await?;
+ let mut project: Self = serde_json::from_str(&contents)?;
+
+ let _ = project.theme_code(&project.get_theme(&config));
+
+ Ok((path, project))
+ }
+
+ pub async fn from_file(config: Config) -> Result<(PathBuf, Self), Error> {
+ let picked_file = rfd::AsyncFileDialog::new()
+ .set_title("Open a JSON file...")
+ .add_filter("*.json, *.JSON", &["json", "JSON"])
+ .pick_file()
+ .await
+ .ok_or(Error::DialogClosed)?;
+
+ let path = picked_file.path().to_owned();
+
+ Self::from_path(path, config).await
+ }
+
+ pub async fn write_to_file(
+ self,
+ path: Option<PathBuf>,
+ ) -> Result<PathBuf, Error> {
+ let path = if let Some(p) = path {
+ p
+ } else {
+ rfd::AsyncFileDialog::new()
+ .set_title("Save to JSON file...")
+ .add_filter("*.json, *.JSON", &["json", "JSON"])
+ .save_file()
+ .await
+ .as_ref()
+ .map(rfd::FileHandle::path)
+ .map(Path::to_owned)
+ .ok_or(Error::DialogClosed)?
+ };
+
+ let contents = serde_json::to_string(&self)?;
+ tokio::fs::write(&path, contents).await?;
+
+ Ok(path)
+ }
+
+ pub fn app_code(&mut self, config: &Config) -> Result<String, Error> {
+ match self.element_tree {
+ Some(ref element_tree) => {
+ let (imports, view) = element_tree.codegen();
+ let theme = self.get_theme(config);
+ let theme_code = self.theme_code(&theme);
+ let mut theme_imports = "";
+ if theme_index(&theme.to_string(), Theme::ALL).is_none() {
+ if theme_code.contains("Extended") {
+ theme_imports = "use iced::{{color,theme::{{Palette,palette::{{Extended,Background,Primary,Secondary,Success,Danger,Pair}}}}}};\n";
+ } else {
+ theme_imports = "use iced::{{color,theme::Palette}};\n";
+ }
+ }
+
+ let app_code = format!(
+ r#"// Automatically generated by iced Builder
+ use iced::{{widget::{{{imports}}},Element}};
+ {theme_imports}
+
+ fn main() -> iced::Result {{
+ iced::application("{}", State::update, State::view).theme(State::theme).run()
+ }}
+
+ #[derive(Default)]
+ struct State;
+
+ #[derive(Debug, Clone)]
+ enum Message {{}}
+
+ impl State {{
+ fn update(&mut self, _message: Message) {{}}
+
+ fn theme(&self) -> iced::Theme {{
+ iced::Theme::{}
+ }}
+
+ fn view(&self) -> Element<Message> {{
+ {view}.into()
+ }}
+ }}"#,
+ match self.title {
+ Some(ref t) => t,
+ None => "New app",
+ },
+ theme_code
+ );
+ let config = rust_format::Config::new_str()
+ .edition(Edition::Rust2021)
+ .option("trailing_comma", "Never")
+ .option("imports_granularity", "Crate");
+ let rustfmt = RustFmt::from_config(config);
+ Ok(rustfmt.format_str(app_code)?)
+ }
+ None => Err("No element tree present".into()),
+ }
+ }
+}