start implementing getting meta for structure

This commit is contained in:
OMGeeky
2024-04-14 14:52:47 +02:00
parent 2a66a6572a
commit 2f72a8a7c7
8 changed files with 207 additions and 42 deletions

View File

@@ -10,6 +10,7 @@ pub struct Drive {
tracked_files: HashMap<DriveId, DateTime<Utc>>,
pub path_resolver: PathResolver,
google_drive: GoogleDrive,
pub offline_mode: bool,
}
impl Drive {
#[instrument()]
@@ -18,8 +19,12 @@ impl Drive {
tracked_files: HashMap::new(),
path_resolver: PathResolver::new(),
google_drive: GoogleDrive::new().await?,
offline_mode: false,
})
}
pub fn set_offline_mode(&mut self, offline_mode: bool) {
self.offline_mode = offline_mode;
}
#[instrument(skip(self))]
pub fn get_file_tracking_state(&self, id: &DriveId) -> TrackingState {
let file = self.tracked_files.get(id);
@@ -28,20 +33,46 @@ impl Drive {
None => TrackingState::Untracked,
}
}
#[instrument(skip(self))]
pub async fn get_all_file_metas(&mut self) -> Result<()> {
if self.offline_mode {
info!("Offline mode, skipping get_all_file_metas");
//TODO: load from local storage
return Ok(());
}
let has_existing_token = self.google_drive.has_local_change_token().await;
if !has_existing_token {
//only get start token & data if we shouldn't have it
self.google_drive.get_change_start_token().await?;
let x = self.google_drive.get_all_file_metas().await?;
dbg!(&x);
} else {
//TODO: get file metas from local storage
}
Ok(())
}
#[instrument(skip(self))]
pub async fn update(&mut self) -> Result<()> {
if self.offline_mode {
info!("Offline mode, skipping update");
return Ok(());
}
let changes = self.google_drive.get_changes().await?;
if changes.is_empty() {
info!("No changes");
return Ok(());
}
for change in changes {
dbg!(change);
dbg!(&change);
}
Err("Not implemented".into())
// Ok(()) //TODO: implement this
}
#[instrument(skip(self))]
pub async fn ping(&self) -> Result<()> {
self.google_drive.ping().await

View File

@@ -1,19 +1,55 @@
use crate::prelude::*;
use chrono::{DateTime, Utc};
use const_format::formatcp;
use gdriver_common::{ipc::gdriver_service::SETTINGS, prelude::*};
use google_drive3::api::File;
use google_drive3::{
api::{Change, Scope},
hyper::{client::HttpConnector, Client},
hyper_rustls::{self, HttpsConnector},
oauth2, DriveHub,
};
use serde::{Deserialize, Serialize};
use std::any::type_name;
use std::fmt::{Debug, Display, Formatter};
use tokio::fs;
const FIELDS_FILE: &'static str = "id, name, size, mimeType, kind, md5Checksum, parents, trashed, createdTime, modifiedTime, viewedByMeTime";
#[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Serialize, Deserialize, Clone, Hash)]
pub struct FileData {
pub id: String,
pub name: String,
pub size: Option<i64>,
pub mime_type: String,
pub kind: String,
pub md5_checksum: Option<String>,
pub parents: Option<Vec<String>>,
pub trashed: Option<bool>,
pub created_time: Option<DateTime<Utc>>,
pub modified_time: Option<DateTime<Utc>>,
pub viewed_by_me_time: Option<DateTime<Utc>>,
}
impl FileData {
fn convert_from_api_file(file: File) -> Self {
Self {
id: file.id.unwrap_or_default(),
name: file.name.unwrap_or_default(),
size: file.size,
mime_type: file.mime_type.unwrap_or_default(),
kind: file.kind.unwrap_or_default(),
md5_checksum: file.md5_checksum,
parents: file.parents,
trashed: file.trashed,
created_time: file.created_time,
modified_time: file.modified_time,
viewed_by_me_time: file.viewed_by_me_time,
}
}
}
const FIELDS_CHANGE: &str = formatcp!(
"nextPageToken, newStartPageToken, changes(removed, fileId, changeType, file({FIELDS_FILE}))"
"nextPageToken, newStartPageToken, changes(removed, fileId, changeType, file({}))",
FIELDS_FILE
);
#[derive(Clone)]
pub struct GoogleDrive {
@@ -21,6 +57,43 @@ pub struct GoogleDrive {
changes_start_page_token: Option<String>,
}
impl GoogleDrive {
#[instrument]
pub(crate) async fn get_all_file_metas(&self) -> Result<Vec<FileData>> {
let mut page_token: Option<String> = None;
let mut files = Vec::new();
loop {
let (response, body) = self
.hub
.files()
.list()
.supports_all_drives(false)
.spaces("drive")
.page_token(&page_token.unwrap_or_default())
.param("fields", &format!("nextPageToken, files({})", FIELDS_FILE))
.doit()
.await?;
page_token = body.next_page_token;
if response.status().is_success() {
files.extend(
body.files
.unwrap_or_default()
.into_iter()
.map(FileData::convert_from_api_file),
);
} else {
error!("Could not get files: {:?}", response);
return Err("Could not get files".into());
}
if page_token.is_none() {
info!("No more pages");
break;
}
}
Ok(files)
}
}
impl GoogleDrive {
#[instrument]
pub(crate) async fn new() -> Result<Self> {
@@ -82,7 +155,7 @@ impl GoogleDrive {
#[instrument]
pub async fn get_changes(&mut self) -> Result<Vec<Change>> {
info!("Getting changes");
let mut page_token = Some(self.change_start_token().await?);
let mut page_token = Some(self.get_change_start_token().await?);
let mut changes = Vec::new();
while let Some(current_page_token) = page_token {
info!("Getting changes with page token: {}", current_page_token);
@@ -100,7 +173,7 @@ impl GoogleDrive {
.doit()
.await?;
if let Some(token) = body.new_start_page_token {
self.changes_start_page_token = Some(token);
self.set_change_start_token(token).await?;
}
if response.status().is_success() {
changes.extend(body.changes.unwrap_or_default());
@@ -113,19 +186,21 @@ impl GoogleDrive {
trace!("Got {} changes", changes.len());
Ok(changes)
}
async fn set_change_start_token(&mut self, token: String) -> Result<()> {
info!("Setting start page token: {}", token);
fs::write(SETTINGS.get_changes_file_path(), token.clone()).await?;
self.changes_start_page_token = Some(token);
Ok(())
}
async fn change_start_token(&mut self) -> Result<String> {
pub async fn get_change_start_token(&mut self) -> Result<String> {
Ok(match &self.changes_start_page_token {
None => {
info!("Getting start page token");
let token = fs::read_to_string(SETTINGS.get_changes_file_path())
.await
.unwrap_or_default();
self.changes_start_page_token = if !token.is_empty() {
Some(token)
} else {
Some(self.get_changes_start_token_from_api().await?)
};
let has_local_token = self.has_local_change_token().await;
if !has_local_token {
self.update_change_start_token_from_api().await?;
}
info!("Got start page token: {:?}", self.changes_start_page_token);
self.changes_start_page_token
.clone()
@@ -137,7 +212,22 @@ impl GoogleDrive {
}
})
}
async fn get_changes_start_token_from_api(&self) -> Result<String> {
pub(crate) async fn has_local_change_token(&mut self) -> bool {
!self
.get_local_change_start_token()
.await
.unwrap_or_default()
.is_empty()
}
pub async fn get_local_change_start_token(&mut self) -> Option<String> {
self.changes_start_page_token = fs::read_to_string(SETTINGS.get_changes_file_path())
.await
.ok();
self.changes_start_page_token.clone()
}
async fn update_change_start_token_from_api(&mut self) -> Result<()> {
info!("Getting start page token from API");
let (response, body) = self
.hub
@@ -147,8 +237,9 @@ impl GoogleDrive {
.doit()
.await?;
if response.status().is_success() {
let start_page_token = body.start_page_token.unwrap_or_default();
Ok(start_page_token)
let token = body.start_page_token.clone().unwrap_or_default();
self.set_change_start_token(token).await?;
Ok(())
} else {
Err("Could not get start page token".into())
}

View File

@@ -1,14 +1,16 @@
use crate::drive::Drive;
use crate::prelude::*;
use gdriver_common::ipc::gdriver_service::ReadDirResult;
use gdriver_common::path_resolve_error::PathResolveError;
use gdriver_common::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::Path;
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct PathResolver {
/// A map of children to their parents
parents: HashMap<DriveId, Vec<DriveId>>,
/// A map of parents to their children with id, name and type (folder/file/symlink)
children: HashMap<DriveId, Vec<ReadDirResult>>,
}
@@ -19,20 +21,22 @@ impl PathResolver {
children: HashMap::new(),
}
}
pub async fn get_id_from_path(&mut self, path: &Path, drive: &Drive) -> Result<DriveId> {
pub async fn get_id_from_path(
&mut self,
path: &Path,
) -> StdResult<Option<DriveId>, PathResolveError> {
let segments: Vec<_> = path
.to_str()
.ok_or(PathResolveError::InvalidPath)?
.split('/')
.collect();
let mut current = ROOT_ID.clone();
self.update_from_drive(drive).await?;
for segment in segments {
current = self
.get_id_from_parent_and_name(segment, &current)
.ok_or("path-segment not found")?;
current = self.get_id_from_parent_and_name(segment, &current).ok_or(
PathResolveError::Other("path-segment not found".to_string()),
)?;
}
return Ok(current);
return Ok(Some(current));
}
pub fn get_id_from_parent_and_name(&self, name: &str, parent: &DriveId) -> Option<DriveId> {
if let Some(children) = self.children.get(parent) {
@@ -46,16 +50,28 @@ impl PathResolver {
async fn update_from_drive(&mut self, drive: &Drive) -> Result<()> {
todo!()
}
/// Add a relationship between a parent and a child
pub(crate) fn add_relationship(&mut self, parent: DriveId, entry: ReadDirResult) {
todo!()
match self.parents.get_mut(&entry.id) {
Some(x) => x.push(parent.clone()),
None => {
self.parents.insert(entry.id.clone(), vec![parent.clone()]);
}
};
match self.children.get_mut(&parent) {
Some(x) => x.push(entry.clone()),
None => {
self.children.insert(parent.clone(), vec![entry.clone()]);
}
}
}
/// Remove the relationship between a parent and a child
pub(crate) fn remove_relationship(&mut self, parent: DriveId, entry: ReadDirResult) {
todo!()
self.parents
.get_mut(&entry.id)
.map(|x| x.retain(|e| e != &parent));
self.children
.get_mut(&parent)
.map(|x| x.retain(|e| e.id != entry.id));
}
}
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum PathResolveError {
#[error("The path provided was invalid")]
InvalidPath,
}

View File

@@ -16,9 +16,14 @@ struct GdriverServer {
drive: Arc<Mutex<Drive>>,
}
impl GDriverService for GdriverServer {
// async fn get_settings(self, context: Context) -> StdResult<GDriverSettings, GetSettingsError> {
// todo!()
// }
async fn set_offline_mode(
self,
_context: Context,
offline_mode: bool,
) -> StdResult<(), GDriverServiceError> {
self.drive.lock().await.set_offline_mode(offline_mode);
Ok(())
}
async fn get_file_by_name(
self,
@@ -42,7 +47,12 @@ impl GDriverService for GdriverServer {
context: Context,
path: PathBuf,
) -> StdResult<DriveId, GetFileByPathError> {
Err(GetFileByPathError::Other)
let mut drive_lock = self.drive.lock().await;
let x = drive_lock.path_resolver.get_id_from_path(&path).await?;
match x {
None => Err(GetFileByPathError::NotFound),
Some(id) => Ok(id),
}
}
async fn write_local_change(
@@ -191,7 +201,7 @@ pub async fn start() -> Result<()> {
let config = &CONFIGURATION;
info!("Config: {:?}", **config);
let drive = Drive::new().await?;
let mut drive = Drive::new().await?;
match drive.ping().await {
Ok(_) => {
info!("Can reach google drive api.");
@@ -201,7 +211,8 @@ pub async fn start() -> Result<()> {
return Err(e);
}
}
let m = Arc::new(Mutex::new(drive));
drive.get_all_file_metas().await?;
let drive = Arc::new(Mutex::new(drive));
let server_addr = (config.ip, config.port);
let mut listener = tarpc::serde_transport::tcp::listen(&server_addr, Json::default).await?;
@@ -220,7 +231,7 @@ pub async fn start() -> Result<()> {
let c = channel.transport().peer_addr().unwrap();
let server = GdriverServer {
socket_address: c,
drive: m.clone(),
drive: drive.clone(),
};
channel.execute(server.serve()).for_each(spawn)
})

View File

@@ -4,6 +4,7 @@ use tokio::sync::mpsc::{channel, Sender};
use crate::filesystem::{Filesystem, ShutdownRequest};
use gdriver_common::{ipc::sample::*, prelude::*};
use tarpc::context::Context;
use tarpc::{client, tokio_serde::formats::Json};
use tokio::task::JoinHandle;
@@ -15,10 +16,11 @@ async fn main() -> Result<()> {
// service::start().await?;
let mount_options = &[MountOption::RW];
let (tx, rx) = channel(1);
let f = Filesystem::new(
service::create_client(CONFIGURATION.ip, CONFIGURATION.port).await?,
rx,
);
let gdriver_client = service::create_client(CONFIGURATION.ip, CONFIGURATION.port).await?;
gdriver_client
.set_offline_mode(Context::current(), true) //TODO make this configurable
.await??;
let f = Filesystem::new(gdriver_client, rx);
mount(f, &"/var/tmp/gdriver2_mount", mount_options, tx)
.await?
.await?;

View File

@@ -10,6 +10,7 @@ use std::path::PathBuf;
#[tarpc::service]
pub trait GDriverService {
async fn set_offline_mode(offline_mode: bool) -> StdResult<(), GDriverServiceError>;
async fn get_file_by_name(
name: OsString,
parent: DriveId,
@@ -52,6 +53,7 @@ lazy_static! {
pub mod errors {
use super::*;
use crate::path_resolve_error::PathResolveError;
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum GDriverServiceError {
#[error("Error getting the settings: {0}")]
@@ -102,6 +104,8 @@ pub mod errors {
NotFound,
#[error("Could not update drive info")]
Update(String),
#[error("Could not resolve path")]
PathResolve(#[from] PathResolveError),
}
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]

View File

@@ -5,5 +5,6 @@ pub mod prelude;
pub mod config;
pub mod drive_structure;
pub mod ipc;
pub mod path_resolve_error;
pub mod project_dirs;
pub mod tracing_setup;

View File

@@ -0,0 +1,9 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, thiserror::Error)]
pub enum PathResolveError {
#[error("The path provided was invalid")]
InvalidPath,
#[error("Some other error occurred")]
Other(String),
}