feat: abstract commander
All checks were successful
continuous-integration/drone/push Build is passing

Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
Kasper Juul Hermansen 2024-05-12 22:24:37 +02:00
parent 64d59e069f
commit 76f1c87663
Signed by: kjuulh
GPG Key ID: 57B6E1465221F912
9 changed files with 244 additions and 118 deletions

View File

@ -2,16 +2,6 @@ syntax = "proto3";
package hyperlog; package hyperlog;
service Graph {
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
rpc Get(GetRequest) returns (GetReply);
}
message GetAvailableRootsRequest {}
message GetAvailableRootsResponse {
repeated string roots = 1;
}
message UserGraphItem { message UserGraphItem {
map<string, GraphItem> items = 1; map<string, GraphItem> items = 1;
@ -41,6 +31,17 @@ message GraphItem {
} }
} }
service Graph {
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
rpc Get(GetRequest) returns (GetReply);
rpc CreateSection(CreateSectionRequest) returns (CreateSectionResponse);
}
message GetAvailableRootsRequest {}
message GetAvailableRootsResponse {
repeated string roots = 1;
}
message GetRequest { message GetRequest {
string root = 1; string root = 1;
repeated string paths = 2; repeated string paths = 2;
@ -50,3 +51,7 @@ message GetReply {
GraphItem item = 1; GraphItem item = 1;
} }
message CreateSectionRequest {}
message CreateSectionResponse {}

View File

@ -64,6 +64,16 @@ impl Graph for Server {
roots: vec!["kjuulh".into()], roots: vec!["kjuulh".into()],
})) }))
} }
async fn create_section(
&self,
request: tonic::Request<CreateSectionRequest>,
) -> std::result::Result<tonic::Response<CreateSectionResponse>, tonic::Status> {
let req = request.into_inner();
tracing::trace!("create section: req({:?})", req);
Ok(Response::new(CreateSectionResponse {}))
}
} }
pub trait ServerExt { pub trait ServerExt {

View File

@ -1,8 +1,12 @@
use hyperlog_core::log::ItemState; use hyperlog_core::log::ItemState;
use serde::Serialize; use serde::Serialize;
use tonic::transport::Channel;
use crate::{events::Events, shared_engine::SharedEngine, storage::Storage}; use crate::{events::Events, shared_engine::SharedEngine, storage::Storage};
mod local;
mod remote;
#[derive(Serialize, PartialEq, Eq, Debug, Clone)] #[derive(Serialize, PartialEq, Eq, Debug, Clone)]
pub enum Command { pub enum Command {
CreateRoot { CreateRoot {
@ -40,6 +44,7 @@ pub enum Command {
#[derive(Clone)] #[derive(Clone)]
enum CommanderVariant { enum CommanderVariant {
Local(local::Commander), Local(local::Commander),
Remote(remote::Commander),
} }
#[derive(Clone)] #[derive(Clone)]
@ -54,97 +59,16 @@ impl Commander {
}) })
} }
pub fn remote(channel: Channel) -> anyhow::Result<Self> {
Ok(Self {
variant: CommanderVariant::Remote(remote::Commander::new(channel)?),
})
}
pub async fn execute(&self, cmd: Command) -> anyhow::Result<()> { pub async fn execute(&self, cmd: Command) -> anyhow::Result<()> {
match &self.variant { match &self.variant {
CommanderVariant::Local(commander) => commander.execute(cmd), CommanderVariant::Local(commander) => commander.execute(cmd),
} CommanderVariant::Remote(commander) => commander.execute(cmd).await,
}
}
mod local {
use std::collections::BTreeMap;
use hyperlog_core::log::GraphItem;
use crate::{events::Events, shared_engine::SharedEngine, storage::Storage};
use super::Command;
#[derive(Clone)]
pub struct Commander {
engine: SharedEngine,
storage: Storage,
events: Events,
}
impl Commander {
pub fn new(engine: SharedEngine, storage: Storage, events: Events) -> anyhow::Result<Self> {
Ok(Self {
engine,
storage,
events,
})
}
pub fn execute(&self, cmd: Command) -> anyhow::Result<()> {
tracing::debug!("executing event: {}", serde_json::to_string(&cmd)?);
match cmd.clone() {
Command::CreateRoot { root } => {
self.engine.create_root(&root)?;
}
Command::CreateSection { root, path } => {
self.engine.create(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Section(BTreeMap::default()),
)?;
}
Command::CreateItem {
root,
path,
title,
description,
state,
} => self.engine.create(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Item {
title,
description,
state,
},
)?,
Command::Move { root, src, dest } => self.engine.section_move(
&root,
&src.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
&dest.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
)?,
Command::ToggleItem { root, path } => self
.engine
.toggle_item(&root, &path.iter().map(|p| p.as_str()).collect::<Vec<_>>())?,
Command::UpdateItem {
root,
path,
title,
description,
state,
} => self.engine.update_item(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Item {
title,
description,
state,
},
)?,
}
self.storage.store(&self.engine)?;
self.events.enque_command(cmd)?;
Ok(())
} }
} }
} }

