From 01b1d79d75e61d0fdc9a4485322b5857c1ce7493 Mon Sep 17 00:00:00 2001 From: kjuulh Date: Thu, 31 Jul 2025 21:59:47 +0200 Subject: [PATCH] feat: update project --- Cargo.lock | 79 +++++++++++++ .../norun-grpc-interface/src/grpc/norun.v1.rs | 21 ++-- crates/norun/Cargo.toml | 3 + crates/norun/src/main.rs | 2 + crates/norun/src/models.rs | 14 +++ crates/norun/src/services.rs | 1 + crates/norun/src/services/project_registry.rs | 104 ++++++++++++++++++ 7 files changed, 213 insertions(+), 11 deletions(-) create mode 100644 crates/norun/src/services.rs create mode 100644 crates/norun/src/services/project_registry.rs diff --git a/Cargo.lock b/Cargo.lock index fca6cc6..28d62e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,6 +215,9 @@ name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "bollard" @@ -369,6 +372,27 @@ version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -912,6 +936,16 @@ version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +[[package]] +name = "libredox" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "litemap" version = "0.8.0" @@ -990,12 +1024,14 @@ dependencies = [ "bollard", "bytes", "clap", + "dirs", "futures-util", "norun-grpc-interface", "notmad", "pretty_assertions", "prost", "prost-types", + "ron", "serde", "tokio", "tokio-util", @@ -1003,6 +1039,7 @@ dependencies = [ "tonic", "tracing", "tracing-subscriber", + "uuid", ] [[package]] @@ -1079,6 +1116,12 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "overload" version = "0.1.1" @@ -1304,6 +1347,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror", +] + [[package]] name = "ref-cast" version = "1.0.24" @@ -1368,6 +1422,19 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "ron" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f" +dependencies = [ + "base64", + "bitflags", + "serde", + "serde_derive", + "unicode-ident", +] + [[package]] name = "rustc-demangle" version = "0.1.25" @@ -1924,6 +1991,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.3", + "js-sys", + "serde", + "wasm-bindgen", +] + [[package]] name = "valuable" version = "0.1.1" diff --git a/crates/norun-grpc-interface/src/grpc/norun.v1.rs b/crates/norun-grpc-interface/src/grpc/norun.v1.rs index da22b28..ef745dc 100644 --- a/crates/norun-grpc-interface/src/grpc/norun.v1.rs +++ b/crates/norun-grpc-interface/src/grpc/norun.v1.rs @@ -3,42 +3,41 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct PublishRequest { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub project: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, Copy, PartialEq, ::prost::Message)] -pub struct PublishResponse { -} +pub struct PublishResponse {} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetTopicRequest { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub topic: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetTopicResponse { - #[prost(message, optional, tag="1")] + #[prost(message, optional, tag = "1")] pub projects: ::core::option::Option, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Projects { - #[prost(message, repeated, tag="1")] + #[prost(message, repeated, tag = "1")] pub projects: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Project { - #[prost(string, tag="1")] + #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, - #[prost(string, tag="2")] + #[prost(string, tag = "2")] pub image: ::prost::alloc::string::String, - #[prost(string, tag="3")] + #[prost(string, tag = "3")] pub version: ::prost::alloc::string::String, - #[prost(uint32, optional, tag="4")] + #[prost(uint32, optional, tag = "4")] pub port: ::core::option::Option, } include!("norun.v1.tonic.rs"); -// @@protoc_insertion_point(module) \ No newline at end of file +// @@protoc_insertion_point(module) diff --git a/crates/norun/Cargo.toml b/crates/norun/Cargo.toml index 13c0bfe..c27038b 100644 --- a/crates/norun/Cargo.toml +++ b/crates/norun/Cargo.toml @@ -23,6 +23,9 @@ async-trait = "0.1.88" notmad = "0.7.2" bollard = "0.19.1" futures-util = "0.3.31" +dirs = "6.0.0" +uuid = { version = "1.17.0", features = ["serde", "v4"] } +ron = "0.10.1" [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/crates/norun/src/main.rs b/crates/norun/src/main.rs index 233653c..2a84eae 100644 --- a/crates/norun/src/main.rs +++ b/crates/norun/src/main.rs @@ -7,6 +7,8 @@ mod state; mod server; +mod services; + mod grpc_client; mod grpc_server; diff --git a/crates/norun/src/models.rs b/crates/norun/src/models.rs index 3c85bf5..13e1409 100644 --- a/crates/norun/src/models.rs +++ b/crates/norun/src/models.rs @@ -1,5 +1,7 @@ pub mod project_tag; pub use project_tag::*; +use serde::{Deserialize, Serialize}; +use uuid::Uuid; pub mod port { #[derive(Clone, Debug, PartialEq)] @@ -8,3 +10,15 @@ pub mod port { pub container_port: usize, } } + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub struct Project { + pub id: Uuid, + pub spec: ProjectSpec, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +pub enum ProjectSpec { + Compose {}, + Container {}, +} diff --git a/crates/norun/src/services.rs b/crates/norun/src/services.rs new file mode 100644 index 0000000..faf232a --- /dev/null +++ b/crates/norun/src/services.rs @@ -0,0 +1 @@ +pub mod project_registry; diff --git a/crates/norun/src/services/project_registry.rs b/crates/norun/src/services/project_registry.rs new file mode 100644 index 0000000..8274df0 --- /dev/null +++ b/crates/norun/src/services/project_registry.rs @@ -0,0 +1,104 @@ +use std::path::PathBuf; + +use anyhow::Context; +use tokio::io::AsyncWriteExt; + +use crate::{models::Project, state::ClientState}; + +pub struct ProjectRegistry { + state_dir: PathBuf, +} + +impl ProjectRegistry { + pub async fn get_project(&self, project: &Project) -> anyhow::Result> { + let project_dir = self.project_file(project)?; + + if !project_dir.exists() { + return Ok(None); + } + + let project_content = tokio::fs::read_to_string(&project_dir) + .await + .context("failed to read ron file")?; + + let project: Project = ron::from_str(&project_content) + .context(format!("failed to read: '{}'", project_dir.display()))?; + + Ok(Some(project)) + } + + pub async fn create_project(&self, project: &Project) -> anyhow::Result<()> { + match self.get_project(project).await { + Ok(_) => anyhow::bail!("project already exists"), + Err(_) => { + // continue + } + } + + let project_file_path = self.project_file(project)?; + if let Some(project_file) = project_file_path.parent() { + tokio::fs::create_dir_all(project_file) + .await + .context("create ron project dir")?; + } + + let mut project_file = tokio::fs::File::create_new(&project_file_path) + .await + .context("create project file")?; + + let project_content = ron::to_string(project)?; + + project_file + .write_all(&project_content.as_bytes()) + .await + .context("write project file")?; + + Ok(()) + } + + pub async fn update_project(&self, project: &Project) -> anyhow::Result<()> { + let project_file_path = self.project_file(project)?; + if let Some(project_file) = project_file_path.parent() { + tokio::fs::create_dir_all(project_file) + .await + .context("update ron project dir")?; + } + + let mut project_file = tokio::fs::File::create_new(&project_file_path) + .await + .context("update project file")?; + + let project_content = ron::to_string(project)?; + + project_file + .write_all(project_content.as_bytes()) + .await + .context("update project file")?; + + Ok(()) + } + + fn project_file(&self, project: &Project) -> anyhow::Result { + let project_dir = self + .state_dir + .join(project.id.to_string()) + .join("project.ron"); + + Ok(project_dir) + } +} + +pub trait ProjectRegistryState { + fn project_registry(&self) -> ProjectRegistry; +} + +impl ProjectRegistryState for ClientState { + fn project_registry(&self) -> ProjectRegistry { + ProjectRegistry { + state_dir: dirs::state_dir() + .expect("to be able to find state") + .join("norun") + .join("projects"), + } + } +}