-
Sqlx
MySQL
PostgreSQL
SQLite
@@ -376,6 +377,7 @@
current project: None
}
await fetchCargoToml()
+ await fetchFeatures()
});
// ask the server to change the current directory
@@ -413,18 +415,26 @@
current project: None
// init the options of each group
- function init_options() {
- const optionsDivs = document.querySelectorAll('.option-group');
-
- optionsDivs.forEach(div => {
- if (div.classList.contains('exclusive')) {
- const firstUnchecked = div.querySelector('.unchecked');
-
- if (firstUnchecked) {
- firstUnchecked.classList.replace('unchecked', 'checked')
- }
+ async function fetchFeatures() {
+ try {
+ const response = fetch('/get_current_features');
+ let features = await (await response).json();
+ document.querySelectorAll('.checked').forEach(option => {
+ option.classList.replace('checked', 'unchecked')
+ })
+ for (let feature of features.data.zino_feature) {
+ Array.from(document.querySelectorAll('#zino-config-form [data-feature]')).filter(option => option.getAttribute('data-feature') === feature).forEach(option => {
+ option.click()
+ })
}
- });
+ for (let feature of features.data.core_feature) {
+ Array.from(document.querySelectorAll('#core-config-form [data-feate]')).filter(option => option.getAttribute('data-feature') === feature).forEach(option => {
+ option.click()
+ })
+ }
+ } catch (error) {
+ console.error('Failed to init options:', error);
+ }
}
function checkedOptions() {
@@ -470,23 +480,23 @@
current project: None
document.querySelector('#aimCargoTomlTextArea').value = (await res.json()).data;
}
+
function change_option_state() {
+ this.classList.toggle('unchecked');
+ this.classList.toggle('checked');
+ let self_checked = this.classList.contains('checked');
+
let group = this.parentElement;
+
if (group.classList.contains('exclusive')) {
- group.querySelectorAll('.checked').forEach(option => {
+ [...group.querySelectorAll('.checked')].filter(o => o !== this).forEach(option => {
option.classList.toggle('checked')
option.classList.toggle('unchecked')
})
}
- this.classList.toggle('unchecked');
- this.classList.toggle('checked');
-
- let state = this.classList.contains('checked')
-
-
if (this.classList.contains('all-options')) {
- if (state) {
+ if (self_checked) {
group.querySelectorAll('.unchecked').forEach(option => {
option.classList.replace('unchecked', 'checked')
})
@@ -496,7 +506,7 @@
current project: None
})
}
} else {
- if (state) {
+ if (self_checked) {
if (group.querySelectorAll('.unchecked').length === 1) {
group.querySelectorAll('.all-options').forEach(option => {
option.classList.replace('unchecked', 'checked')
@@ -509,6 +519,17 @@
current project: None
}
}
+ const ormOption = document.querySelector('#zino-config-form [data-feature="orm"]');
+ const ormForm = document.getElementById('orm-form');
+ if (ormOption && ormOption.classList.contains('checked')) {
+ ormForm.classList.remove('hidden');
+ } else {
+ ormForm.classList.add('hidden');
+ ormForm.querySelectorAll('.option-group div').forEach(option => {
+ option.classList.replace('checked', 'unchecked');
+ });
+ }
+
generateCargoToml();
}
@@ -537,10 +558,30 @@
current project: None
});
});
- window.onload = () => {
- fetchCurrentDir();
- fetchCargoToml();
- init_options();
+ // save the generated Cargo.toml
+ document.getElementById('save-config').addEventListener('click', async () => {
+ const aimCargoToml = document.getElementById('aimCargoTomlTextArea').value;
+ try {
+ const response = await fetch('/save_cargo_toml', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(aimCargoToml),
+ });
+ if (!response.ok) {
+ throw new Error(await response.text());
+ }
+ await fetchCargoToml();
+ } catch (error) {
+ console.error('Failed to save Cargo.toml:', error);
+ }
+ });
+
+ window.onload = async () => {
+ await fetchCurrentDir();
+ await fetchCargoToml();
+ await fetchFeatures();
};
diff --git a/zino-cli/src/cli/init.rs b/zino-cli/src/cli/init.rs
index 27e1079f..55f6e172 100644
--- a/zino-cli/src/cli/init.rs
+++ b/zino-cli/src/cli/init.rs
@@ -24,6 +24,7 @@ impl Init {
return Err(Error::new("current directory is already a Rust project"));
}
let init_res = self.init_with_template();
+ // must clean the temporary template directory after the initialization
clean_template_dir(TEMPORARY_TEMPLATE_PATH);
match init_res {
Ok(_) => {
diff --git a/zino-cli/src/cli/new.rs b/zino-cli/src/cli/new.rs
index b3e9cfad..717b0d1b 100644
--- a/zino-cli/src/cli/new.rs
+++ b/zino-cli/src/cli/new.rs
@@ -21,6 +21,7 @@ impl New {
pub fn run(self) -> Result<(), Error> {
let project_dir_already_exists = self.check_project_dir_status()?;
let new_res = self.new_with_template();
+ // must clean the temporary template directory after the initialization
clean_template_dir(TEMPORARY_TEMPLATE_PATH);
match new_res {
Ok(_) => {
diff --git a/zino-cli/src/cli/serve.rs b/zino-cli/src/cli/serve.rs
index f1c7c00f..5f6f9683 100644
--- a/zino-cli/src/cli/serve.rs
+++ b/zino-cli/src/cli/serve.rs
@@ -6,7 +6,7 @@ use axum::{
};
use clap::Parser;
use include_dir::Dir;
-use serde::Deserialize;
+use serde::{Deserialize, Serialize};
use std::{env, fs};
use toml_edit::{Array, DocumentMut as Document};
use zino::prelude::*;
@@ -34,7 +34,9 @@ impl Serve {
.route("/current_dir", get(get_current_dir))
.route("/update_current_dir/:path", post(update_current_dir))
.route("/get_current_cargo_toml", get(get_current_cargo_toml))
- .route("/generate_cargo_toml", post(generate_cargo_toml))])
+ .route("/generate_cargo_toml", post(generate_cargo_toml))
+ .route("/save_cargo_toml", post(save_cargo_toml))
+ .route("/get_current_features", get(get_current_features))])
.run();
Ok(())
}
@@ -116,13 +118,62 @@ async fn update_current_dir(Path(path): Path
) -> impl IntoResponse {
}
/// Features struct.
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, Default)]
struct Features {
zino_feature: Vec,
core_feature: Vec,
}
-/// Generates dependencies in `Cargo.toml` file.
+impl Features {
+ /// Get zino-features and zino-core-features from path to Cargo.toml
+ fn from_path(path: &str) -> Self {
+ let cargo_toml = fs::read_to_string(path)
+ .unwrap_or_default()
+ .parse::()
+ .unwrap_or_default();
+
+ if cargo_toml.get("dependencies").is_none() {
+ return Self::default();
+ }
+
+ let zino_features = if cargo_toml["dependencies"].get("zino").is_none()
+ || cargo_toml["dependencies"]["zino"].get("features").is_none()
+ {
+ vec![]
+ } else {
+ match cargo_toml["dependencies"]["zino"]["features"].as_array() {
+ Some(features) => features
+ .iter()
+ .map(|f| f.as_str().unwrap_or_default().to_string())
+ .collect(),
+ None => vec![],
+ }
+ };
+
+ let core_features = if cargo_toml["dependencies"].get("zino-core").is_none()
+ || cargo_toml["dependencies"]["zino-core"]
+ .get("features")
+ .is_none()
+ {
+ vec![]
+ } else {
+ match cargo_toml["dependencies"]["zino-core"]["features"].as_array() {
+ Some(features) => features
+ .iter()
+ .map(|f| f.as_str().unwrap_or_default().to_string())
+ .collect(),
+ None => vec![],
+ }
+ };
+
+ Self {
+ zino_feature: zino_features,
+ core_feature: core_features,
+ }
+ }
+}
+
+/// Generates zino-features and zino-core-features from user select options
async fn generate_cargo_toml(mut req: zino::Request) -> zino::Result {
let mut res = zino::Response::default().context(&req);
let body = req.parse_body::().await?;
@@ -135,7 +186,7 @@ async fn generate_cargo_toml(mut req: zino::Request) -> zino::Result {
return Ok(res.into());
}
};
- let mut cargo_toml = match current_cargo_toml_content.parse::(){
+ let mut cargo_toml = match current_cargo_toml_content.parse::() {
Ok(doc) => doc,
Err(err) => {
res.set_code(axum::http::StatusCode::INTERNAL_SERVER_ERROR);
@@ -168,7 +219,6 @@ async fn generate_cargo_toml(mut req: zino::Request) -> zino::Result {
cargo_toml["dependencies"]["zino-core"]["features"] = toml_edit::value(core_feature);
}
-
let options = taplo::formatter::Options {
compact_arrays: false,
compact_inline_tables: false,
@@ -182,3 +232,30 @@ async fn generate_cargo_toml(mut req: zino::Request) -> zino::Result {
res.set_data(&formatted_toml);
Ok(res.into())
}
+
+/// Returns a `Features` struct from current_dir/Cargo.toml.
+async fn get_current_features(req: zino::Request) -> zino::Result {
+ let mut res = zino::Response::default().context(&req);
+ let features = Features::from_path("./Cargo.toml");
+ res.set_content_type("application/json");
+ res.set_data(&features);
+ Ok(res.into())
+}
+
+/// Saves the content of `Cargo.toml` file.
+async fn save_cargo_toml(mut req: zino::Request) -> zino::Result {
+ let mut res = zino::Response::default().context(&req);
+
+ let body = req.parse_body::().await?;
+
+ match fs::write("./Cargo.toml", body) {
+ Ok(_) => {
+ res.set_code(axum::http::StatusCode::OK);
+ }
+ Err(err) => {
+ res.set_code(axum::http::StatusCode::INTERNAL_SERVER_ERROR);
+ res.set_data(&err.to_string());
+ }
+ }
+ Ok(res.into())
+}