Initial prototype

This prototype has basic IPC working, with starting actions and waiting for actions or just getting information
This commit is contained in:
OMGeeky
2024-01-31 15:38:52 +01:00
commit 1f8105943c
21 changed files with 1873 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

1407
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

20
Cargo.toml Normal file
View File

@@ -0,0 +1,20 @@
[workspace]
members = [
"gdriver-common",
"gdriver-backend",
"gdriver-client",
]
resolver="2"
[workspace.dependencies]
tracing="0.1"
tokio={ version = "1.35", features = ["rt-multi-thread", "tracing", "fs", "macros"] }
serde={ version = "1.0", features = ["serde_derive"] }
tarpc = { version = "0.34", features = ["full"] }
futures="0.3"
lazy_static="1.4"
chrono="0.4"
[patch.crates-io]
#tarpc = {path = "../../Documents/git/OMGeeky/tarpc/tarpc/"}
tarpc = {git = "https://github.com/google/tarpc.git"}

View File

@@ -0,0 +1,18 @@
[package]
name = "gdriver-backend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
fuser={ version = "0.14", default_features = true, features = ["serializable"] }
tracing.workspace = true
tokio.workspace = true
serde.workspace = true
tarpc.workspace = true
futures.workspace = true
chrono.workspace = true
[dependencies.gdriver-common]
path = "../gdriver-common/"

View File

