diff --git a/CHANGELOG.md b/CHANGELOG.md index b03874c..2c5b0b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), ### Added +- Full two-way sync - Support for `legacy_scripts` and `keep_unknowns` fields - Ability to re-release the same version when needed - Virtual file system for testing diff --git a/crates/LICENSE.txt b/crates/rbx_dom_weak/LICENSE.txt similarity index 100% rename from crates/LICENSE.txt rename to crates/rbx_dom_weak/LICENSE.txt diff --git a/src/core/meta.rs b/src/core/meta.rs index 4cdf76e..4e10776 100644 --- a/src/core/meta.rs +++ b/src/core/meta.rs @@ -116,7 +116,7 @@ impl Source { pub fn project(name: &str, path: &Path, node: ProjectNode, node_path: NodePath) -> Self { Self { inner: SourceKind::Project(name.to_owned(), path.to_owned(), node, node_path), - relevant: vec![SourceEntry::Project(path.to_owned())], + relevant: vec![], } } @@ -137,6 +137,10 @@ impl Source { self.add_relevant(SourceEntry::Data(path.to_owned())) } + pub fn add_project(&mut self, path: &Path) { + self.add_relevant(SourceEntry::Project(path.to_owned())) + } + pub fn remove_data(&mut self) { self.relevant.retain(|entry| !matches!(entry, SourceEntry::Data(_))) } diff --git a/src/core/mod.rs b/src/core/mod.rs index c338ea3..4d3c752 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -197,14 +197,7 @@ impl Core { }) } - let mut sourcemap = walk(&tree, dom.root_ref(), non_scripts); - - // We need to add root project path manually - // as we ignore other project paths by default - if let Some(sourcemap) = &mut sourcemap { - let root_path = tree.get_meta(tree.root_ref()).unwrap().source.paths()[0].to_owned(); - sourcemap.file_paths.push(root_path); - } + let sourcemap = walk(&tree, dom.root_ref(), non_scripts); if let Some(path) = path { let writer = BufWriter::new(File::create(path)?); diff --git a/src/core/processor/mod.rs b/src/core/processor/mod.rs index a80e355..cc42dce 100644 --- a/src/core/processor/mod.rs +++ b/src/core/processor/mod.rs @@ -8,7 +8,7 @@ use std::{ time::{Duration, Instant}, }; -use super::{changes::Changes, queue::Queue, tree::Tree}; +use super::{changes::Changes, queue::Queue, snapshot::Snapshot, tree::Tree}; use crate::{ argon_error, constants::{BLACKLISTED_PATHS, CHANGES_TRESHOLD}, @@ -67,6 +67,10 @@ impl Processor { pub fn write(&self, changes: Changes) { self.writer.send(changes).unwrap(); } + + pub fn write_all(&self, _snapshot: Snapshot) { + // TODO + } } struct Handler { diff --git a/src/middleware/project.rs b/src/middleware/project.rs index df2bd94..a21493e 100644 --- a/src/middleware/project.rs +++ b/src/middleware/project.rs @@ -21,11 +21,13 @@ use crate::{ pub fn read_project(path: &Path, vfs: &Vfs) -> Result { let project: Project = Project::load(path)?; - let mut meta = Meta::from_project(&project); + let meta = Meta::from_project(&project); let mut snapshot = new_snapshot_node(&project.name, path, project.node, NodePath::new(), &meta.context, vfs)?; - meta.source = snapshot.meta.source.clone(); - snapshot.set_meta(meta); + let mut source = Source::file(path).with_relevants(snapshot.meta.source.relevants().to_owned()); + source.add_project(path); + + snapshot.set_meta(meta.with_source(source)); vfs.watch(path)?; diff --git a/src/server/mod.rs b/src/server/mod.rs index b906116..a7d4c92 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -11,11 +11,12 @@ mod exec; mod home; mod open; mod read; -mod snapshot; +mod read_all; mod stop; mod subscribe; mod unsubscribe; mod write; +mod write_all; async fn default_redirect() -> impl Responder { web::Redirect::to("/") @@ -50,7 +51,8 @@ impl Server { .service(stop::main) .service(read::main) .service(write::main) - .service(snapshot::main) + .service(read_all::main) + .service(write_all::main) .service(exec::main) .service(open::main) .default_service(web::to(default_redirect)) diff --git a/src/server/read_all.rs b/src/server/read_all.rs new file mode 100644 index 0000000..0fc2522 --- /dev/null +++ b/src/server/read_all.rs @@ -0,0 +1,28 @@ +use actix_msgpack::MsgPackResponseBuilder; +use actix_web::{ + get, + web::{Data, Query}, + HttpResponse, Responder, +}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use crate::core::{snapshot::Snapshot, Core}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Request { + client_id: u32, +} + +#[derive(Serialize)] +struct Response(Snapshot); + +#[get("/readAll")] +async fn main(request: Query, core: Data>) -> impl Responder { + if !core.queue().is_subscribed(request.client_id) { + return HttpResponse::Unauthorized().body("Not subscribed"); + } + + HttpResponse::Ok().msgpack(core.snapshot()) +} diff --git a/src/server/snapshot.rs b/src/server/snapshot.rs deleted file mode 100644 index 5c97f95..0000000 --- a/src/server/snapshot.rs +++ /dev/null @@ -1,14 +0,0 @@ -use actix_msgpack::MsgPackResponseBuilder; -use actix_web::{get, web::Data, HttpResponse, Responder}; -use serde::Serialize; -use std::sync::Arc; - -use crate::core::{snapshot::Snapshot, Core}; - -#[derive(Serialize)] -struct Response(Snapshot); - -#[get("/snapshot")] -async fn main(core: Data>) -> impl Responder { - HttpResponse::Ok().msgpack(core.snapshot()) -} diff --git a/src/server/write_all.rs b/src/server/write_all.rs new file mode 100644 index 0000000..a410388 --- /dev/null +++ b/src/server/write_all.rs @@ -0,0 +1,30 @@ +use actix_web::{ + get, + web::{Data, Query}, + HttpResponse, Responder, +}; +use serde::{Deserialize, Serialize}; +use std::sync::Arc; + +use crate::core::{snapshot::Snapshot, Core}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct Request { + client_id: u32, + snapshot: Snapshot, +} + +#[derive(Serialize)] +struct Response(Snapshot); + +#[get("/writeAll")] +async fn main(request: Query, core: Data>) -> impl Responder { + if !core.queue().is_subscribed(request.client_id) { + return HttpResponse::Unauthorized().body("Not subscribed"); + } + + core.processor().write_all(request.snapshot.clone()); + + HttpResponse::Ok().body("Written all changes successfully") +} diff --git a/src/stats.rs b/src/stats.rs index cb632af..efafe3a 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -3,6 +3,7 @@ use lazy_static::lazy_static; use log::{debug, warn}; use reqwest::blocking::Client; use serde::{Deserialize, Serialize}; +use serde_json::json; use std::{ fs, sync::RwLock, @@ -26,7 +27,6 @@ macro_rules! stat_fn { #[derive(Clone, Debug, Default, Serialize, Deserialize)] struct ArgonStats { - #[serde(rename(serialize = "hours_used"))] minutes_used: u32, files_synced: u32, lines_synced: u32, @@ -36,14 +36,23 @@ struct ArgonStats { } impl ArgonStats { - fn len(&self) -> u32 { - self.minutes_used + fn total(&self) -> u32 { + self.minutes_used / 60 + self.files_synced + self.lines_synced + self.projects_created + self.projects_built + self.sessions_started } + + fn extend(&mut self, other: &ArgonStats) { + self.minutes_used += other.minutes_used; + self.files_synced += other.files_synced; + self.lines_synced += other.lines_synced; + self.projects_created += other.projects_created; + self.projects_built += other.projects_built; + self.sessions_started += other.sessions_started; + } } #[derive(Debug, Serialize, Deserialize)] @@ -52,6 +61,20 @@ struct StatTracker { stats: ArgonStats, } +impl StatTracker { + fn reset(&mut self) { + self.stats = ArgonStats::default(); + } + + fn merge(&mut self, other: Self) { + if other.last_synced > self.last_synced { + self.last_synced = other.last_synced; + } + + self.stats.extend(&other.stats); + } +} + impl Default for StatTracker { fn default() -> Self { Self { @@ -89,12 +112,19 @@ fn set_tracker(tracker: &StatTracker) -> Result<()> { pub fn track() -> Result<()> { let mut tracker = get_tracker()?; - if tracker.last_synced.elapsed()?.as_secs() > 3600 && tracker.stats.len() > 10 { + if tracker.last_synced.elapsed()?.as_secs() > 3600 && tracker.stats.total() > 10 { if let Some(token) = option_env!("AUTH_TOKEN") { - let mut stats = tracker.stats.clone(); - let remaining_minutes = stats.minutes_used % 60; - - stats.minutes_used /= 60; + let stats = tracker.stats; + let remainder = stats.minutes_used % 60; + + let stats = json!({ + "hours_used": stats.minutes_used / 60, + "files_synced": stats.files_synced, + "lines_synced": stats.lines_synced, + "projects_created": stats.projects_created, + "projects_built": stats.projects_built, + "sessions_started": stats.sessions_started, + }); Client::new() .post(format!("https://api.argon.wiki/push?auth={}", token)) @@ -104,11 +134,11 @@ pub fn track() -> Result<()> { tracker.last_synced = SystemTime::now(); tracker.stats = ArgonStats::default(); - tracker.stats.minutes_used = remaining_minutes; + tracker.stats.minutes_used = remainder; set_tracker(&tracker)?; } else { - warn!("This Argon build has no `AUTH_TOKEN` set, stats will not be synced") + warn!("This Argon build has no `AUTH_TOKEN` set, stats will not be uploaded") } } else { debug!("Stats already synced within the last hour or too few stats to sync"); @@ -128,9 +158,14 @@ pub fn track() -> Result<()> { } pub fn save() -> Result<()> { - let tracker = TRACKER.read().unwrap(); + let mut tracker = TRACKER.write().unwrap(); + + if let Ok(old) = get_tracker() { + tracker.merge(old); + } set_tracker(&tracker)?; + tracker.reset(); Ok(()) }