View File

@ -0,0 +1,85 @@
use std::collections::BTreeMap;
use hyperlog_core::log::GraphItem;
use crate::{events::Events, shared_engine::SharedEngine, storage::Storage};
use super::Command;
#[derive(Clone)]
pub struct Commander {
engine: SharedEngine,
storage: Storage,
events: Events,
}
impl Commander {
pub fn new(engine: SharedEngine, storage: Storage, events: Events) -> anyhow::Result<Self> {
Ok(Self {
engine,
storage,
events,
})
}
pub fn execute(&self, cmd: Command) -> anyhow::Result<()> {
tracing::debug!("executing event: {}", serde_json::to_string(&cmd)?);
match cmd.clone() {
Command::CreateRoot { root } => {
self.engine.create_root(&root)?;
}
Command::CreateSection { root, path } => {
self.engine.create(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Section(BTreeMap::default()),
)?;
}
Command::CreateItem {
root,
path,
title,
description,
state,
} => self.engine.create(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Item {
title,
description,
state,
},
)?,
Command::Move { root, src, dest } => self.engine.section_move(
&root,
&src.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
&dest.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
)?,
Command::ToggleItem { root, path } => self
.engine
.toggle_item(&root, &path.iter().map(|p| p.as_str()).collect::<Vec<_>>())?,
Command::UpdateItem {
root,
path,
title,
description,
state,
} => self.engine.update_item(
&root,
&path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
GraphItem::Item {
title,
description,
state,
},
)?,
}
self.storage.store(&self.engine)?;
self.events.enque_command(cmd)?;
Ok(())
}
}

View File

@ -0,0 +1,98 @@
use hyperlog_protos::hyperlog::{graph_client::GraphClient, *};
use tonic::transport::Channel;
use super::Command;
#[allow(dead_code, unused_variables)]
#[derive(Clone)]
pub struct Commander {
channel: Channel,
}
#[allow(dead_code, unused_variables)]
impl Commander {
pub fn new(channel: Channel) -> anyhow::Result<Self> {
Ok(Self { channel })
}
pub async fn execute(&self, cmd: Command) -> anyhow::Result<()> {
tracing::debug!("executing event: {}", serde_json::to_string(&cmd)?);
match cmd.clone() {
Command::CreateRoot { root } => {
todo!()
//self.engine.create_root(&root)?;
}
Command::CreateSection { root, path } => {
let channel = self.channel.clone();
let mut client = GraphClient::new(channel);
let request = tonic::Request::new(CreateSectionRequest {});
let response = client.create_section(request).await?;
let res = response.into_inner();
// self.engine.create(
// &root,
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
// GraphItem::Section(BTreeMap::default()),
// )?;
}
Command::CreateItem {
root,
path,
title,
description,
state,
} => {
todo!()
// self.engine.create(
// &root,
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
// GraphItem::Item {
// title,
// description,
// state,
// },
// )?
}
Command::Move { root, src, dest } => {
todo!()
// self.engine.section_move(
// &root,
// &src.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
// &dest.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
// )?
}
Command::ToggleItem { root, path } => {
todo!()
// self
// .engine
// .toggle_item(&root, &path.iter().map(|p| p.as_str()).collect::<Vec<_>>())?
}
Command::UpdateItem {
root,
path,
title,
description,
state,
} => {
todo!()
// self.engine.update_item(
// &root,
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
// GraphItem::Item {
// title,
// description,
// state,
// },
// )?
}
}
// self.storage.store(&self.engine)?;
// self.events.enque_command(cmd)?;
Ok(())
}
}