@@ -0,0 +1,29 @@
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use gdriver_common::{drive_structure::drive_id::DriveId, prelude::CONFIGURATION};
use crate::prelude::*;
pub struct Drive {
tracked_files: HashMap<DriveId, DateTime<Utc>>,
}
impl Drive {
pub fn new() -> Self {
Self {
tracked_files: HashMap::new(),
}
}
pub fn get_file_tracking_state(&self, id: &DriveId) -> TrackingState {
let file = self.tracked_files.get(id);
match file {
Some(date) => TrackingState::Tracked(*date),
None => TrackingState::Untracked,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TrackingState {
Untracked,
Tracked(DateTime<Utc>),
}

View File

@@ -0,0 +1,23 @@
use futures::{future, prelude::*};
use std::net::SocketAddr;
use tarpc::{
context,
server::{self, incoming::Incoming, Channel},
tokio_serde::formats::Json,
};
mod prelude;
use crate::prelude::*;
pub(crate) use gdriver_common::prelude::*;
mod drive;
mod sample;
mod service;
pub(crate) async fn spawn(fut: impl Future<Output = ()> + Send + 'static) {
tokio::spawn(fut);
}
#[tokio::main]
async fn main() -> Result<()> {
// sample::main().await?;
service::start().await?;
Ok(())
}

View File

@@ -0,0 +1,2 @@
pub(crate) type Result<T> = StdResult<T, Box<dyn Error>>;
use std::{error::Error, result::Result as StdResult};

View File

@@ -0,0 +1,38 @@
use super::*;
use gdriver_common::ipc::sample::World;
#[derive(Clone)]
struct HelloServer(SocketAddr);
impl World for HelloServer {
async fn hello(self, _: context::Context, name: String) -> String {
println!("Got Hello request with name: {name}");
format!("Hello {}", name)
}
}
pub(super) async fn main() -> Result<()> {
println!("Hello, world!");
let config = &CONFIGURATION;
let server_addr = (config.ip, config.port);
let mut listener = tarpc::serde_transport::tcp::listen(&server_addr, Json::default).await?;
println!("Listening");
listener.config_mut().max_frame_length(usize::MAX);
listener
// Ignore accept errors.
.filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults)
// Limit channels to 1 per IP.
.max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip())
// serve is generated by the service attribute. It takes as input any type implementing
// the generated World trait.
.map(|channel| {
let server = HelloServer(channel.transport().peer_addr().unwrap());
channel.execute(server.serve()).for_each(spawn)
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {})
.await;
Ok(())
}

View File

@@ -0,0 +1,100 @@
use std::{sync::Arc, thread};
use chrono::Duration;
use gdriver_common::{
drive_structure::drive_id::ROOT_ID,
ipc::gdriver_service::{BackendActionError, BackendActionRequest, GDriverService},
};
use tokio::sync::Mutex;
use crate::drive::Drive;
use super::*;
#[derive(Clone)]
struct GdriverServer {
socket_address: SocketAddr,
drive: Arc<Mutex<Drive>>,
}
impl GDriverService for GdriverServer {
async fn do_something2(
self,
_: ::tarpc::context::Context,
req: BackendActionRequest,
) -> std::result::Result<String, BackendActionError> {
println!("You are connected from {}", self.socket_address);
match req {
BackendActionRequest::ShutdownGracefully => {
println!("Shutdown request received, but I dont want to.");
Err(BackendActionError::CouldNotComplete)
//Ok(String::from("OK. Shutting down"))
}
BackendActionRequest::UpdateChanges => {
println!("UpdateChanges request received");
let drive = &self.drive;
print_sample_tracking_state(drive).await;
Ok(String::from("OK"))
}
BackendActionRequest::Ping => {
println!("Ping request received");
Ok(String::from("Pong"))
}
BackendActionRequest::RunLong => {
println!("RunLong request received");
long_running_task(&self.drive).await;
Ok(String::from("OK"))
}
BackendActionRequest::StartLong => {
println!("StartLong request received");
tokio::spawn(async move { long_running_task(&self.drive).await });
Ok(String::from("OK"))
}
}
}
}
async fn long_running_task(drive: &Arc<Mutex<Drive>>) {
thread::sleep(Duration::seconds(10).to_std().unwrap());
print_sample_tracking_state(drive).await;
}
async fn print_sample_tracking_state(drive: &Arc<Mutex<Drive>>) {
let lock = drive.lock();
let drive = lock.await;
let state = drive.get_file_tracking_state(&ROOT_ID);
dbg!(state);
}
pub async fn start() -> Result<()> {
println!("Hello, world!");
let config = &CONFIGURATION;
println!("Config: {:?}", **config);
let drive = Drive::new();
let m = 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?;
listener.config_mut().max_frame_length(usize::MAX);
println!("Listening");
listener
// Ignore accept errors.
.filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults)
// // Limit channels to 1 per IP.
.max_channels_per_key(1, |t| t.transport().peer_addr().unwrap().ip())
// serve is generated by the service attribute. It takes as input any type implementing
// the generated World trait.
.map(|channel| {
let c = channel.transport().peer_addr().unwrap();
let server = GdriverServer {
socket_address: c,
drive: m.clone(),
};
channel.execute(server.serve()).for_each(spawn)
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {})
.await;
Ok(())
}

14
gdriver-client/Cargo.toml Normal file
View File

@@ -0,0 +1,14 @@
[package]
name = "gdriver-client"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tarpc.workspace = true
tokio.workspace = true
futures-sink = "0.3.30"
[dependencies.gdriver-common]
path = "../gdriver-common/"

View File

@@ -0,0 +1,15 @@
use std::{error::Error, net::IpAddr, result::Result as StdResult};
use gdriver_common::{ipc::sample::*, prelude::*};
use tarpc::{client, tokio_serde::formats::Json};
type Result<T> = StdResult<T, Box<dyn Error>>;
#[tokio::main]
async fn main() -> Result<()> {
service::start().await?;
Ok(())
}
mod sample;
mod service;

View File

@@ -0,0 +1,32 @@
use gdriver_common::config::CONFIGURATION;
use super::*;
pub async fn start() -> Result<()> {
println!("Hello, world!");
let name = "test1".to_string();
let config = &CONFIGURATION;
let client: WorldClient = create_client(config.ip, config.port).await?;
let hello = client
.hello(tarpc::context::current(), name.to_string())
.await;
match hello {
Ok(hello) => println!("{hello:?}"),
Err(e) => println!("{:?}", (e)),
}
Ok(())
}
pub async fn create_client(ip: IpAddr, port: u16) -> Result<WorldClient> {
let server_addr = (ip, port);
let transport = tarpc::serde_transport::tcp::connect(&server_addr, Json::default)
.await
.map_err(|e| {
println!("Could not connect");
e
})?;
let var_name = WorldClient::new(client::Config::default(), transport);
let client = var_name.spawn();
Ok(client)
}

View File

@@ -0,0 +1,61 @@
use std::time;
use gdriver_common::ipc::gdriver_service::{BackendActionRequest, GDriverServiceClient};
use super::*;
pub async fn start() -> Result<()> {
println!("Hello, world!");
let config = &CONFIGURATION;
println!("Config: {:?}", **config);
let client: GDriverServiceClient = create_client(config.ip, config.port).await?;
let hello = client
.do_something2(tarpc::context::current(), BackendActionRequest::Ping)
.await;
match hello {
Ok(hello) => println!("Yay: {:?}", hello),
Err(e) => {
println!(":( {:?}", (e));
dbg!(e);
}
}
let start = time::SystemTime::now();
let hello = client
.do_something2(tarpc::context::current(), BackendActionRequest::RunLong)
.await;
let seconds = (time::SystemTime::now().duration_since(start))
.unwrap()
.as_secs();
match hello {
Ok(hello) => println!("Run Long returned after {} seconds: {:?}", seconds, hello),
Err(e) => println!(":( {:?}", (e)),
}
let start = time::SystemTime::now();
let hello = client
.do_something2(tarpc::context::current(), BackendActionRequest::StartLong)
.await;
let seconds = (time::SystemTime::now().duration_since(start))
.unwrap()
.as_secs();
match hello {
Ok(hello) => println!("Start Long returned after {} seconds: {:?}", seconds, hello),
Err(e) => println!(":( {:?}", (e)),
}
Ok(())
}
pub async fn create_client(ip: IpAddr, port: u16) -> Result<GDriverServiceClient> {
let server_addr = (ip, port);
let transport = tarpc::serde_transport::tcp::connect(&server_addr, Json::default)
.await
.map_err(|e| {
println!("Could not connect");
e
})?;
let service = GDriverServiceClient::new(client::Config::default(), transport);
let client = service.spawn();
Ok(client)
}

17
gdriver-common/Cargo.toml Normal file
View File

@@ -0,0 +1,17 @@
[package]
name = "gdriver-common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde.workspace = true
tarpc.workspace = true
tokio.workspace = true
futures.workspace = true
lazy_static.workspace = true
confique={ version = "0.2" }
#[patch.crates-io]
#confique = {path="~/Documents/git/OMGeeky/confique "}

View File

@@ -0,0 +1,33 @@
use super::*;
use crate::prelude::*;
use confique::{Config, Partial};
use std::net::{IpAddr, Ipv6Addr};
const IP_DEFAULT: IpAddr = IpAddr::V6(Ipv6Addr::LOCALHOST);
#[derive(Debug, Serialize, Deserialize, Config, Clone)]
pub struct Configuration {
#[config(default = 33333)]
pub port: u16,
// #[config(default = Test)]
pub ip: std::net::IpAddr,
}
pub fn load_config() -> Result<Configuration> {
Ok(add_default_locations(Config::builder()).load()?)
}
pub fn load_config_with_path(path: &Path) -> Result<Configuration> {
Ok(add_default_locations(Config::builder().file(path)).load()?)
}
fn add_default_locations(
builder: confique::Builder<Configuration>,
) -> confique::Builder<Configuration> {
type P = <Configuration as Config>::Partial;
let prebuilt = P {
ip: Some(IP_DEFAULT),
..P::empty()
};
builder.env().file("config.toml").preloaded(prebuilt)
}
use lazy_static::lazy_static;
lazy_static! {
pub static ref CONFIGURATION: Configuration = load_config().unwrap();
}

View File

@@ -0,0 +1,23 @@
pub mod drive_id {
use lazy_static::lazy_static;
lazy_static! {
pub static ref ROOT_ID: DriveId = DriveId(String::from("root"));
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DriveId(pub String);
impl<T> From<T> for DriveId
where
T: Into<String>,
{
fn from(s: T) -> Self {
DriveId(s.into())
}
}
impl AsRef<str> for DriveId {
fn as_ref(&self) -> &str {
&self.0
}
}
}

View File

@@ -0,0 +1,3 @@
use serde::{Deserialize, Serialize};
pub mod gdriver_service;
pub mod sample;

View File

@@ -0,0 +1,21 @@
use serde::{Deserialize, Serialize};
#[tarpc::service]
pub trait GDriverService {
async fn do_something2(
req: BackendActionRequest,
) -> std::result::Result<String, BackendActionError>;
}
#[derive(Debug, Serialize, Deserialize)]
pub enum BackendActionRequest {
ShutdownGracefully,
UpdateChanges,
Ping,
RunLong,
StartLong,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum BackendActionError {
Unknown,
CouldNotComplete,
}

View File

@@ -0,0 +1,5 @@
#[tarpc::service]
pub trait World {
/// Returns a greeting for name.
async fn hello(name: String) -> String;
}

View File

@@ -0,0 +1,7 @@
use serde::{Deserialize, Serialize};
use std::path::Path;
pub mod prelude;
pub mod config;
pub mod ipc;
pub mod drive_structure;

View File

@@ -0,0 +1,4 @@
pub use crate::config::Configuration;
pub use crate::config::CONFIGURATION;
pub use crate::ipc;
pub(crate) type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;