diff --git a/gdriver-backend/src/drive.rs b/gdriver-backend/src/drive.rs index d547439..02cb1ab 100644 --- a/gdriver-backend/src/drive.rs +++ b/gdriver-backend/src/drive.rs @@ -1,10 +1,10 @@ -use std::collections::HashMap; - -use crate::drive::google_drive::GoogleDrive; +use crate::apply_change; +use crate::drive::google_drive::{FileData, GoogleDrive}; use crate::path_resolver::PathResolver; use chrono::{DateTime, Utc}; -use gdriver_common::drive_structure::meta::{write_metadata_file, write_metadata_file_to_path}; -use gdriver_common::ipc::gdriver_service::ReadDirResult; +use gdriver_common::drive_structure::meta::{read_metadata_by_id, write_metadata_file, Metadata}; +use google_drive3::api::Change; +use std::collections::HashMap; use crate::prelude::*; mod google_drive; @@ -47,16 +47,11 @@ impl Drive { self.path_resolver.reset()?; for file in files { - for parent in file.parents.clone() { - let relation_data = ReadDirResult { - id: file.id.clone().into(), - name: file.name.clone(), - kind: file.kind.clone(), - }; - self.path_resolver - .add_relationship(parent.into(), relation_data)?; - } + let parents = file.parents.clone(); let meta = file.into_meta()?; + self.path_resolver + .add_relationships_for_meta(parents, &meta)?; + write_metadata_file(&meta)?; } } else { @@ -83,11 +78,81 @@ impl Drive { return Ok(()); } for change in changes { - dbg!(&change); + // dbg!(&change); + self.process_change(change)?; } - Err("Not implemented".into()) + Ok(()) //TODO: implement this + } + #[instrument(skip(self, change))] + fn process_change(&mut self, change: Change) -> Result<()> { + let file_data = + FileData::convert_from_api_file(change.file.ok_or("change had no file data")?); + let parents = file_data + .parents + .clone() + .into_iter() + .map(Into::into) + .collect(); + let new_meta = file_data.into_meta()?; + info!("Processing change: {:?}", new_meta); + let id: DriveId = change.file_id.clone().ok_or("No file id in change")?.into(); + let original_meta = read_metadata_by_id(&id); + if original_meta.is_err() { + info!("File not found so it has to be new: {:?}", id); + self.path_resolver + .add_relationships_for_meta(parents, &new_meta)?; + write_metadata_file(&new_meta)?; + return Ok(()); + } + if change.removed.unwrap_or_default() { + info!("File removed: {:?}", id); + todo!("Do something when a file is removed from drive"); + return Ok(()); + } + let mut original_meta = original_meta?; + self.process_parents_changes(parents, &id, &new_meta)?; + Self::process_meta_changes(new_meta, &mut original_meta)?; + Ok(()) + } - // Ok(()) //TODO: implement this + fn process_meta_changes(new_meta: Metadata, original_meta: &mut Metadata) -> Result<()> { + let mut has_meta_changed = false; + + apply_change!(original_meta, new_meta, last_modified, has_meta_changed, where: { + original_meta.last_modified < new_meta.last_modified + }); + apply_change!(original_meta, new_meta, last_accessed, has_meta_changed, where: { + original_meta.last_accessed < new_meta.last_accessed + }); + apply_change!(original_meta, new_meta, last_metadata_changed, has_meta_changed, where: { + original_meta.last_metadata_changed < new_meta.last_metadata_changed + }); + apply_change!(original_meta, new_meta, name, has_meta_changed); + apply_change!(original_meta, new_meta, size, has_meta_changed); + apply_change!(original_meta, new_meta, permissions, has_meta_changed); + apply_change!(original_meta, new_meta, extra_attributes, has_meta_changed); + info!("Has changed: {}", has_meta_changed); + if has_meta_changed { + write_metadata_file(&original_meta)?; + } + Ok(()) + } + + fn process_parents_changes( + &mut self, + parents: Vec, + id: &DriveId, + new_meta: &Metadata, + ) -> Result<()> { + let original_parents = self.path_resolver.get_parents(&id)?.clone(); + if original_parents != parents { + info!("Parents changed: {:?}", id); + self.path_resolver + .remove_relationships_for_id(&original_parents, &new_meta.id)?; + self.path_resolver + .add_relationships_for_meta(parents, &new_meta)?; + } + Ok(()) } #[instrument(skip(self))] @@ -100,3 +165,4 @@ pub enum TrackingState { Untracked, Tracked(DateTime), } +mod macros; diff --git a/gdriver-backend/src/drive/google_drive.rs b/gdriver-backend/src/drive/google_drive.rs index 0a7ffe0..117ce45 100644 --- a/gdriver-backend/src/drive/google_drive.rs +++ b/gdriver-backend/src/drive/google_drive.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use chrono::{DateTime, Utc}; use const_format::formatcp; -use gdriver_common::drive_structure::meta::{FileKind, FileState, Metadata}; +use gdriver_common::drive_structure::meta::{FileKind, FileState, Metadata, DEFAULT_PERMISSIONS}; use gdriver_common::time_utils::datetime_to_timestamp; use gdriver_common::{ipc::gdriver_service::SETTINGS, prelude::*}; use google_drive3::api::File; @@ -38,19 +38,20 @@ impl FileData { Ok(Metadata { id: self.id.into(), kind: self.kind, + name: self.name, size: self.size.unwrap_or_default() as u64, last_accessed: datetime_to_timestamp(self.viewed_by_me_time.unwrap_or_default())?, last_modified, extra_attributes: Default::default(), state: FileState::MetadataOnly, - permissions: 0, //TODO: parse permissions + permissions: DEFAULT_PERMISSIONS, //TODO: parse permissions last_metadata_changed: last_modified, }) } } impl FileData { - fn convert_from_api_file(file: File) -> Self { + pub(crate) fn convert_from_api_file(file: File) -> Self { let kind = file.kind.unwrap_or_default(); info!( "Converting file with id {:?} with parent: {:?}", @@ -221,7 +222,7 @@ impl GoogleDrive { Err("Did not get expected result on ping".into()) } //region changes - #[instrument] + #[instrument(skip(self))] pub async fn get_changes(&mut self) -> Result> { info!("Getting changes"); let mut page_token = Some(self.get_change_start_token().await?); @@ -260,6 +261,9 @@ impl GoogleDrive { } async fn set_change_start_token(&mut self, token: String) -> Result<()> { + if self.changes_start_page_token.as_ref() == Some(&token) { + return Ok(()); + } info!("Setting start page token: {}", token); fs::write(SETTINGS.get_changes_file_path(), token.clone()).await?; self.changes_start_page_token = Some(token); @@ -297,7 +301,8 @@ impl GoogleDrive { pub async fn get_local_change_start_token(&mut self) -> Option { self.changes_start_page_token = fs::read_to_string(SETTINGS.get_changes_file_path()) .await - .ok(); + .ok() + .map(|s| s.trim().to_string()); self.changes_start_page_token.clone() } async fn update_change_start_token_from_api(&mut self) -> Result<()> { @@ -342,7 +347,7 @@ impl GoogleDrive { info!("replacing {id} with {}", ROOT_ID.as_ref()); *id = ROOT_ID.0.clone(); } else { - info!("{id} did not match {}", self.root_alt_id); + // info!("{id} did not match {}", self.root_alt_id); } } //endregion diff --git a/gdriver-backend/src/drive/macros.rs b/gdriver-backend/src/drive/macros.rs new file mode 100644 index 0000000..5835e1b --- /dev/null +++ b/gdriver-backend/src/drive/macros.rs @@ -0,0 +1,27 @@ +mod apply_change { + #[macro_export] + macro_rules! apply_change { + ($meta:ident, $changed_meta:ident, $field:ident, $has_changed:expr) => { + apply_change!($meta, $changed_meta, $field, $has_changed, where:true); + }; + ($meta:ident, $changed_meta:ident, $field:ident, $has_changed:expr, where:$extra_condition:expr) => { + apply_change!( + $meta, + $changed_meta, + $field, + $has_changed, + $extra_condition, + { + ::tracing::info!("{} changed from '{:?}' to '{:?}'", stringify!($field), $meta.$field, $changed_meta.$field); + $has_changed |= true; + $meta.$field = $changed_meta.$field.clone(); + } + ); + }; + ($meta:ident, $changed_meta:ident, $field:ident, $has_changed:expr, $extra_condition:expr, $action_on_difference:expr) => { + if $meta.$field != $changed_meta.$field && $extra_condition { + $action_on_difference + } + }; + } +} diff --git a/gdriver-backend/src/path_resolver.rs b/gdriver-backend/src/path_resolver.rs index 290f3b4..9373283 100644 --- a/gdriver-backend/src/path_resolver.rs +++ b/gdriver-backend/src/path_resolver.rs @@ -1,5 +1,5 @@ -use crate::drive::Drive; use crate::prelude::*; +use gdriver_common::drive_structure::meta::Metadata; use gdriver_common::ipc::gdriver_service::{ReadDirResult, SETTINGS}; use gdriver_common::path_resolve_error::PathResolveError; use gdriver_common::prelude::*; @@ -20,6 +20,9 @@ impl PathResolver { pub(crate) fn get_children(&self, id: &DriveId) -> Result<&Vec> { self.children.get(id).ok_or("Item with ID not found".into()) } + pub(crate) fn get_parents(&self, id: &DriveId) -> Result<&Vec> { + self.parents.get(id).ok_or("Item with ID not found".into()) + } } impl PathResolver { @@ -61,6 +64,21 @@ impl PathResolver { self.write_to_disk()?; Ok(()) } + pub(crate) fn add_relationships_for_meta( + &mut self, + parents: Vec>, + meta: &Metadata, + ) -> Result<()> { + let entry = ReadDirResult { + id: meta.id.clone().into(), + name: meta.name.to_string(), + kind: meta.kind.clone(), + }; + for parent in parents { + self.add_relationship(parent.into(), entry.clone())?; + } + Ok(()) + } /// Add a relationship between a parent and a child and write to disk pub(crate) fn add_relationship(&mut self, parent: DriveId, entry: ReadDirResult) -> Result<()> { match self.parents.get_mut(&entry.id) { @@ -78,18 +96,23 @@ impl PathResolver { self.write_to_disk()?; Ok(()) } - /// Remove the relationship between a parent and a child and write to disk - pub(crate) fn remove_relationship( + + pub(crate) fn remove_relationships_for_id( &mut self, - parent: DriveId, - entry: ReadDirResult, + parents: &Vec, + id: &DriveId, ) -> Result<()> { - self.parents - .get_mut(&entry.id) - .map(|x| x.retain(|e| e != &parent)); + for parent in parents { + self.remove_relationship(parent, id)?; + } + Ok(()) + } + /// Remove the relationship between a parent and a child and write to disk + pub(crate) fn remove_relationship(&mut self, parent: &DriveId, id: &DriveId) -> Result<()> { + self.parents.get_mut(id).map(|x| x.retain(|e| e != parent)); self.children - .get_mut(&parent) - .map(|x| x.retain(|e| e.id != entry.id)); + .get_mut(parent) + .map(|x| x.retain(|e| e.id != *id)); self.write_to_disk()?; Ok(()) } diff --git a/gdriver-backend/src/service.rs b/gdriver-backend/src/service.rs index 074a4b6..1bae5f7 100644 --- a/gdriver-backend/src/service.rs +++ b/gdriver-backend/src/service.rs @@ -248,6 +248,7 @@ pub async fn start() -> Result<()> { } } drive.get_all_file_metas().await?; + drive.update().await?; let drive = Arc::new(Mutex::new(drive)); let server_addr = (config.ip, config.port); diff --git a/gdriver-common/src/drive_structure/meta.rs b/gdriver-common/src/drive_structure/meta.rs index afcc31a..829a25f 100644 --- a/gdriver-common/src/drive_structure/meta.rs +++ b/gdriver-common/src/drive_structure/meta.rs @@ -13,6 +13,7 @@ pub type TIMESTAMP = (i64, u32); pub struct Metadata { pub id: DriveId, pub state: FileState, + pub name: String, pub size: u64, pub last_accessed: TIMESTAMP, pub last_modified: TIMESTAMP, @@ -22,13 +23,15 @@ pub struct Metadata { pub extra_attributes: BTreeMap, Vec>, } -const PERMISSIONS_RWXRWXRWX: u16 = 0b111_111_111; // 511; +pub const PERMISSIONS_RWXRWXRWX: u16 = 0o777; +pub const DEFAULT_PERMISSIONS: u16 = PERMISSIONS_RWXRWXRWX; impl Metadata { pub fn root() -> Self { Self { id: ROOT_ID.clone(), state: FileState::Root, + name: "".to_string(), size: 0, last_accessed: time_now(), last_modified: time_now(), @@ -45,6 +48,10 @@ pub fn read_metadata_file(path: &Path) -> Result { let reader = File::open(path)?; Ok(serde_json::from_reader(reader)?) } +pub fn read_metadata_by_id(id: &DriveId) -> Result { + let path = SETTINGS.get_metadata_file_path(id); + read_metadata_file(&path) +} pub fn write_metadata_file(metadata: &Metadata) -> Result<()> { let path = SETTINGS.get_metadata_file_path(&metadata.id); write_metadata_file_to_path(&path, metadata) diff --git a/gdriver-common/src/ipc/gdriver_settings.rs b/gdriver-common/src/ipc/gdriver_settings.rs index 369c92a..1f91e3a 100644 --- a/gdriver-common/src/ipc/gdriver_settings.rs +++ b/gdriver-common/src/ipc/gdriver_settings.rs @@ -11,7 +11,7 @@ pub struct GDriverSettings { data_path: PathBuf, } impl GDriverSettings { - #[instrument] + #[instrument(skip(self))] pub fn initialize_dirs(&self) -> Result<()> { info!("Initializing dirs"); let dirs = vec![