View File

@ -3,7 +3,7 @@ use itertools::Itertools;
use crate::{ use crate::{
commander::{self, Commander}, commander::{self, Commander},
models::IOEvent, models::{IOEvent, Msg},
state::SharedState, state::SharedState,
}; };
@ -32,7 +32,7 @@ impl CreateItemCommand {
super::Command::new(|dispatch| { super::Command::new(|dispatch| {
tokio::spawn(async move { tokio::spawn(async move {
dispatch.send(crate::models::Msg::ItemCreated(IOEvent::Initialized)); dispatch.send(Msg::ItemCreated(IOEvent::Initialized));
match self match self
.commander .commander
@ -50,12 +50,10 @@ impl CreateItemCommand {
{ {
tokio::time::sleep(std::time::Duration::from_secs(1)).await; tokio::time::sleep(std::time::Duration::from_secs(1)).await;
} }
dispatch.send(crate::models::Msg::ItemCreated(IOEvent::Success(()))); dispatch.send(Msg::ItemCreated(IOEvent::Success(())));
} }
Err(e) => { Err(e) => {
dispatch.send(crate::models::Msg::ItemCreated(IOEvent::Failure( dispatch.send(Msg::ItemCreated(IOEvent::Failure(e.to_string())));
e.to_string(),
)));
} }
} }
}); });

View File

@ -1,3 +1,5 @@
use tonic::transport::Channel;
use crate::{ use crate::{
commander::Commander, events::Events, querier::Querier, shared_engine::SharedEngine, commander::Commander, events::Events, querier::Querier, shared_engine::SharedEngine,
storage::Storage, storage::Storage,
@ -25,14 +27,21 @@ impl State {
let events = Events::default(); let events = Events::default();
let engine = SharedEngine::from(engine); let engine = SharedEngine::from(engine);
let querier = match backend { let (querier, commander) = match backend {
Backend::Local => Querier::local(&engine), Backend::Local => (
Backend::Remote => Querier::remote().await?, Querier::local(&engine),
}; Commander::local(engine.clone(), storage.clone(), events.clone())?,
),
Backend::Remote => {
let channel = Channel::from_static("http://localhost:4000")
.connect()
.await?;
let commander = match backend { (
Backend::Local => Commander::local(engine.clone(), storage.clone(), events.clone())?, Querier::remote(channel.clone()).await?,
Backend::Remote => todo!(), Commander::remote(channel)?,
)
}
}; };
Ok(Self { Ok(Self {

View File

@ -1,4 +1,5 @@
use hyperlog_core::log::GraphItem; use hyperlog_core::log::GraphItem;
use tonic::transport::Channel;
use crate::shared_engine::SharedEngine; use crate::shared_engine::SharedEngine;
@ -23,9 +24,9 @@ impl Querier {
} }
} }
pub async fn remote() -> anyhow::Result<Self> { pub async fn remote(channel: Channel) -> anyhow::Result<Self> {
Ok(Self { Ok(Self {
variant: QuerierVariant::Remote(remote::Querier::new().await?), variant: QuerierVariant::Remote(remote::Querier::new(channel).await?),
}) })
} }

View File

@ -15,11 +15,7 @@ pub struct Querier {
#[allow(dead_code, unused_variables)] #[allow(dead_code, unused_variables)]
impl Querier { impl Querier {
pub async fn new() -> anyhow::Result<Self> { pub async fn new(channel: Channel) -> anyhow::Result<Self> {
let channel = Channel::from_static("http://localhost:4000")
.connect()
.await?;
Ok(Self { channel }) Ok(Self { channel })
} }