Compare commits
31 Commits
v0.1.0
...
61090ba5c2
Author | SHA1 | Date | |
---|---|---|---|
|
61090ba5c2 | ||
a70f1cd4b4 | |||
0927d36505 | |||
bc2b47a6c5 | |||
44d9ed2790 | |||
118aeb3898 | |||
65c2466f97
|
|||
832587b51d
|
|||
4ad8120cb5
|
|||
1f0f526e38
|
|||
91ad8eda01
|
|||
7496d0e964
|
|||
208b14583e
|
|||
4a91a564bf
|
|||
364f3992bb | |||
837caee5db
|
|||
816869e6f9
|
|||
7bdf8393b1
|
|||
699bac7159
|
|||
76f1c87663
|
|||
64d59e069f
|
|||
9bb5bc9e87
|
|||
2d63d3ad4c
|
|||
9cb3296cec
|
|||
874045dca8
|
|||
5548d8e36e
|
|||
cf26422673
|
|||
4a0fcd1bbb
|
|||
86cba91b16
|
|||
113c646334
|
|||
6a147ba0d2
|
38
CHANGELOG.md
38
CHANGELOG.md
@@ -6,6 +6,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0] - 2024-05-25
|
||||
|
||||
### Added
|
||||
- enable creating items on the same level
|
||||
- add command for quickly creating an item
|
||||
- remove removal of spaces in title
|
||||
- with toggle item
|
||||
- with backend
|
||||
- can get actual available roots
|
||||
- can add items
|
||||
- server can actually create root and sections
|
||||
- abstract commander
|
||||
- with async commands instead of inline mutations phew.
|
||||
- add command pattern
|
||||
- allow async function in command
|
||||
- move core to tui and begin grpc work
|
||||
- add protos
|
||||
- update deps
|
||||
- with basic server
|
||||
|
||||
### Fixed
|
||||
- *(deps)* update rust crate serde to v1.0.203
|
||||
- *(deps)* update rust crate prost to 0.12.6
|
||||
- *(deps)* update rust crate prost to 0.12.5
|
||||
- *(deps)* update rust crate serde to 1.0.202
|
||||
|
||||
### Other
|
||||
- *(deps)* update all dependencies
|
||||
- *(deps)* update rust crate itertools to 0.13.0
|
||||
- move unused imports into cfg
|
||||
- remove unused functions and fix warnings
|
||||
- remove unused variables
|
||||
- fix formatting
|
||||
- refactor out graph created event
|
||||
- let state use either local or backend
|
||||
- remove warnings
|
||||
- remove extra logs
|
||||
|
||||
## [0.1.0] - 2024-05-11
|
||||
|
||||
### Added
|
||||
|
1177
Cargo.lock
generated
1177
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
10
Cargo.toml
10
Cargo.toml
@@ -5,6 +5,8 @@ resolver = "2"
|
||||
[workspace.dependencies]
|
||||
hyperlog-core = { path = "crates/hyperlog-core" }
|
||||
hyperlog-tui = { path = "crates/hyperlog-tui" }
|
||||
hyperlog-server = { path = "crates/hyperlog-server" }
|
||||
hyperlog-protos = { path = "crates/hyperlog-protos" }
|
||||
|
||||
anyhow = { version = "1" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
@@ -13,8 +15,12 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
dotenv = { version = "0.15" }
|
||||
axum = { version = "0.7" }
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
serde_json = "1.0.117"
|
||||
itertools = "0.12.1"
|
||||
itertools = "0.13.0"
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
tonic = "0.11.0"
|
||||
futures = { version = "0.3.30" }
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
|
@@ -12,19 +12,9 @@ clap.workspace = true
|
||||
dotenv.workspace = true
|
||||
axum.workspace = true
|
||||
|
||||
serde = { version = "1.0.201", features = ["derive"] }
|
||||
sqlx = { version = "0.7.4", features = [
|
||||
"runtime-tokio",
|
||||
"tls-rustls",
|
||||
"postgres",
|
||||
"uuid",
|
||||
"time",
|
||||
] }
|
||||
serde = { version = "1.0.202", features = ["derive"] }
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
serde_json = "1.0.117"
|
||||
bus = "2.4.1"
|
||||
dirs = "5.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
similar-asserts = "1.5.0"
|
||||
|
@@ -1,11 +1 @@
|
||||
#![feature(map_try_insert)]
|
||||
|
||||
pub mod commander;
|
||||
pub mod querier;
|
||||
|
||||
pub mod engine;
|
||||
pub mod events;
|
||||
pub mod log;
|
||||
pub mod shared_engine;
|
||||
pub mod state;
|
||||
pub mod storage;
|
||||
|
@@ -1,32 +0,0 @@
|
||||
use crate::{
|
||||
commander::Commander, events::Events, querier::Querier, shared_engine::SharedEngine,
|
||||
storage::Storage,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct State {
|
||||
engine: SharedEngine,
|
||||
pub storage: Storage,
|
||||
events: Events,
|
||||
|
||||
pub commander: Commander,
|
||||
pub querier: Querier,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> anyhow::Result<Self> {
|
||||
let storage = Storage::new();
|
||||
let engine = storage.load()?;
|
||||
let events = Events::default();
|
||||
let engine = SharedEngine::from(engine);
|
||||
|
||||
Ok(Self {
|
||||
engine: engine.clone(),
|
||||
storage: storage.clone(),
|
||||
events: events.clone(),
|
||||
|
||||
commander: Commander::new(engine.clone(), storage, events)?,
|
||||
querier: Querier::new(engine),
|
||||
})
|
||||
}
|
||||
}
|
12
crates/hyperlog-protos/Cargo.toml
Normal file
12
crates/hyperlog-protos/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "hyperlog-protos"
|
||||
edition = "2021"
|
||||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
tonic.workspace = true
|
||||
|
||||
prost = "0.12.6"
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = "0.11.0"
|
3
crates/hyperlog-protos/build.rs
Normal file
3
crates/hyperlog-protos/build.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
tonic_build::compile_protos("proto/hyperlog.proto").unwrap();
|
||||
}
|
92
crates/hyperlog-protos/proto/hyperlog.proto
Normal file
92
crates/hyperlog-protos/proto/hyperlog.proto
Normal file
@@ -0,0 +1,92 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package hyperlog;
|
||||
|
||||
message UserGraphItem {
|
||||
map<string, GraphItem> items = 1;
|
||||
|
||||
}
|
||||
message SectionGraphItem {
|
||||
map<string, GraphItem> items = 1;
|
||||
}
|
||||
|
||||
message ItemStateNotDone {}
|
||||
message ItemStateDone {}
|
||||
|
||||
message ItemGraphItem {
|
||||
string title = 1;
|
||||
string description = 2;
|
||||
oneof item_state {
|
||||
ItemStateNotDone not_done = 3;
|
||||
ItemStateDone done = 4;
|
||||
}
|
||||
}
|
||||
|
||||
message GraphItem {
|
||||
oneof contents {
|
||||
UserGraphItem user = 1;
|
||||
SectionGraphItem section = 2;
|
||||
ItemGraphItem item = 3;
|
||||
}
|
||||
}
|
||||
|
||||
service Graph {
|
||||
// Commands
|
||||
rpc CreateSection(CreateSectionRequest) returns (CreateSectionResponse);
|
||||
rpc CreateRoot(CreateRootRequest) returns (CreateRootResponse);
|
||||
rpc CreateItem(CreateItemRequest) returns (CreateItemResponse);
|
||||
rpc UpdateItem(UpdateItemRequest) returns (UpdateItemResponse);
|
||||
rpc ToggleItem(ToggleItemRequest) returns (ToggleItemResponse);
|
||||
|
||||
// Queriers
|
||||
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
||||
rpc Get(GetRequest) returns (GetReply);
|
||||
|
||||
}
|
||||
|
||||
// Commands
|
||||
message CreateSectionRequest {
|
||||
string root = 1;
|
||||
repeated string path = 2;
|
||||
}
|
||||
message CreateSectionResponse {}
|
||||
|
||||
message CreateRootRequest {
|
||||
string root = 1;
|
||||
}
|
||||
message CreateRootResponse {}
|
||||
|
||||
message CreateItemRequest {
|
||||
string root = 1;
|
||||
repeated string path = 2;
|
||||
ItemGraphItem item = 3;
|
||||
}
|
||||
message CreateItemResponse {}
|
||||
|
||||
message UpdateItemRequest {
|
||||
string root = 1;
|
||||
repeated string path = 2;
|
||||
ItemGraphItem item = 3;
|
||||
}
|
||||
message UpdateItemResponse {}
|
||||
|
||||
message ToggleItemRequest {
|
||||
string root = 1;
|
||||
repeated string path = 2;
|
||||
}
|
||||
message ToggleItemResponse {}
|
||||
|
||||
// Queries
|
||||
message GetAvailableRootsRequest {}
|
||||
message GetAvailableRootsResponse {
|
||||
repeated string roots = 1;
|
||||
}
|
||||
|
||||
message GetRequest {
|
||||
string root = 1;
|
||||
repeated string paths = 2;
|
||||
}
|
||||
|
||||
message GetReply {
|
||||
GraphItem item = 1;
|
||||
}
|
3
crates/hyperlog-protos/src/lib.rs
Normal file
3
crates/hyperlog-protos/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod hyperlog {
|
||||
tonic::include_proto!("hyperlog"); // Specify the same package name as in your .proto file
|
||||
}
|
30
crates/hyperlog-server/Cargo.toml
Normal file
30
crates/hyperlog-server/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "hyperlog-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
hyperlog-core.workspace = true
|
||||
hyperlog-protos.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
axum.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
uuid.workspace = true
|
||||
tonic.workspace = true
|
||||
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
sqlx = { version = "0.7.4", features = [
|
||||
"runtime-tokio",
|
||||
"tls-rustls",
|
||||
"postgres",
|
||||
"uuid",
|
||||
"time",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
similar-asserts = "1.5.0"
|
||||
tempfile = "3.10.1"
|
@@ -0,0 +1,17 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE TABLE roots (
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
root_name VARCHAR(255) UNIQUE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE nodes (
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
root_id UUID NOT NULL,
|
||||
path VARCHAR NOT NULL,
|
||||
item_type VARCHAR NOT NULL,
|
||||
item_content JSONB
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX idx_unique_root_path ON nodes(root_id, path);
|
||||
|
154
crates/hyperlog-server/src/commands.rs
Normal file
154
crates/hyperlog-server/src/commands.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
|
||||
use crate::{
|
||||
services::{
|
||||
create_item::{self, CreateItem, CreateItemExt},
|
||||
create_root::{self, CreateRoot, CreateRootExt},
|
||||
create_section::{self, CreateSection, CreateSectionExt},
|
||||
toggle_item::{self, ToggleItem, ToggleItemExt},
|
||||
update_item::{self, UpdateItem, UpdateItemExt},
|
||||
},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum Command {
|
||||
CreateRoot {
|
||||
root: String,
|
||||
},
|
||||
CreateSection {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
CreateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
UpdateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
ToggleItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
Move {
|
||||
root: String,
|
||||
src: Vec<String>,
|
||||
dest: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Commander {
|
||||
create_root: CreateRoot,
|
||||
create_section: CreateSection,
|
||||
create_item: CreateItem,
|
||||
update_item: UpdateItem,
|
||||
toggle_item: ToggleItem,
|
||||
}
|
||||
|
||||
impl Commander {
|
||||
pub fn new(
|
||||
create_root: CreateRoot,
|
||||
create_section: CreateSection,
|
||||
create_item: CreateItem,
|
||||
update_item: UpdateItem,
|
||||
toggle_item: ToggleItem,
|
||||
) -> Self {
|
||||
Self {
|
||||
create_root,
|
||||
create_section,
|
||||
create_item,
|
||||
update_item,
|
||||
toggle_item,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: Command) -> anyhow::Result<()> {
|
||||
match cmd {
|
||||
Command::CreateRoot { root } => {
|
||||
self.create_root
|
||||
.execute(create_root::Request { root })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::CreateSection { root, path } => {
|
||||
self.create_section
|
||||
.execute(create_section::Request { root, path })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::CreateItem {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
} => {
|
||||
self.create_item
|
||||
.execute(create_item::Request {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::UpdateItem {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
} => {
|
||||
self.update_item
|
||||
.execute(update_item::Request {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::ToggleItem { root, path } => {
|
||||
self.toggle_item
|
||||
.execute(toggle_item::Request { root, path })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::Move { .. } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CommanderExt {
|
||||
fn commander(&self) -> Commander;
|
||||
}
|
||||
|
||||
impl CommanderExt for SharedState {
|
||||
fn commander(&self) -> Commander {
|
||||
Commander::new(
|
||||
self.create_root_service(),
|
||||
self.create_section_service(),
|
||||
self.create_item_service(),
|
||||
self.update_item_service(),
|
||||
self.toggle_item_service(),
|
||||
)
|
||||
}
|
||||
}
|
454
crates/hyperlog-server/src/external_grpc.rs
Normal file
454
crates/hyperlog-server/src/external_grpc.rs
Normal file
@@ -0,0 +1,454 @@
|
||||
use hyperlog_protos::hyperlog::{
|
||||
graph_server::{Graph, GraphServer},
|
||||
*,
|
||||
};
|
||||
use std::{collections::HashMap, net::SocketAddr};
|
||||
use tonic::{transport, Response};
|
||||
|
||||
use crate::{
|
||||
commands::{Command, Commander, CommanderExt},
|
||||
querier::{Querier, QuerierExt},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Server {
|
||||
querier: Querier,
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(querier: Querier, commander: Commander) -> Self {
|
||||
Self { querier, commander }
|
||||
}
|
||||
}
|
||||
|
||||
#[tonic::async_trait]
|
||||
impl Graph for Server {
|
||||
async fn create_item(
|
||||
&self,
|
||||
request: tonic::Request<CreateItemRequest>,
|
||||
) -> std::result::Result<tonic::Response<CreateItemResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("create item: req({:?})", req);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req.path.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain empty paths".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.contains("."))
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain `.`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let item = match req.item {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"item cannot contain empty or null".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
self.commander
|
||||
.execute(Command::CreateItem {
|
||||
root: req.root,
|
||||
path: req.path,
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
state: match item.item_state {
|
||||
Some(item_graph_item::ItemState::Done(_)) => {
|
||||
hyperlog_core::log::ItemState::Done
|
||||
}
|
||||
Some(item_graph_item::ItemState::NotDone(_)) => {
|
||||
hyperlog_core::log::ItemState::NotDone
|
||||
}
|
||||
None => hyperlog_core::log::ItemState::default(),
|
||||
},
|
||||
})
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(CreateItemResponse {}))
|
||||
}
|
||||
|
||||
async fn create_root(
|
||||
&self,
|
||||
request: tonic::Request<CreateRootRequest>,
|
||||
) -> std::result::Result<tonic::Response<CreateRootResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("create root: req({:?})", req);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.commander
|
||||
.execute(Command::CreateRoot { root: req.root })
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(CreateRootResponse {}))
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req.path.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain empty paths".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.contains("."))
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain `.`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.commander
|
||||
.execute(Command::CreateSection {
|
||||
root: req.root,
|
||||
path: req.path,
|
||||
})
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(CreateSectionResponse {}))
|
||||
}
|
||||
|
||||
async fn get(
|
||||
&self,
|
||||
request: tonic::Request<GetRequest>,
|
||||
) -> std::result::Result<tonic::Response<GetReply>, tonic::Status> {
|
||||
let msg = request.get_ref();
|
||||
|
||||
tracing::trace!("get: req({:?})", msg);
|
||||
|
||||
let res = self
|
||||
.querier
|
||||
.get(&msg.root, msg.paths.clone())
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
match res {
|
||||
Some(item) => Ok(Response::new(GetReply {
|
||||
item: Some(to_native(&item).map_err(to_tonic_err)?),
|
||||
})),
|
||||
None => {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::NotFound,
|
||||
"failed to find any valid roots",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_available_roots(
|
||||
&self,
|
||||
request: tonic::Request<GetAvailableRootsRequest>,
|
||||
) -> std::result::Result<tonic::Response<GetAvailableRootsResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("get available roots: req({:?})", req);
|
||||
|
||||
let roots = match self
|
||||
.querier
|
||||
.get_available_roots()
|
||||
.await
|
||||
.map_err(to_tonic_err)?
|
||||
{
|
||||
Some(roots) => roots,
|
||||
None => {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::NotFound,
|
||||
"failed to find any valid roots",
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Response::new(GetAvailableRootsResponse { roots }))
|
||||
}
|
||||
|
||||
async fn update_item(
|
||||
&self,
|
||||
request: tonic::Request<UpdateItemRequest>,
|
||||
) -> std::result::Result<tonic::Response<UpdateItemResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("update item: req({:?})", req);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req.path.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain empty paths".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.contains("."))
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain `.`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let item = match req.item {
|
||||
Some(i) => i,
|
||||
None => {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"item cannot contain empty or null".to_string(),
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
self.commander
|
||||
.execute(Command::UpdateItem {
|
||||
root: req.root,
|
||||
path: req.path,
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
state: match item.item_state {
|
||||
Some(item_graph_item::ItemState::Done(_)) => {
|
||||
hyperlog_core::log::ItemState::Done
|
||||
}
|
||||
Some(item_graph_item::ItemState::NotDone(_)) => {
|
||||
hyperlog_core::log::ItemState::NotDone
|
||||
}
|
||||
None => hyperlog_core::log::ItemState::default(),
|
||||
},
|
||||
})
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(UpdateItemResponse {}))
|
||||
}
|
||||
|
||||
async fn toggle_item(
|
||||
&self,
|
||||
request: tonic::Request<ToggleItemRequest>,
|
||||
) -> std::result::Result<tonic::Response<ToggleItemResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("update item: req({:?})", req);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req.path.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain empty paths".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.contains("."))
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain `.`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.commander
|
||||
.execute(Command::ToggleItem {
|
||||
root: req.root,
|
||||
path: req.path,
|
||||
})
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(ToggleItemResponse {}))
|
||||
}
|
||||
}
|
||||
|
||||
fn to_native(from: &hyperlog_core::log::GraphItem) -> anyhow::Result<GraphItem> {
|
||||
match from {
|
||||
hyperlog_core::log::GraphItem::User(section)
|
||||
| hyperlog_core::log::GraphItem::Section(section) => {
|
||||
let mut root = HashMap::new();
|
||||
for (key, value) in section.iter() {
|
||||
root.insert(key.to_string(), to_native(value)?);
|
||||
}
|
||||
match from {
|
||||
hyperlog_core::log::GraphItem::User(_) => Ok(GraphItem {
|
||||
contents: Some(graph_item::Contents::User(UserGraphItem { items: root })),
|
||||
}),
|
||||
hyperlog_core::log::GraphItem::Section(_) => Ok(GraphItem {
|
||||
contents: Some(graph_item::Contents::Section(SectionGraphItem {
|
||||
items: root,
|
||||
})),
|
||||
}),
|
||||
_ => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
}
|
||||
hyperlog_core::log::GraphItem::Item {
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
} => Ok(GraphItem {
|
||||
contents: Some(graph_item::Contents::Item(ItemGraphItem {
|
||||
title: title.to_owned(),
|
||||
description: description.to_owned(),
|
||||
item_state: Some(match state {
|
||||
hyperlog_core::log::ItemState::NotDone => {
|
||||
item_graph_item::ItemState::NotDone(ItemStateNotDone {})
|
||||
}
|
||||
hyperlog_core::log::ItemState::Done => {
|
||||
item_graph_item::ItemState::Done(ItemStateDone {})
|
||||
}
|
||||
}),
|
||||
})),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: create more defined protobuf categories for errors
|
||||
fn to_tonic_err(err: anyhow::Error) -> tonic::Status {
|
||||
tonic::Status::new(tonic::Code::Unknown, err.to_string())
|
||||
}
|
||||
|
||||
pub trait ServerExt {
|
||||
fn grpc_server(&self) -> Server;
|
||||
}
|
||||
|
||||
impl ServerExt for SharedState {
|
||||
fn grpc_server(&self) -> Server {
|
||||
Server::new(self.querier(), self.commander())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn serve(state: &SharedState, host: SocketAddr) -> anyhow::Result<()> {
|
||||
tracing::info!("listening on {}", host);
|
||||
|
||||
let graph_server = state.grpc_server();
|
||||
|
||||
transport::Server::builder()
|
||||
.add_service(GraphServer::new(graph_server))
|
||||
.serve(host)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
40
crates/hyperlog-server/src/external_http.rs
Normal file
40
crates/hyperlog-server/src/external_http.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
async fn root() -> &'static str {
|
||||
"Hello, hyperlog!"
|
||||
}
|
||||
|
||||
pub async fn serve(state: &SharedState, host: &SocketAddr) -> anyhow::Result<()> {
|
||||
let app = Router::new()
|
||||
.route("/", get(root))
|
||||
.with_state(state.clone())
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||
// Log the matched route's path (with placeholders not filled in).
|
||||
// Use request.uri() or OriginalUri if you want the real path.
|
||||
let matched_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str);
|
||||
|
||||
tracing::info_span!(
|
||||
"http_request",
|
||||
method = ?request.method(),
|
||||
matched_path,
|
||||
some_other_field = tracing::field::Empty,
|
||||
)
|
||||
}), // ...
|
||||
);
|
||||
|
||||
tracing::info!("listening on {}", host);
|
||||
let listener = tokio::net::TcpListener::bind(host).await.unwrap();
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
40
crates/hyperlog-server/src/internal_http.rs
Normal file
40
crates/hyperlog-server/src/internal_http.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
async fn root() -> &'static str {
|
||||
"Hello, hyperlog!"
|
||||
}
|
||||
|
||||
pub async fn serve(state: &SharedState, host: &SocketAddr) -> anyhow::Result<()> {
|
||||
let app = Router::new()
|
||||
.route("/", get(root))
|
||||
.with_state(state.clone())
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||
// Log the matched route's path (with placeholders not filled in).
|
||||
// Use request.uri() or OriginalUri if you want the real path.
|
||||
let matched_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str);
|
||||
|
||||
tracing::info_span!(
|
||||
"http_request",
|
||||
method = ?request.method(),
|
||||
matched_path,
|
||||
some_other_field = tracing::field::Empty,
|
||||
)
|
||||
}), // ...
|
||||
);
|
||||
|
||||
tracing::info!("listening on {}", host);
|
||||
let listener = tokio::net::TcpListener::bind(host).await.unwrap();
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
50
crates/hyperlog-server/src/lib.rs
Normal file
50
crates/hyperlog-server/src/lib.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
#![feature(map_try_insert)]
|
||||
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use crate::state::{SharedState, State};
|
||||
|
||||
mod external_grpc;
|
||||
mod external_http;
|
||||
mod internal_http;
|
||||
|
||||
mod commands;
|
||||
mod querier;
|
||||
|
||||
mod state;
|
||||
|
||||
mod services;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServeOptions {
|
||||
pub external_http: SocketAddr,
|
||||
pub internal_http: SocketAddr,
|
||||
|
||||
pub external_grpc: SocketAddr,
|
||||
}
|
||||
|
||||
pub async fn serve(opts: ServeOptions) -> anyhow::Result<()> {
|
||||
let ctrl_c = async {
|
||||
tokio::signal::ctrl_c().await.unwrap();
|
||||
tracing::info!("kill signal received, shutting down");
|
||||
};
|
||||
tracing::debug!("setting up dependencies");
|
||||
let state = SharedState(Arc::new(State::new().await?));
|
||||
|
||||
tracing::debug!("serve starting");
|
||||
tokio::select!(
|
||||
res = external_http::serve(&state, &opts.external_http) => {
|
||||
res?
|
||||
},
|
||||
res = internal_http::serve(&state, &opts.internal_http) => {
|
||||
res?
|
||||
},
|
||||
res = external_grpc::serve(&state, opts.external_grpc) => {
|
||||
res?
|
||||
}
|
||||
() = ctrl_c => {}
|
||||
);
|
||||
tracing::debug!("serve finalized");
|
||||
|
||||
Ok(())
|
||||
}
|
62
crates/hyperlog-server/src/querier.rs
Normal file
62
crates/hyperlog-server/src/querier.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use hyperlog_core::log::GraphItem;
|
||||
|
||||
use crate::{
|
||||
services::{
|
||||
get_available_roots::{self, GetAvailableRoots, GetAvailableRootsExt},
|
||||
get_graph::{GetGraph, GetGraphExt},
|
||||
},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct Querier {
|
||||
get_available_roots: GetAvailableRoots,
|
||||
get_graph: GetGraph,
|
||||
}
|
||||
|
||||
impl Querier {
|
||||
pub fn new(get_available_roots: GetAvailableRoots, get_graph: GetGraph) -> Self {
|
||||
Self {
|
||||
get_available_roots,
|
||||
get_graph,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_available_roots(&self) -> anyhow::Result<Option<Vec<String>>> {
|
||||
let res = self
|
||||
.get_available_roots
|
||||
.execute(get_available_roots::Request {})
|
||||
.await?;
|
||||
|
||||
if res.roots.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(res.roots))
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
&self,
|
||||
root: &str,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> anyhow::Result<Option<GraphItem>> {
|
||||
let graph = self
|
||||
.get_graph
|
||||
.execute(crate::services::get_graph::Request {
|
||||
root: root.into(),
|
||||
path: path.into_iter().map(|s| s.into()).collect(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(Some(graph.item))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait QuerierExt {
|
||||
fn querier(&self) -> Querier;
|
||||
}
|
||||
|
||||
impl QuerierExt for SharedState {
|
||||
fn querier(&self) -> Querier {
|
||||
Querier::new(self.get_available_roots_service(), self.get_graph_service())
|
||||
}
|
||||
}
|
8
crates/hyperlog-server/src/services.rs
Normal file
8
crates/hyperlog-server/src/services.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
pub mod create_item;
|
||||
pub mod create_root;
|
||||
pub mod create_section;
|
||||
pub mod toggle_item;
|
||||
pub mod update_item;
|
||||
|
||||
pub mod get_available_roots;
|
||||
pub mod get_graph;
|
104
crates/hyperlog-server/src/services/create_item.rs
Normal file
104
crates/hyperlog-server/src/services/create_item.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use sqlx::types::Json;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateItem {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub state: ItemState,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct ItemContent {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub state: ItemState,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Section {}
|
||||
|
||||
impl CreateItem {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let Root { id: root_id, .. } =
|
||||
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||
.bind(req.root)
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
|
||||
match req.path.split_last() {
|
||||
Some((_, section_path)) => {
|
||||
if !section_path.is_empty() {
|
||||
let Section { .. } = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
nodes
|
||||
WHERE
|
||||
root_id = $1 AND
|
||||
path = $2 AND
|
||||
item_type = 'SECTION'
|
||||
"#,
|
||||
)
|
||||
.bind(root_id)
|
||||
.bind(section_path.join("."))
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let node_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO nodes
|
||||
(id, root_id, path, item_type, item_content)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5)"#,
|
||||
)
|
||||
.bind(node_id)
|
||||
.bind(root_id)
|
||||
.bind(req.path.join("."))
|
||||
.bind("ITEM".to_string())
|
||||
.bind(Json(ItemContent {
|
||||
title: req.title,
|
||||
description: req.description,
|
||||
state: req.state,
|
||||
}))
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
}
|
||||
None => anyhow::bail!("path most contain at least one item"),
|
||||
}
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateItemExt {
|
||||
fn create_item_service(&self) -> CreateItem;
|
||||
}
|
||||
|
||||
impl CreateItemExt for SharedState {
|
||||
fn create_item_service(&self) -> CreateItem {
|
||||
CreateItem::new(self.db.clone())
|
||||
}
|
||||
}
|
38
crates/hyperlog-server/src/services/create_root.rs
Normal file
38
crates/hyperlog-server/src/services/create_root.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateRoot {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
impl CreateRoot {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let root_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(r#"INSERT INTO roots (id, root_name) VALUES ($1, $2)"#)
|
||||
.bind(root_id)
|
||||
.bind(req.root)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateRootExt {
|
||||
fn create_root_service(&self) -> CreateRoot;
|
||||
}
|
||||
|
||||
impl CreateRootExt for SharedState {
|
||||
fn create_root_service(&self) -> CreateRoot {
|
||||
CreateRoot::new(self.db.clone())
|
||||
}
|
||||
}
|
61
crates/hyperlog-server/src/services/create_section.rs
Normal file
61
crates/hyperlog-server/src/services/create_section.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateSection {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl CreateSection {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let Root { id: root_id, .. } =
|
||||
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||
.bind(req.root)
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
|
||||
// FIXME: implement consistency check on path
|
||||
|
||||
let node_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO nodes
|
||||
(id, root_id, path, item_type, item_content)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5)"#,
|
||||
)
|
||||
.bind(node_id)
|
||||
.bind(root_id)
|
||||
.bind(req.path.join("."))
|
||||
.bind("SECTION".to_string())
|
||||
.bind(None::<serde_json::Value>)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateSectionExt {
|
||||
fn create_section_service(&self) -> CreateSection;
|
||||
}
|
||||
|
||||
impl CreateSectionExt for SharedState {
|
||||
fn create_section_service(&self) -> CreateSection {
|
||||
CreateSection::new(self.db.clone())
|
||||
}
|
||||
}
|
51
crates/hyperlog-server/src/services/get_available_roots.rs
Normal file
51
crates/hyperlog-server/src/services/get_available_roots.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GetAvailableRoots {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {}
|
||||
pub struct Response {
|
||||
pub roots: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
pub struct Root {
|
||||
root_name: String,
|
||||
}
|
||||
|
||||
impl GetAvailableRoots {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, _req: Request) -> anyhow::Result<Response> {
|
||||
let roots: Vec<Root> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
roots
|
||||
LIMIT
|
||||
100
|
||||
"#,
|
||||
)
|
||||
.fetch_all(&self.db)
|
||||
.await?;
|
||||
|
||||
Ok(Response {
|
||||
roots: roots.into_iter().map(|i| i.root_name).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetAvailableRootsExt {
|
||||
fn get_available_roots_service(&self) -> GetAvailableRoots;
|
||||
}
|
||||
|
||||
impl GetAvailableRootsExt for SharedState {
|
||||
fn get_available_roots_service(&self) -> GetAvailableRoots {
|
||||
GetAvailableRoots::new(self.db.clone())
|
||||
}
|
||||
}
|
368
crates/hyperlog-server/src/services/get_graph.rs
Normal file
368
crates/hyperlog-server/src/services/get_graph.rs
Normal file
@@ -0,0 +1,368 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use hyperlog_core::log::{GraphItem, ItemState};
|
||||
use serde::Deserialize;
|
||||
use sqlx::types::Json;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
use self::engine::Engine;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GetGraph {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
}
|
||||
pub struct Response {
|
||||
pub item: GraphItem,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Item {
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Debug)]
|
||||
struct Node {
|
||||
path: String,
|
||||
item_type: String,
|
||||
item_content: Option<Json<serde_json::Value>>,
|
||||
}
|
||||
|
||||
impl GetGraph {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let Root { id: root_id, .. } =
|
||||
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||
.bind(&req.root)
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
|
||||
let nodes: Vec<Node> = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
nodes
|
||||
WHERE
|
||||
root_id = $1
|
||||
LIMIT
|
||||
1000
|
||||
"#,
|
||||
)
|
||||
.bind(root_id)
|
||||
.fetch_all(&self.db)
|
||||
.await?;
|
||||
|
||||
let item = self.build_graph(req.root, req.path, nodes)?;
|
||||
|
||||
Ok(Response { item })
|
||||
}
|
||||
|
||||
fn build_graph(
|
||||
&self,
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
mut nodes: Vec<Node>,
|
||||
) -> anyhow::Result<GraphItem> {
|
||||
nodes.sort_by(|a, b| a.path.cmp(&b.path));
|
||||
let mut engine = Engine::default();
|
||||
engine.create_root(&root)?;
|
||||
|
||||
self.get_graph_items(&root, &mut engine, &nodes)?;
|
||||
|
||||
engine
|
||||
.get(&root, &path.iter().map(|s| s.as_str()).collect::<Vec<_>>())
|
||||
.ok_or(anyhow::anyhow!("failed to find a valid graph"))
|
||||
.cloned()
|
||||
}
|
||||
|
||||
fn get_graph_items(
|
||||
&self,
|
||||
root: &str,
|
||||
engine: &mut Engine,
|
||||
nodes: &Vec<Node>,
|
||||
) -> anyhow::Result<()> {
|
||||
for node in nodes {
|
||||
if let Some(item) = self.get_graph_item(node) {
|
||||
let path = node.path.split('.').collect::<Vec<_>>();
|
||||
engine.create(root, &path, item)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_graph_item(&self, node: &Node) -> Option<GraphItem> {
|
||||
match node.item_type.as_str() {
|
||||
"SECTION" => Some(GraphItem::Section(BTreeMap::default())),
|
||||
"ITEM" => {
|
||||
if let Some(content) = &node.item_content {
|
||||
let item: Item = serde_json::from_value(content.0.clone()).ok()?;
|
||||
|
||||
Some(GraphItem::Item {
|
||||
title: item.title,
|
||||
description: item.description,
|
||||
state: item.state,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetGraphExt {
|
||||
fn get_graph_service(&self) -> GetGraph;
|
||||
}
|
||||
|
||||
impl GetGraphExt for SharedState {
|
||||
fn get_graph_service(&self) -> GetGraph {
|
||||
GetGraph::new(self.db.clone())
|
||||
}
|
||||
}
|
||||
|
||||
mod engine {
|
||||
|
||||
use std::{collections::BTreeMap, fmt::Display};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use hyperlog_core::log::{Graph, GraphItem, ItemState};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Engine {
|
||||
graph: Graph,
|
||||
}
|
||||
|
||||
impl Engine {
|
||||
#[allow(dead_code)]
|
||||
pub fn engine_from_str(input: &str) -> anyhow::Result<Self> {
|
||||
let graph: Graph = serde_json::from_str(input)?;
|
||||
|
||||
Ok(Self { graph })
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn to_str(&self) -> anyhow::Result<String> {
|
||||
serde_json::to_string_pretty(&self.graph).context("failed to serialize graph")
|
||||
}
|
||||
|
||||
pub fn create_root(&mut self, root: &str) -> anyhow::Result<()> {
|
||||
self.graph
|
||||
.try_insert(root.to_string(), GraphItem::User(BTreeMap::default()))
|
||||
.map_err(|_| anyhow!("entry was already found, aborting"))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create(&mut self, root: &str, path: &[&str], item: GraphItem) -> anyhow::Result<()> {
|
||||
let graph = &mut self.graph;
|
||||
|
||||
let (last, items) = path.split_last().ok_or(anyhow!(
|
||||
"path cannot be empty, must contain at least one item"
|
||||
))?;
|
||||
|
||||
let root = graph
|
||||
.get_mut(root)
|
||||
.ok_or(anyhow!("root was missing a user, aborting"))?;
|
||||
|
||||
let mut current_item = root;
|
||||
for section in items {
|
||||
match current_item {
|
||||
GraphItem::User(u) => match u.get_mut(section.to_owned()) {
|
||||
Some(graph_item) => {
|
||||
current_item = graph_item;
|
||||
}
|
||||
None => anyhow::bail!("path: {} section was not found", section),
|
||||
},
|
||||
GraphItem::Item { .. } => anyhow::bail!("path: {} was already found", section),
|
||||
GraphItem::Section(s) => match s.get_mut(section.to_owned()) {
|
||||
Some(graph_item) => {
|
||||
current_item = graph_item;
|
||||
}
|
||||
None => anyhow::bail!("path: {} section was not found", section),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
match current_item {
|
||||
GraphItem::User(u) => {
|
||||
u.insert(last.to_string(), item);
|
||||
}
|
||||
GraphItem::Section(s) => {
|
||||
s.insert(last.to_string(), item);
|
||||
}
|
||||
GraphItem::Item { .. } => anyhow::bail!("cannot insert an item into an item"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, root: &str, path: &[&str]) -> Option<&GraphItem> {
|
||||
let root = self.graph.get(root)?;
|
||||
|
||||
root.get(path)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_mut(&mut self, root: &str, path: &[&str]) -> Option<&mut GraphItem> {
|
||||
let root = self.graph.get_mut(root)?;
|
||||
|
||||
root.get_mut(path)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn take(&mut self, root: &str, path: &[&str]) -> Option<GraphItem> {
|
||||
let root = self.graph.get_mut(root)?;
|
||||
|
||||
root.take(path)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn section_move(
|
||||
&mut self,
|
||||
root: &str,
|
||||
src_path: &[&str],
|
||||
dest_path: &[&str],
|
||||
) -> anyhow::Result<()> {
|
||||
let src = self
|
||||
.take(root, src_path)
|
||||
.ok_or(anyhow!("failed to find source path"))?;
|
||||
|
||||
let dest = self
|
||||
.get_mut(root, dest_path)
|
||||
.ok_or(anyhow!("failed to find destination"))?;
|
||||
|
||||
let src_item = src_path
|
||||
.last()
|
||||
.ok_or(anyhow!("src path must have at least one item"))?;
|
||||
|
||||
match dest {
|
||||
GraphItem::User(u) => {
|
||||
u.try_insert(src_item.to_string(), src)
|
||||
.map_err(|_e| anyhow!("key was already found, aborting: {}", src_item))?;
|
||||
}
|
||||
GraphItem::Section(s) => {
|
||||
s.try_insert(src_item.to_string(), src)
|
||||
.map_err(|_e| anyhow!("key was already found, aborting: {}", src_item))?;
|
||||
}
|
||||
GraphItem::Item { .. } => {
|
||||
anyhow::bail!(
|
||||
"failed to insert src at item, item doesn't support arbitrary items"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn delete(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {
|
||||
self.take(root, path)
|
||||
.map(|_| ())
|
||||
.ok_or(anyhow!("item was not found"))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn toggle_item(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {
|
||||
if let Some(item) = self.get_mut(root, path) {
|
||||
match item {
|
||||
GraphItem::Item { state, .. } => match state {
|
||||
ItemState::NotDone => *state = ItemState::Done,
|
||||
ItemState::Done => *state = ItemState::NotDone,
|
||||
},
|
||||
_ => {
|
||||
anyhow::bail!("{}.{:?} is not an item", root, path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn update_item(
|
||||
&mut self,
|
||||
root: &str,
|
||||
path: &[&str],
|
||||
item: &GraphItem,
|
||||
) -> anyhow::Result<()> {
|
||||
if let Some((name, dest_last)) = path.split_last() {
|
||||
if let Some(parent) = self.get_mut(root, dest_last) {
|
||||
match parent {
|
||||
GraphItem::User(s) | GraphItem::Section(s) => {
|
||||
if let Some(mut existing) = s.remove(*name) {
|
||||
match (&mut existing, item) {
|
||||
(
|
||||
GraphItem::Item {
|
||||
title: ex_title,
|
||||
description: ex_desc,
|
||||
state: ex_state,
|
||||
},
|
||||
GraphItem::Item {
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
},
|
||||
) => {
|
||||
ex_title.clone_from(title);
|
||||
ex_desc.clone_from(description);
|
||||
ex_state.clone_from(state);
|
||||
|
||||
let title = title.replace(".", "-");
|
||||
s.insert(title, existing.clone());
|
||||
}
|
||||
_ => {
|
||||
anyhow::bail!(
|
||||
"path: {}.{} found is not an item",
|
||||
root,
|
||||
path.join(".")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
GraphItem::Item { .. } => {
|
||||
anyhow::bail!("cannot rename when item is placed in an item")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_roots(&self) -> Option<Vec<String>> {
|
||||
let items = self.graph.keys().cloned().collect::<Vec<_>>();
|
||||
if items.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(items)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Engine {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let output = serde_json::to_string_pretty(&self.graph).unwrap();
|
||||
f.write_str(&output)
|
||||
}
|
||||
}
|
||||
}
|
105
crates/hyperlog-server/src/services/toggle_item.rs
Normal file
105
crates/hyperlog-server/src/services/toggle_item.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use sqlx::types::Json;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ToggleItem {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
#[derive(serde::Serialize, serde::Deserialize)]
|
||||
struct ItemContent {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub state: ItemState,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Node {
|
||||
id: uuid::Uuid,
|
||||
item_content: Option<Json<ItemContent>>,
|
||||
}
|
||||
|
||||
impl ToggleItem {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let Root { id: root_id, .. } =
|
||||
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||
.bind(req.root)
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
let Node {
|
||||
id: node_id,
|
||||
mut item_content,
|
||||
} = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
nodes
|
||||
WHERE
|
||||
root_id = $1
|
||||
AND path = $2
|
||||
AND item_type = $3
|
||||
"#,
|
||||
)
|
||||
.bind(root_id)
|
||||
.bind(req.path.join("."))
|
||||
.bind("ITEM")
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
|
||||
if let Some(ref mut content) = item_content {
|
||||
content.state = match content.state {
|
||||
ItemState::NotDone => ItemState::Done,
|
||||
ItemState::Done => ItemState::NotDone,
|
||||
}
|
||||
}
|
||||
|
||||
let res = sqlx::query(
|
||||
r#"
|
||||
UPDATE
|
||||
nodes
|
||||
SET
|
||||
item_content = $1
|
||||
WHERE
|
||||
id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(item_content)
|
||||
.bind(node_id)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
if res.rows_affected() != 1 {
|
||||
anyhow::bail!("failed to update item");
|
||||
}
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToggleItemExt {
|
||||
fn toggle_item_service(&self) -> ToggleItem;
|
||||
}
|
||||
|
||||
impl ToggleItemExt for SharedState {
|
||||
fn toggle_item_service(&self) -> ToggleItem {
|
||||
ToggleItem::new(self.db.clone())
|
||||
}
|
||||
}
|
107
crates/hyperlog-server/src/services/update_item.rs
Normal file
107
crates/hyperlog-server/src/services/update_item.rs
Normal file
@@ -0,0 +1,107 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use sqlx::types::Json;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UpdateItem {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub state: ItemState,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct ItemContent {
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub state: ItemState,
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl UpdateItem {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let Root { id: root_id, .. } =
|
||||
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||
.bind(req.root)
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
let Root { id: node_id } = sqlx::query_as(
|
||||
r#"
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
nodes
|
||||
WHERE
|
||||
root_id = $1
|
||||
AND path = $2
|
||||
AND item_type = $3
|
||||
"#,
|
||||
)
|
||||
.bind(root_id)
|
||||
.bind(req.path.join("."))
|
||||
.bind("ITEM")
|
||||
.fetch_one(&self.db)
|
||||
.await?;
|
||||
|
||||
let (_, rest) = req
|
||||
.path
|
||||
.split_last()
|
||||
.ok_or(anyhow::anyhow!("expected path to have at least one item"))?;
|
||||
|
||||
let mut rest = rest.to_vec();
|
||||
rest.push(req.title.replace(".", "-"));
|
||||
|
||||
let res = sqlx::query(
|
||||
r#"
|
||||
UPDATE
|
||||
nodes
|
||||
SET
|
||||
item_content = $1,
|
||||
path = $2
|
||||
WHERE
|
||||
id = $3
|
||||
"#,
|
||||
)
|
||||
.bind(Json(ItemContent {
|
||||
title: req.title,
|
||||
description: req.description,
|
||||
state: req.state,
|
||||
}))
|
||||
.bind(rest.join("."))
|
||||
.bind(node_id)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
if res.rows_affected() != 1 {
|
||||
anyhow::bail!("failed to update item");
|
||||
}
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpdateItemExt {
|
||||
fn update_item_service(&self) -> UpdateItem;
|
||||
}
|
||||
|
||||
impl UpdateItemExt for SharedState {
|
||||
fn update_item_service(&self) -> UpdateItem {
|
||||
UpdateItem::new(self.db.clone())
|
||||
}
|
||||
}
|
37
crates/hyperlog-server/src/state.rs
Normal file
37
crates/hyperlog-server/src/state.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedState(pub Arc<State>);
|
||||
|
||||
impl Deref for SharedState {
|
||||
type Target = Arc<State>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub db: Pool<Postgres>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
let db = sqlx::PgPool::connect(
|
||||
&std::env::var("DATABASE_URL").context("DATABASE_URL is not set")?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
sqlx::migrate!("migrations/crdb")
|
||||
.set_locking(false)
|
||||
.run(&db)
|
||||
.await?;
|
||||
|
||||
let _ = sqlx::query("SELECT 1;").fetch_one(&db).await?;
|
||||
|
||||
Ok(Self { db })
|
||||
}
|
||||
}
|
@@ -6,19 +6,26 @@ repository = "https://git.front.kjuulh.io/kjuulh/hyperlog"
|
||||
|
||||
[dependencies]
|
||||
hyperlog-core.workspace = true
|
||||
hyperlog-protos.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
tracing.workspace = true
|
||||
tracing-subscriber.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
itertools.workspace = true
|
||||
tonic.workspace = true
|
||||
futures.workspace = true
|
||||
|
||||
ratatui = "0.26.2"
|
||||
crossterm = { version = "0.27.0", features = ["event-stream"] }
|
||||
directories = "5.0.1"
|
||||
human-panic = "2.0.0"
|
||||
ropey = "1.6.1"
|
||||
bus = "2.4.1"
|
||||
dirs = "5.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
similar-asserts = "1.5.0"
|
||||
tempfile = "3.10.1"
|
||||
|
@@ -5,8 +5,12 @@ use ratatui::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
command_parser::CommandParser, commands::IntoCommand,
|
||||
components::graph_explorer::GraphExplorer, state::SharedState, Msg,
|
||||
command_parser::CommandParser,
|
||||
commands::{batch::BatchCommand, IntoCommand},
|
||||
components::graph_explorer::GraphExplorer,
|
||||
models::IOEvent,
|
||||
state::SharedState,
|
||||
Msg,
|
||||
};
|
||||
|
||||
use self::{
|
||||
@@ -26,10 +30,10 @@ pub enum Dialog {
|
||||
}
|
||||
|
||||
impl Dialog {
|
||||
pub fn get_command(&self) -> Option<hyperlog_core::commander::Command> {
|
||||
pub fn get_command(&self) -> Option<impl IntoCommand> {
|
||||
match self {
|
||||
Dialog::CreateItem { state } => state.get_command(),
|
||||
Dialog::EditItem { state } => state.get_command(),
|
||||
Dialog::CreateItem { state } => state.get_command().map(|c| c.into_command()),
|
||||
Dialog::EditItem { state } => state.get_command().map(|c| c.into_command()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,12 +86,21 @@ impl<'a> App<'a> {
|
||||
pub fn update(&mut self, msg: Msg) -> anyhow::Result<impl IntoCommand> {
|
||||
tracing::trace!("handling msg: {:?}", msg);
|
||||
|
||||
let mut batch = BatchCommand::default();
|
||||
|
||||
match &msg {
|
||||
Msg::ItemCreated(IOEvent::Success(()))
|
||||
| Msg::ItemUpdated(IOEvent::Success(()))
|
||||
| Msg::SectionCreated(IOEvent::Success(()))
|
||||
| Msg::ItemToggled(IOEvent::Success(())) => {
|
||||
batch.with(self.graph_explorer.new_update_graph());
|
||||
}
|
||||
Msg::MoveRight => self.graph_explorer.move_right()?,
|
||||
Msg::MoveLeft => self.graph_explorer.move_left()?,
|
||||
Msg::MoveDown => self.graph_explorer.move_down()?,
|
||||
Msg::MoveUp => self.graph_explorer.move_up()?,
|
||||
Msg::OpenCreateItemDialog => self.open_dialog(),
|
||||
Msg::OpenCreateItemDialogBelow => self.open_dialog_below(),
|
||||
Msg::OpenEditItemDialog { item } => self.open_edit_item_dialog(item),
|
||||
Msg::EnterInsertMode => self.mode = Mode::Insert,
|
||||
Msg::EnterViewMode => self.mode = Mode::View,
|
||||
@@ -97,7 +110,10 @@ impl<'a> App<'a> {
|
||||
}
|
||||
Msg::Interact => match self.focus {
|
||||
AppFocus::Dialog => {}
|
||||
AppFocus::Graph => self.graph_explorer.interact()?,
|
||||
AppFocus::Graph => {
|
||||
let cmd = self.graph_explorer.interact()?;
|
||||
batch.with(cmd);
|
||||
}
|
||||
},
|
||||
Msg::SubmitCommand { command } => {
|
||||
tracing::info!("submitting command");
|
||||
@@ -108,11 +124,9 @@ impl<'a> App<'a> {
|
||||
if command.is_write() {
|
||||
if let Some(dialog) = &self.dialog {
|
||||
if let Some(output) = dialog.get_command() {
|
||||
self.state.commander.execute(output)?;
|
||||
batch.with(output.into_command());
|
||||
}
|
||||
}
|
||||
|
||||
self.graph_explorer.update_graph()?;
|
||||
}
|
||||
|
||||
if command.is_quit() {
|
||||
@@ -121,26 +135,31 @@ impl<'a> App<'a> {
|
||||
}
|
||||
}
|
||||
AppFocus::Graph => {
|
||||
if let Some(msg) = self.graph_explorer.execute_command(&command)? {
|
||||
if let Some(cmd) = self.graph_explorer.execute_command(&command)? {
|
||||
self.command = None;
|
||||
return Ok(msg.into_command());
|
||||
batch.with(cmd);
|
||||
}
|
||||
|
||||
if command.is_quit() {
|
||||
return Ok(Msg::QuitApp.into_command());
|
||||
batch.with(Msg::QuitApp.into_command());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
self.command = None;
|
||||
return Ok(Msg::EnterViewMode.into_command());
|
||||
batch.with(Msg::EnterViewMode.into_command());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let cmd = self.graph_explorer.inner.update(&msg);
|
||||
if let Some(cmd) = cmd {
|
||||
batch.with(cmd);
|
||||
}
|
||||
|
||||
if let Some(command) = &mut self.command {
|
||||
let cmd = command.update(&msg)?;
|
||||
return Ok(cmd.into_command());
|
||||
batch.with(cmd);
|
||||
} else if let Some(dialog) = &mut self.dialog {
|
||||
match dialog {
|
||||
Dialog::CreateItem { state } => state.update(&msg)?,
|
||||
@@ -148,7 +167,7 @@ impl<'a> App<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
Ok(().into_command())
|
||||
Ok(batch.into_command())
|
||||
}
|
||||
|
||||
fn open_dialog(&mut self) {
|
||||
@@ -158,18 +177,34 @@ impl<'a> App<'a> {
|
||||
|
||||
self.focus = AppFocus::Dialog;
|
||||
self.dialog = Some(Dialog::CreateItem {
|
||||
state: CreateItemState::new(root, path),
|
||||
state: CreateItemState::new(&self.state, root, path),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn open_dialog_below(&mut self) {
|
||||
if self.dialog.is_none() {
|
||||
let root = self.root.clone();
|
||||
let path = self.graph_explorer.get_current_path();
|
||||
|
||||
if let Some((_, rest)) = path.split_last() {
|
||||
let path = rest.to_vec();
|
||||
|
||||
self.focus = AppFocus::Dialog;
|
||||
self.dialog = Some(Dialog::CreateItem {
|
||||
state: CreateItemState::new(&self.state, root, path),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn open_edit_item_dialog(&mut self, item: &GraphItem) {
|
||||
if self.dialog.is_none() {
|
||||
let root = self.root.clone();
|
||||
let path = self.graph_explorer.get_current_path();
|
||||
|
||||
self.dialog = Some(Dialog::EditItem {
|
||||
state: EditItemState::new(root, path, item),
|
||||
state: EditItemState::new(&self.state, root, path, item),
|
||||
});
|
||||
self.command = None;
|
||||
self.focus = AppFocus::Dialog;
|
||||
|
@@ -1,7 +1,12 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use itertools::Itertools;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
|
||||
use crate::models::Msg;
|
||||
use crate::{
|
||||
commands::{create_item::CreateItemCommandExt, IntoCommand},
|
||||
models::Msg,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
use super::{InputBuffer, InputField};
|
||||
|
||||
@@ -23,10 +28,16 @@ pub struct CreateItemState {
|
||||
description: InputBuffer,
|
||||
|
||||
focused: CreateItemFocused,
|
||||
|
||||
state: SharedState,
|
||||
}
|
||||
|
||||
impl CreateItemState {
|
||||
pub fn new(root: impl Into<String>, path: impl IntoIterator<Item = impl Into<String>>) -> Self {
|
||||
pub fn new(
|
||||
state: &SharedState,
|
||||
root: impl Into<String>,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> Self {
|
||||
let root = root.into();
|
||||
let path = path.into_iter().map(|p| p.into()).collect_vec();
|
||||
|
||||
@@ -37,6 +48,8 @@ impl CreateItemState {
|
||||
title: Default::default(),
|
||||
description: Default::default(),
|
||||
focused: Default::default(),
|
||||
|
||||
state: state.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,21 +74,29 @@ impl CreateItemState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_command(&self) -> Option<hyperlog_core::commander::Command> {
|
||||
pub fn get_command(&self) -> Option<impl IntoCommand> {
|
||||
let title = self.title.string();
|
||||
let description = self.description.string();
|
||||
|
||||
if !title.is_empty() {
|
||||
let mut path = self.path.clone();
|
||||
path.push(title.replace([' ', '.'], "-"));
|
||||
path.push(title.replace(['.'], "-"));
|
||||
|
||||
Some(hyperlog_core::commander::Command::CreateItem {
|
||||
root: self.root.clone(),
|
||||
path,
|
||||
title: title.trim().into(),
|
||||
description: description.trim().into(),
|
||||
state: hyperlog_core::log::ItemState::NotDone,
|
||||
})
|
||||
Some(self.state.create_item_command().command(
|
||||
&self.root,
|
||||
&path.iter().map(|i| i.as_str()).collect_vec(),
|
||||
title.trim(),
|
||||
description.trim(),
|
||||
&ItemState::NotDone,
|
||||
))
|
||||
|
||||
// Some(commander::Command::CreateItem {
|
||||
// root: self.root.clone(),
|
||||
// path,
|
||||
// title: title.trim().into(),
|
||||
// description: description.trim().into(),
|
||||
// state: hyperlog_core::log::ItemState::NotDone,
|
||||
// })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@@ -2,7 +2,11 @@ use hyperlog_core::log::GraphItem;
|
||||
use itertools::Itertools;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
|
||||
use crate::models::Msg;
|
||||
use crate::{
|
||||
commands::{update_item::UpdateItemCommandExt, IntoCommand},
|
||||
models::Msg,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
use super::{InputBuffer, InputField};
|
||||
|
||||
@@ -26,10 +30,13 @@ pub struct EditItemState {
|
||||
item: GraphItem,
|
||||
|
||||
focused: EditItemFocused,
|
||||
|
||||
state: SharedState,
|
||||
}
|
||||
|
||||
impl EditItemState {
|
||||
pub fn new(
|
||||
state: &SharedState,
|
||||
root: impl Into<String>,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
item: &GraphItem,
|
||||
@@ -47,6 +54,8 @@ impl EditItemState {
|
||||
title.set_position(title_len);
|
||||
|
||||
Self {
|
||||
state: state.clone(),
|
||||
|
||||
root,
|
||||
path,
|
||||
|
||||
@@ -82,24 +91,36 @@ impl EditItemState {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_command(&self) -> Option<hyperlog_core::commander::Command> {
|
||||
pub fn get_command(&self) -> Option<impl IntoCommand> {
|
||||
let title = self.title.string();
|
||||
let description = self.description.string();
|
||||
|
||||
if !title.is_empty() {
|
||||
let path = self.path.clone();
|
||||
|
||||
Some(hyperlog_core::commander::Command::UpdateItem {
|
||||
root: self.root.clone(),
|
||||
path,
|
||||
title: title.trim().into(),
|
||||
description: description.trim().into(),
|
||||
state: match &self.item {
|
||||
Some(self.state.update_item_command().command(
|
||||
&self.root,
|
||||
&path.iter().map(|s| s.as_str()).collect_vec(),
|
||||
title.trim(),
|
||||
description.trim(),
|
||||
match &self.item {
|
||||
GraphItem::User(_) => Default::default(),
|
||||
GraphItem::Section(_) => Default::default(),
|
||||
GraphItem::Item { state, .. } => state.clone(),
|
||||
},
|
||||
})
|
||||
))
|
||||
|
||||
// Some(commander::Command::UpdateItem {
|
||||
// root: self.root.clone(),
|
||||
// path,
|
||||
// title: title.trim().into(),
|
||||
// description: description.trim().into(),
|
||||
// state: match &self.item {
|
||||
// GraphItem::User(_) => Default::default(),
|
||||
// GraphItem::Section(_) => Default::default(),
|
||||
// GraphItem::Item { state, .. } => state.clone(),
|
||||
// },
|
||||
// })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@@ -6,10 +6,13 @@ pub enum Commands {
|
||||
WriteQuit,
|
||||
Archive,
|
||||
CreateSection { name: String },
|
||||
CreateItem { name: String },
|
||||
CreateBelow { name: String },
|
||||
Edit,
|
||||
|
||||
ShowAll,
|
||||
HideDone,
|
||||
Test,
|
||||
}
|
||||
|
||||
impl Commands {
|
||||
@@ -39,9 +42,16 @@ impl CommandParser {
|
||||
"cs" | "create-section" => rest.first().map(|name| Commands::CreateSection {
|
||||
name: name.to_string(),
|
||||
}),
|
||||
"ci" | "create-item" => Some(Commands::CreateItem {
|
||||
name: rest.join(" ").to_string(),
|
||||
}),
|
||||
"cb" | "create-below" => Some(Commands::CreateBelow {
|
||||
name: rest.join(" ").to_string(),
|
||||
}),
|
||||
"e" | "edit" => Some(Commands::Edit),
|
||||
"show-all" => Some(Commands::ShowAll),
|
||||
"hide-done" => Some(Commands::HideDone),
|
||||
"test" => Some(Commands::Test),
|
||||
_ => None,
|
||||
},
|
||||
None => None,
|
||||
|
74
crates/hyperlog-tui/src/commander.rs
Normal file
74
crates/hyperlog-tui/src/commander.rs
Normal file
@@ -0,0 +1,74 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use serde::Serialize;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{events::Events, shared_engine::SharedEngine, storage::Storage};
|
||||
|
||||
mod local;
|
||||
mod remote;
|
||||
|
||||
#[derive(Serialize, PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Command {
|
||||
CreateRoot {
|
||||
root: String,
|
||||
},
|
||||
CreateSection {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
CreateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
UpdateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
ToggleItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
Move {
|
||||
root: String,
|
||||
src: Vec<String>,
|
||||
dest: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
enum CommanderVariant {
|
||||
Local(local::Commander),
|
||||
Remote(remote::Commander),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Commander {
|
||||
variant: CommanderVariant,
|
||||
}
|
||||
|
||||
impl Commander {
|
||||
pub fn local(engine: SharedEngine, storage: Storage, events: Events) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
variant: CommanderVariant::Local(local::Commander::new(engine, storage, events)?),
|
||||
})
|
||||
}
|
||||
|
||||
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<()> {
|
||||
match &self.variant {
|
||||
CommanderVariant::Local(commander) => commander.execute(cmd),
|
||||
CommanderVariant::Remote(commander) => commander.execute(cmd).await,
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,48 +1,12 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use serde::Serialize;
|
||||
use hyperlog_core::log::GraphItem;
|
||||
|
||||
use crate::{
|
||||
events::Events,
|
||||
log::{GraphItem, ItemState},
|
||||
shared_engine::SharedEngine,
|
||||
storage::Storage,
|
||||
};
|
||||
use crate::{events::Events, shared_engine::SharedEngine, storage::Storage};
|
||||
|
||||
#[derive(Serialize, PartialEq, Eq, Debug, Clone)]
|
||||
pub enum Command {
|
||||
CreateRoot {
|
||||
root: String,
|
||||
},
|
||||
CreateSection {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
CreateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
UpdateItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
title: String,
|
||||
description: String,
|
||||
state: ItemState,
|
||||
},
|
||||
ToggleItem {
|
||||
root: String,
|
||||
path: Vec<String>,
|
||||
},
|
||||
Move {
|
||||
root: String,
|
||||
src: Vec<String>,
|
||||
dest: Vec<String>,
|
||||
},
|
||||
}
|
||||
use super::Command;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Commander {
|
||||
engine: SharedEngine,
|
||||
storage: Storage,
|
149
crates/hyperlog-tui/src/commander/remote.rs
Normal file
149
crates/hyperlog-tui/src/commander/remote.rs
Normal file
@@ -0,0 +1,149 @@
|
||||
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 } => {
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(CreateRootRequest { root });
|
||||
let response = client.create_root(request).await?;
|
||||
let res = response.into_inner();
|
||||
//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 { root, path });
|
||||
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,
|
||||
} => {
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(CreateItemRequest {
|
||||
root,
|
||||
path,
|
||||
item: Some(ItemGraphItem {
|
||||
title,
|
||||
description,
|
||||
item_state: Some(match state {
|
||||
hyperlog_core::log::ItemState::NotDone => {
|
||||
item_graph_item::ItemState::NotDone(ItemStateNotDone {})
|
||||
}
|
||||
hyperlog_core::log::ItemState::Done => {
|
||||
item_graph_item::ItemState::Done(ItemStateDone {})
|
||||
}
|
||||
}),
|
||||
}),
|
||||
});
|
||||
let response = client.create_item(request).await?;
|
||||
let res = response.into_inner();
|
||||
// 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 } => {
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(ToggleItemRequest { root, path });
|
||||
let response = client.toggle_item(request).await?;
|
||||
let res = response.into_inner();
|
||||
}
|
||||
Command::UpdateItem {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
} => {
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(UpdateItemRequest {
|
||||
root,
|
||||
path,
|
||||
item: Some(ItemGraphItem {
|
||||
title,
|
||||
description,
|
||||
item_state: Some(match state {
|
||||
hyperlog_core::log::ItemState::NotDone => {
|
||||
item_graph_item::ItemState::NotDone(ItemStateNotDone {})
|
||||
}
|
||||
hyperlog_core::log::ItemState::Done => {
|
||||
item_graph_item::ItemState::Done(ItemStateDone {})
|
||||
}
|
||||
}),
|
||||
}),
|
||||
});
|
||||
let response = client.update_item(request).await?;
|
||||
let res = response.into_inner();
|
||||
// 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(())
|
||||
}
|
||||
}
|
@@ -1,3 +1,14 @@
|
||||
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||
|
||||
pub mod batch;
|
||||
|
||||
pub mod create_item;
|
||||
pub mod create_section;
|
||||
pub mod open_update_item_dialog;
|
||||
pub mod toggle_item;
|
||||
pub mod update_graph;
|
||||
pub mod update_item;
|
||||
|
||||
use crate::models::Msg;
|
||||
|
||||
pub trait IntoCommand {
|
||||
@@ -6,7 +17,7 @@ pub trait IntoCommand {
|
||||
|
||||
impl IntoCommand for () {
|
||||
fn into_command(self) -> Command {
|
||||
Command::new(|| None)
|
||||
Command::new(|_| None)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,16 +27,47 @@ impl IntoCommand for Command {
|
||||
}
|
||||
}
|
||||
|
||||
type CommandFunc = dyn FnOnce(Dispatch) -> Option<Msg>;
|
||||
|
||||
pub struct Command {
|
||||
func: Box<dyn FnOnce() -> Option<Msg>>,
|
||||
func: Box<CommandFunc>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn new<T: FnOnce() -> Option<Msg> + 'static>(f: T) -> Self {
|
||||
pub fn new<T: FnOnce(Dispatch) -> Option<Msg> + 'static>(f: T) -> Self {
|
||||
Self { func: Box::new(f) }
|
||||
}
|
||||
|
||||
pub fn execute(self) -> Option<Msg> {
|
||||
self.func.call_once(())
|
||||
pub fn execute(self, dispatch: Dispatch) -> Option<Msg> {
|
||||
self.func.call_once((dispatch,))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_dispatch() -> (Dispatch, Receiver) {
|
||||
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
|
||||
|
||||
(Dispatch { sender: tx }, Receiver { receiver: rx })
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Dispatch {
|
||||
sender: UnboundedSender<Msg>,
|
||||
}
|
||||
|
||||
impl Dispatch {
|
||||
pub fn send(&self, msg: Msg) {
|
||||
if let Err(e) = self.sender.send(msg) {
|
||||
tracing::warn!("failed to send event: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Receiver {
|
||||
receiver: UnboundedReceiver<Msg>,
|
||||
}
|
||||
|
||||
impl Receiver {
|
||||
pub async fn next(&mut self) -> Option<Msg> {
|
||||
self.receiver.recv().await
|
||||
}
|
||||
}
|
||||
|
41
crates/hyperlog-tui/src/commands/batch.rs
Normal file
41
crates/hyperlog-tui/src/commands/batch.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use super::IntoCommand;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BatchCommand {
|
||||
commands: Vec<super::Command>,
|
||||
}
|
||||
|
||||
impl BatchCommand {
|
||||
pub fn with(&mut self, cmd: impl IntoCommand) -> &mut Self {
|
||||
self.commands.push(cmd.into_command());
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCommand for Vec<super::Command> {
|
||||
fn into_command(self) -> super::Command {
|
||||
BatchCommand::from(self).into_command()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<super::Command>> for BatchCommand {
|
||||
fn from(value: Vec<super::Command>) -> Self {
|
||||
BatchCommand { commands: value }
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoCommand for BatchCommand {
|
||||
fn into_command(self) -> super::Command {
|
||||
super::Command::new(|dispatch| {
|
||||
for command in self.commands {
|
||||
let msg = command.execute(dispatch.clone());
|
||||
if let Some(msg) = msg {
|
||||
dispatch.send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
73
crates/hyperlog-tui/src/commands/create_item.rs
Normal file
73
crates/hyperlog-tui/src/commands/create_item.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
commander::{self, Commander},
|
||||
models::{IOEvent, Msg},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct CreateItemCommand {
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl CreateItemCommand {
|
||||
pub fn new(commander: Commander) -> Self {
|
||||
Self { commander }
|
||||
}
|
||||
|
||||
pub fn command(
|
||||
self,
|
||||
root: &str,
|
||||
path: &[&str],
|
||||
title: &str,
|
||||
description: &str,
|
||||
state: &ItemState,
|
||||
) -> super::Command {
|
||||
let root = root.to_owned();
|
||||
let path = path.iter().map(|s| s.to_string()).collect_vec();
|
||||
let title = title.to_string();
|
||||
let description = description.to_string();
|
||||
let state = state.clone();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(Msg::ItemCreated(IOEvent::Initialized));
|
||||
|
||||
match self
|
||||
.commander
|
||||
.execute(commander::Command::CreateItem {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
dispatch.send(Msg::ItemCreated(IOEvent::Success(())));
|
||||
}
|
||||
Err(e) => {
|
||||
dispatch.send(Msg::ItemCreated(IOEvent::Failure(e.to_string())));
|
||||
}
|
||||
}
|
||||
});
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateItemCommandExt {
|
||||
fn create_item_command(&self) -> CreateItemCommand;
|
||||
}
|
||||
|
||||
impl CreateItemCommandExt for SharedState {
|
||||
fn create_item_command(&self) -> CreateItemCommand {
|
||||
CreateItemCommand::new(self.commander.clone())
|
||||
}
|
||||
}
|
59
crates/hyperlog-tui/src/commands/create_section.rs
Normal file
59
crates/hyperlog-tui/src/commands/create_section.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
commander::{self, Commander},
|
||||
models::IOEvent,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct CreateSectionCommand {
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl CreateSectionCommand {
|
||||
pub fn new(commander: Commander) -> Self {
|
||||
Self { commander }
|
||||
}
|
||||
|
||||
pub fn command(self, root: &str, path: &[&str]) -> super::Command {
|
||||
let root = root.to_owned();
|
||||
let path = path.iter().map(|s| s.to_string()).collect_vec();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(crate::models::Msg::SectionCreated(IOEvent::Initialized));
|
||||
|
||||
match self
|
||||
.commander
|
||||
.execute(commander::Command::CreateSection { root, path })
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
dispatch.send(crate::models::Msg::SectionCreated(IOEvent::Success(())));
|
||||
}
|
||||
Err(e) => {
|
||||
dispatch.send(crate::models::Msg::SectionCreated(IOEvent::Failure(
|
||||
e.to_string(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
});
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateSectionCommandExt {
|
||||
fn create_section_command(&self) -> CreateSectionCommand;
|
||||
}
|
||||
|
||||
impl CreateSectionCommandExt for SharedState {
|
||||
fn create_section_command(&self) -> CreateSectionCommand {
|
||||
CreateSectionCommand::new(self.commander.clone())
|
||||
}
|
||||
}
|
59
crates/hyperlog-tui/src/commands/open_update_item_dialog.rs
Normal file
59
crates/hyperlog-tui/src/commands/open_update_item_dialog.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use crate::{
|
||||
models::{IOEvent, Msg},
|
||||
querier::Querier,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct OpenUpdateItemDialogCommand {
|
||||
querier: Querier,
|
||||
}
|
||||
|
||||
impl OpenUpdateItemDialogCommand {
|
||||
pub fn new(querier: Querier) -> Self {
|
||||
Self { querier }
|
||||
}
|
||||
|
||||
pub fn command(self, root: &str, path: Vec<String>) -> super::Command {
|
||||
let root = root.to_string();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(Msg::OpenUpdateItemDialog(IOEvent::Initialized));
|
||||
|
||||
let item = match self.querier.get_async(&root, path).await {
|
||||
Ok(item) => match item {
|
||||
Some(item) => {
|
||||
dispatch.send(Msg::OpenUpdateItemDialog(IOEvent::Success(())));
|
||||
item
|
||||
}
|
||||
None => {
|
||||
dispatch.send(Msg::OpenUpdateItemDialog(IOEvent::Failure(
|
||||
"failed to find a valid item for path".into(),
|
||||
)));
|
||||
|
||||
return;
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
dispatch.send(Msg::OpenUpdateItemDialog(IOEvent::Failure(e.to_string())));
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
dispatch.send(Msg::OpenEditItemDialog { item });
|
||||
});
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait OpenUpdateItemDialogCommandExt {
|
||||
fn open_update_item_dialog_command(&self) -> OpenUpdateItemDialogCommand;
|
||||
}
|
||||
|
||||
impl OpenUpdateItemDialogCommandExt for SharedState {
|
||||
fn open_update_item_dialog_command(&self) -> OpenUpdateItemDialogCommand {
|
||||
OpenUpdateItemDialogCommand::new(self.querier.clone())
|
||||
}
|
||||
}
|
59
crates/hyperlog-tui/src/commands/toggle_item.rs
Normal file
59
crates/hyperlog-tui/src/commands/toggle_item.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
commander::{self, Commander},
|
||||
models::IOEvent,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct ToggleItemCommand {
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl ToggleItemCommand {
|
||||
pub fn new(commander: Commander) -> Self {
|
||||
Self { commander }
|
||||
}
|
||||
|
||||
pub fn command(self, root: &str, path: &[&str]) -> super::Command {
|
||||
let root = root.to_owned();
|
||||
let path = path.iter().map(|s| s.to_string()).collect_vec();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(crate::models::Msg::ItemToggled(IOEvent::Initialized));
|
||||
|
||||
match self
|
||||
.commander
|
||||
.execute(commander::Command::ToggleItem { root, path })
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
dispatch.send(crate::models::Msg::ItemToggled(IOEvent::Success(())));
|
||||
}
|
||||
Err(e) => {
|
||||
dispatch.send(crate::models::Msg::ItemToggled(IOEvent::Failure(
|
||||
e.to_string(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
});
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToggleItemCommandExt {
|
||||
fn toggle_item_command(&self) -> ToggleItemCommand;
|
||||
}
|
||||
|
||||
impl ToggleItemCommandExt for SharedState {
|
||||
fn toggle_item_command(&self) -> ToggleItemCommand {
|
||||
ToggleItemCommand::new(self.commander.clone())
|
||||
}
|
||||
}
|
61
crates/hyperlog-tui/src/commands/update_graph.rs
Normal file
61
crates/hyperlog-tui/src/commands/update_graph.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
models::{IOEvent, Msg},
|
||||
querier::Querier,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct UpdateGraphCommand {
|
||||
querier: Querier,
|
||||
}
|
||||
|
||||
impl UpdateGraphCommand {
|
||||
pub fn new(querier: Querier) -> Self {
|
||||
Self { querier }
|
||||
}
|
||||
|
||||
pub fn command(self, root: &str, path: &[&str]) -> super::Command {
|
||||
let root = root.to_owned();
|
||||
let path = path.iter().map(|i| i.to_string()).collect_vec();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
let now = std::time::SystemTime::now();
|
||||
dispatch.send(Msg::GraphUpdated(IOEvent::Initialized));
|
||||
|
||||
match self.querier.get_async(&root, path).await {
|
||||
Ok(Some(graph)) => {
|
||||
dispatch.send(Msg::GraphUpdated(IOEvent::Optimistic(graph.clone())));
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
dispatch.send(Msg::GraphUpdated(IOEvent::Success(graph)))
|
||||
}
|
||||
Ok(None) => dispatch.send(Msg::GraphUpdated(IOEvent::Failure(
|
||||
"graph was not found user root".into(),
|
||||
))),
|
||||
Err(e) => dispatch.send(Msg::GraphUpdated(IOEvent::Failure(format!("{e}")))),
|
||||
}
|
||||
|
||||
let elapsed = now.elapsed().expect("to be able to get time");
|
||||
tracing::trace!("UpdateGraphCommand took: {}nanos", elapsed.as_nanos());
|
||||
});
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpdateGraphCommandExt {
|
||||
fn update_graph_command(&self) -> UpdateGraphCommand;
|
||||
}
|
||||
|
||||
impl UpdateGraphCommandExt for SharedState {
|
||||
fn update_graph_command(&self) -> UpdateGraphCommand {
|
||||
UpdateGraphCommand::new(self.querier.clone())
|
||||
}
|
||||
}
|
76
crates/hyperlog-tui/src/commands/update_item.rs
Normal file
76
crates/hyperlog-tui/src/commands/update_item.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use hyperlog_core::log::ItemState;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::{
|
||||
commander::{self, Commander},
|
||||
models::IOEvent,
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
pub struct UpdateItemCommand {
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl UpdateItemCommand {
|
||||
pub fn new(commander: Commander) -> Self {
|
||||
Self { commander }
|
||||
}
|
||||
|
||||
pub fn command(
|
||||
self,
|
||||
root: &str,
|
||||
path: &[&str],
|
||||
title: &str,
|
||||
description: &str,
|
||||
state: ItemState,
|
||||
) -> super::Command {
|
||||
let root = root.to_owned();
|
||||
let path = path.iter().map(|s| s.to_string()).collect_vec();
|
||||
let title = title.to_string();
|
||||
let description = description.to_string();
|
||||
let state = state.clone();
|
||||
|
||||
super::Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(crate::models::Msg::ItemUpdated(IOEvent::Initialized));
|
||||
|
||||
match self
|
||||
.commander
|
||||
.execute(commander::Command::UpdateItem {
|
||||
root,
|
||||
path,
|
||||
title,
|
||||
description,
|
||||
state,
|
||||
})
|
||||
.await
|
||||
{
|
||||
Ok(()) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
|
||||
dispatch.send(crate::models::Msg::ItemUpdated(IOEvent::Success(())));
|
||||
}
|
||||
Err(e) => {
|
||||
dispatch.send(crate::models::Msg::ItemUpdated(IOEvent::Failure(
|
||||
e.to_string(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
});
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait UpdateItemCommandExt {
|
||||
fn update_item_command(&self) -> UpdateItemCommand;
|
||||
}
|
||||
|
||||
impl UpdateItemCommandExt for SharedState {
|
||||
fn update_item_command(&self) -> UpdateItemCommand {
|
||||
UpdateItemCommand::new(self.commander.clone())
|
||||
}
|
||||
}
|
@@ -1,9 +1,18 @@
|
||||
use anyhow::Result;
|
||||
use hyperlog_core::log::GraphItem;
|
||||
use itertools::Itertools;
|
||||
use ratatui::{prelude::*, widgets::*};
|
||||
|
||||
use crate::{
|
||||
command_parser::Commands, components::movement_graph::GraphItemType, models::Msg,
|
||||
command_parser::Commands,
|
||||
commands::{
|
||||
batch::BatchCommand, create_item::CreateItemCommandExt,
|
||||
create_section::CreateSectionCommandExt,
|
||||
open_update_item_dialog::OpenUpdateItemDialogCommandExt, toggle_item::ToggleItemCommandExt,
|
||||
update_graph::UpdateGraphCommandExt, Command, IntoCommand,
|
||||
},
|
||||
components::movement_graph::GraphItemType,
|
||||
models::{IOEvent, Msg},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
@@ -46,6 +55,31 @@ pub struct GraphExplorerState<'a> {
|
||||
graph: Option<GraphItem>,
|
||||
}
|
||||
|
||||
impl<'a> GraphExplorerState<'a> {
|
||||
pub fn update(&mut self, msg: &Msg) -> Option<Command> {
|
||||
if let Msg::GraphUpdated(graph_update) = msg {
|
||||
match graph_update {
|
||||
IOEvent::Initialized => {
|
||||
tracing::trace!("initialized graph");
|
||||
}
|
||||
IOEvent::Success(graph) => {
|
||||
tracing::trace!("graph updated successfully");
|
||||
self.graph = Some(graph.clone());
|
||||
}
|
||||
IOEvent::Failure(e) => {
|
||||
tracing::error!("graph update failed: {}", e);
|
||||
}
|
||||
IOEvent::Optimistic(graph) => {
|
||||
tracing::trace!("graph updated optimistically");
|
||||
self.graph = Some(graph.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> GraphExplorer<'a> {
|
||||
pub fn new(root: String, state: SharedState) -> Self {
|
||||
Self {
|
||||
@@ -60,19 +94,31 @@ impl<'a> GraphExplorer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_graph(&mut self) -> Result<&mut Self> {
|
||||
pub fn new_update_graph(&self) -> Command {
|
||||
self.state.update_graph_command().command(
|
||||
&self.inner.root,
|
||||
&self
|
||||
.inner
|
||||
.current_path
|
||||
.map(|p| p.split(".").collect_vec())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn update_graph(&mut self) -> Result<&mut Self> {
|
||||
let now = std::time::SystemTime::now();
|
||||
|
||||
let graph = self
|
||||
.state
|
||||
.querier
|
||||
.get(
|
||||
.get_async(
|
||||
&self.inner.root,
|
||||
self.inner
|
||||
.current_path
|
||||
.map(|p| p.split('.').collect::<Vec<_>>())
|
||||
.unwrap_or_default(),
|
||||
)
|
||||
.await?
|
||||
.ok_or(anyhow::anyhow!("graph should've had an item"))?;
|
||||
|
||||
self.inner.graph = Some(graph);
|
||||
@@ -184,7 +230,9 @@ impl<'a> GraphExplorer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_command(&mut self, command: &Commands) -> anyhow::Result<Option<Msg>> {
|
||||
pub fn execute_command(&mut self, command: &Commands) -> anyhow::Result<Option<Command>> {
|
||||
let mut batch = BatchCommand::default();
|
||||
|
||||
match command {
|
||||
Commands::Archive => {
|
||||
if !self.get_current_path().is_empty() {
|
||||
@@ -194,14 +242,55 @@ impl<'a> GraphExplorer<'a> {
|
||||
Commands::CreateSection { name } => {
|
||||
if !name.is_empty() {
|
||||
let mut path = self.get_current_path();
|
||||
path.push(name.replace(" ", "-").replace(".", "-"));
|
||||
path.push(name.replace(".", "-"));
|
||||
|
||||
self.state.commander.execute(
|
||||
hyperlog_core::commander::Command::CreateSection {
|
||||
root: self.inner.root.clone(),
|
||||
path,
|
||||
},
|
||||
)?;
|
||||
// self.state
|
||||
// .commander
|
||||
// .execute(commander::Command::CreateSection {
|
||||
// root: self.inner.root.clone(),
|
||||
// path,
|
||||
// })?;
|
||||
|
||||
let cmd = self.state.create_section_command().command(
|
||||
&self.inner.root,
|
||||
&path.iter().map(|i| i.as_str()).collect_vec(),
|
||||
);
|
||||
|
||||
batch.with(cmd.into_command());
|
||||
}
|
||||
}
|
||||
Commands::CreateItem { name } => {
|
||||
if !name.is_empty() {
|
||||
let mut path = self.get_current_path();
|
||||
path.push(name.replace(".", " "));
|
||||
let cmd = self.state.create_item_command().command(
|
||||
&self.inner.root,
|
||||
&path.iter().map(|i| i.as_str()).collect_vec(),
|
||||
name,
|
||||
"",
|
||||
&hyperlog_core::log::ItemState::default(),
|
||||
);
|
||||
|
||||
batch.with(cmd.into_command());
|
||||
}
|
||||
}
|
||||
Commands::CreateBelow { name } => {
|
||||
if !name.is_empty() {
|
||||
let path = self.get_current_path();
|
||||
if let Some((_, path)) = path.split_last() {
|
||||
let mut path = path.to_vec();
|
||||
path.push(name.replace(".", " "));
|
||||
|
||||
let cmd = self.state.create_item_command().command(
|
||||
&self.inner.root,
|
||||
&path.iter().map(|i| i.as_str()).collect_vec(),
|
||||
name,
|
||||
"",
|
||||
&hyperlog_core::log::ItemState::default(),
|
||||
);
|
||||
|
||||
batch.with(cmd.into_command());
|
||||
}
|
||||
}
|
||||
}
|
||||
Commands::Edit => {
|
||||
@@ -218,11 +307,11 @@ impl<'a> GraphExplorer<'a> {
|
||||
todo!("cannot edit section at the moment")
|
||||
}
|
||||
GraphItemType::Item { .. } => {
|
||||
if let Some(item) = self.state.querier.get(&self.inner.root, path) {
|
||||
if let GraphItem::Item { .. } = item {
|
||||
return Ok(Some(Msg::OpenEditItemDialog { item }));
|
||||
}
|
||||
}
|
||||
batch.with(
|
||||
self.state
|
||||
.open_update_item_dialog_command()
|
||||
.command(&self.inner.root, path),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,29 +322,54 @@ impl<'a> GraphExplorer<'a> {
|
||||
Commands::HideDone => {
|
||||
self.inner.display_options.filter_by = FilterBy::NotDone;
|
||||
}
|
||||
Commands::Test => {
|
||||
return Ok(Some(Command::new(|dispatch| {
|
||||
tokio::spawn(async move {
|
||||
dispatch.send(Msg::MoveDown);
|
||||
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
|
||||
dispatch.send(Msg::EnterViewMode);
|
||||
});
|
||||
|
||||
None
|
||||
})));
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.update_graph()?;
|
||||
//self.update_graph()?;
|
||||
|
||||
Ok(None)
|
||||
Ok(Some(batch.into_command()))
|
||||
}
|
||||
|
||||
pub(crate) fn interact(&mut self) -> anyhow::Result<()> {
|
||||
pub(crate) fn interact(&mut self) -> anyhow::Result<Command> {
|
||||
let mut batch = BatchCommand::default();
|
||||
|
||||
if !self.get_current_path().is_empty() {
|
||||
tracing::info!("toggling state of items");
|
||||
|
||||
self.state
|
||||
.commander
|
||||
.execute(hyperlog_core::commander::Command::ToggleItem {
|
||||
root: self.inner.root.to_string(),
|
||||
path: self.get_current_path(),
|
||||
})?;
|
||||
// self.state
|
||||
// .commander
|
||||
// .execute(commander::Command::ToggleItem {
|
||||
// root: self.inner.root.to_string(),
|
||||
// path: self.get_current_path(),
|
||||
// })?;
|
||||
|
||||
let cmd = self.state.toggle_item_command().command(
|
||||
&self.inner.root,
|
||||
&self
|
||||
.get_current_path()
|
||||
.iter()
|
||||
.map(|i| i.as_str())
|
||||
.collect_vec(),
|
||||
);
|
||||
|
||||
batch.with(cmd.into_command());
|
||||
}
|
||||
|
||||
self.update_graph()?;
|
||||
//self.update_graph()?;
|
||||
|
||||
Ok(())
|
||||
Ok(batch.into_command())
|
||||
}
|
||||
}
|
||||
|
||||
|
56
crates/hyperlog-tui/src/core_state.rs
Normal file
56
crates/hyperlog-tui/src/core_state.rs
Normal file
@@ -0,0 +1,56 @@
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::{
|
||||
commander::Commander, events::Events, querier::Querier, shared_engine::SharedEngine,
|
||||
storage::Storage,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct State {
|
||||
engine: SharedEngine,
|
||||
pub storage: Storage,
|
||||
events: Events,
|
||||
|
||||
pub commander: Commander,
|
||||
pub querier: Querier,
|
||||
}
|
||||
|
||||
pub enum Backend {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new(backend: Backend) -> anyhow::Result<Self> {
|
||||
let storage = Storage::new();
|
||||
let engine = storage.load()?;
|
||||
let events = Events::default();
|
||||
let engine = SharedEngine::from(engine);
|
||||
|
||||
let (querier, commander) = match backend {
|
||||
Backend::Local => (
|
||||
Querier::local(&engine),
|
||||
Commander::local(engine.clone(), storage.clone(), events.clone())?,
|
||||
),
|
||||
Backend::Remote => {
|
||||
let channel = Channel::from_static("http://localhost:4000")
|
||||
.connect()
|
||||
.await?;
|
||||
|
||||
(
|
||||
Querier::remote(channel.clone()).await?,
|
||||
Commander::remote(channel)?,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
engine: engine.clone(),
|
||||
storage: storage.clone(),
|
||||
events: events.clone(),
|
||||
|
||||
commander,
|
||||
querier,
|
||||
})
|
||||
}
|
||||
}
|
@@ -1,8 +1,7 @@
|
||||
use std::{collections::BTreeMap, fmt::Display};
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
use crate::log::{Graph, GraphItem, ItemState};
|
||||
use hyperlog_core::log::{Graph, GraphItem, ItemState};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct Engine {
|
||||
@@ -218,10 +217,9 @@ impl Display for Engine {
|
||||
mod test {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use hyperlog_core::log::{GraphItem, ItemState};
|
||||
use similar_asserts::assert_eq;
|
||||
|
||||
use crate::log::{GraphItem, ItemState};
|
||||
|
||||
use super::Engine;
|
||||
|
||||
#[test]
|
||||
@@ -249,7 +247,7 @@ mod test {
|
||||
.create(
|
||||
"kjuulh",
|
||||
&["some-section"],
|
||||
crate::log::GraphItem::Section(BTreeMap::default()),
|
||||
GraphItem::Section(BTreeMap::default()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -275,7 +273,7 @@ mod test {
|
||||
.create(
|
||||
"kjuulh",
|
||||
&["some-section"],
|
||||
crate::log::GraphItem::Section(BTreeMap::default()),
|
||||
GraphItem::Section(BTreeMap::default()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -283,7 +281,7 @@ mod test {
|
||||
.create(
|
||||
"kjuulh",
|
||||
&["some-section", "some-sub-section"],
|
||||
crate::log::GraphItem::Section(BTreeMap::default()),
|
||||
GraphItem::Section(BTreeMap::default()),
|
||||
)
|
||||
.unwrap();
|
||||
|
@@ -1,13 +1,16 @@
|
||||
#![feature(map_try_insert)]
|
||||
#![feature(fn_traits)]
|
||||
#![feature(let_chains)]
|
||||
|
||||
use std::{io::Stdout, time::Duration};
|
||||
use std::io::Stdout;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use app::{render_app, App};
|
||||
use commands::IntoCommand;
|
||||
use commands::{Dispatch, IntoCommand, Receiver};
|
||||
use components::graph_explorer::GraphExplorer;
|
||||
use crossterm::event::{self, Event, KeyCode};
|
||||
use hyperlog_core::state::State;
|
||||
use core_state::State;
|
||||
use crossterm::event::{Event, KeyCode, KeyEventKind};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use models::{EditMsg, Msg};
|
||||
use ratatui::{backend::CrosstermBackend, Terminal};
|
||||
|
||||
@@ -19,7 +22,16 @@ pub(crate) mod app;
|
||||
pub(crate) mod command_parser;
|
||||
pub(crate) mod commands;
|
||||
pub(crate) mod components;
|
||||
pub(crate) mod state;
|
||||
|
||||
pub mod commander;
|
||||
pub mod core_state;
|
||||
pub mod shared_engine;
|
||||
pub mod state;
|
||||
|
||||
mod engine;
|
||||
mod events;
|
||||
mod querier;
|
||||
mod storage;
|
||||
|
||||
mod logging;
|
||||
mod terminal;
|
||||
@@ -33,13 +45,13 @@ pub async fn execute(state: State) -> Result<()> {
|
||||
let state = SharedState::from(state);
|
||||
|
||||
let mut terminal = TerminalInstance::new()?;
|
||||
run(&mut terminal, state).context("app loop failed")?;
|
||||
run(&mut terminal, state).await.context("app loop failed")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
||||
let root = match state.querier.get_available_roots() {
|
||||
async fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) -> Result<()> {
|
||||
let root = match state.querier.get_available_roots_async().await? {
|
||||
// TODO: maybe present choose root screen
|
||||
Some(roots) => roots.first().cloned().unwrap(),
|
||||
None => {
|
||||
@@ -49,14 +61,25 @@ fn run(terminal: &mut Terminal<CrosstermBackend<Stdout>>, state: SharedState) ->
|
||||
};
|
||||
|
||||
let mut graph_explorer = GraphExplorer::new(root.clone(), state.clone());
|
||||
graph_explorer.update_graph()?;
|
||||
graph_explorer.update_graph().await?;
|
||||
|
||||
let mut app = App::new(&root, state.clone(), graph_explorer);
|
||||
let (dispatch, mut receiver) = commands::create_dispatch();
|
||||
let mut event_stream = crossterm::event::EventStream::new();
|
||||
|
||||
loop {
|
||||
terminal.draw(|f| render_app(f, &mut app))?;
|
||||
|
||||
if update(terminal, &mut app)?.should_quit() {
|
||||
if update(
|
||||
terminal,
|
||||
&mut app,
|
||||
&dispatch,
|
||||
&mut receiver,
|
||||
&mut event_stream,
|
||||
)
|
||||
.await?
|
||||
.should_quit()
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -75,12 +98,21 @@ impl UpdateConclusion {
|
||||
}
|
||||
}
|
||||
|
||||
fn update(
|
||||
async fn update<'a>(
|
||||
_terminal: &mut Terminal<CrosstermBackend<Stdout>>,
|
||||
app: &mut App,
|
||||
app: &mut App<'a>,
|
||||
dispatch: &Dispatch,
|
||||
receiver: &mut Receiver,
|
||||
event_stream: &mut crossterm::event::EventStream,
|
||||
) -> Result<UpdateConclusion> {
|
||||
if event::poll(Duration::from_millis(250)).context("event poll failed")? {
|
||||
if let Event::Key(key) = event::read().context("event read failed")? {
|
||||
let cross_event = event_stream.next().fuse();
|
||||
|
||||
let mut handle_key_event = |maybe_event| -> anyhow::Result<UpdateConclusion> {
|
||||
match maybe_event {
|
||||
Some(Ok(e)) => {
|
||||
if let Event::Key(key) = e
|
||||
&& key.kind == KeyEventKind::Press
|
||||
{
|
||||
let mut cmd = match &app.mode {
|
||||
app::Mode::View => match key.code {
|
||||
KeyCode::Enter => app.update(Msg::Interact)?,
|
||||
@@ -93,6 +125,11 @@ fn update(
|
||||
app.update(Msg::OpenCreateItemDialog)?;
|
||||
app.update(Msg::EnterInsertMode)?
|
||||
}
|
||||
KeyCode::Char('o') => {
|
||||
// TODO: batch commands
|
||||
app.update(Msg::OpenCreateItemDialogBelow)?;
|
||||
app.update(Msg::EnterInsertMode)?
|
||||
}
|
||||
KeyCode::Char('i') => app.update(Msg::EnterInsertMode)?,
|
||||
KeyCode::Char(':') => app.update(Msg::EnterCommandMode)?,
|
||||
_ => return Ok(UpdateConclusion(false)),
|
||||
@@ -112,7 +149,7 @@ fn update(
|
||||
};
|
||||
|
||||
loop {
|
||||
let msg = cmd.into_command().execute();
|
||||
let msg = cmd.into_command().execute(dispatch.clone());
|
||||
match msg {
|
||||
Some(msg) => {
|
||||
if let Msg::QuitApp = msg {
|
||||
@@ -126,6 +163,46 @@ fn update(
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
tracing::warn!("failed to send event: {}", e);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
Ok(UpdateConclusion(false))
|
||||
};
|
||||
|
||||
tokio::select! {
|
||||
maybe_event = cross_event => {
|
||||
let conclusion = handle_key_event(maybe_event)?;
|
||||
|
||||
return Ok(conclusion)
|
||||
},
|
||||
|
||||
msg = receiver.next() => {
|
||||
if let Some(msg) = msg {
|
||||
if let Msg::QuitApp = msg {
|
||||
return Ok(UpdateConclusion(true));
|
||||
}
|
||||
|
||||
let mut cmd = app.update(msg)?;
|
||||
|
||||
loop {
|
||||
let msg = cmd.into_command().execute(dispatch.clone());
|
||||
match msg {
|
||||
Some(msg) => {
|
||||
if let Msg::QuitApp = msg {
|
||||
return Ok(UpdateConclusion(true));
|
||||
}
|
||||
|
||||
cmd = app.update(msg)?;
|
||||
}
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(UpdateConclusion::new(false))
|
||||
}
|
||||
|
@@ -10,6 +10,7 @@ pub enum Msg {
|
||||
MoveUp,
|
||||
QuitApp,
|
||||
OpenCreateItemDialog,
|
||||
OpenCreateItemDialogBelow,
|
||||
OpenEditItemDialog { item: GraphItem },
|
||||
Interact,
|
||||
|
||||
@@ -20,11 +21,27 @@ pub enum Msg {
|
||||
SubmitCommand { command: String },
|
||||
|
||||
Edit(EditMsg),
|
||||
|
||||
GraphUpdated(IOEvent<GraphItem>),
|
||||
ItemCreated(IOEvent<()>),
|
||||
ItemUpdated(IOEvent<()>),
|
||||
SectionCreated(IOEvent<()>),
|
||||
ItemToggled(IOEvent<()>),
|
||||
|
||||
OpenUpdateItemDialog(IOEvent<()>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum IOEvent<T> {
|
||||
Initialized,
|
||||
Optimistic(T),
|
||||
Success(T),
|
||||
Failure(String),
|
||||
}
|
||||
|
||||
impl IntoCommand for Msg {
|
||||
fn into_command(self) -> crate::commands::Command {
|
||||
Command::new(|| Some(self))
|
||||
Command::new(|_| Some(self))
|
||||
}
|
||||
}
|
||||
|
||||
|
68
crates/hyperlog-tui/src/querier.rs
Normal file
68
crates/hyperlog-tui/src/querier.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use hyperlog_core::log::GraphItem;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
use crate::shared_engine::SharedEngine;
|
||||
|
||||
mod local;
|
||||
mod remote;
|
||||
|
||||
#[derive(Clone)]
|
||||
enum QuerierVariant {
|
||||
Local(local::Querier),
|
||||
Remote(remote::Querier),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Querier {
|
||||
variant: QuerierVariant,
|
||||
}
|
||||
|
||||
impl Querier {
|
||||
pub fn local(engine: &SharedEngine) -> Self {
|
||||
Self {
|
||||
variant: QuerierVariant::Local(local::Querier::new(engine)),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn remote(channel: Channel) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
variant: QuerierVariant::Remote(remote::Querier::new(channel).await?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
&self,
|
||||
root: &str,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> Option<GraphItem> {
|
||||
match &self.variant {
|
||||
QuerierVariant::Local(querier) => querier.get(root, path),
|
||||
QuerierVariant::Remote(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_async(
|
||||
&self,
|
||||
root: &str,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> anyhow::Result<Option<GraphItem>> {
|
||||
match &self.variant {
|
||||
QuerierVariant::Local(querier) => Ok(querier.get(root, path)),
|
||||
QuerierVariant::Remote(querier) => querier.get(root, path).await,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_roots(&self) -> Option<Vec<String>> {
|
||||
match &self.variant {
|
||||
QuerierVariant::Local(querier) => querier.get_available_roots(),
|
||||
QuerierVariant::Remote(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_available_roots_async(&self) -> anyhow::Result<Option<Vec<String>>> {
|
||||
match &self.variant {
|
||||
QuerierVariant::Local(querier) => Ok(querier.get_available_roots()),
|
||||
QuerierVariant::Remote(querier) => querier.get_available_roots().await,
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,12 +1,17 @@
|
||||
use crate::{log::GraphItem, shared_engine::SharedEngine};
|
||||
use hyperlog_core::log::GraphItem;
|
||||
|
||||
use crate::shared_engine::SharedEngine;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Querier {
|
||||
engine: SharedEngine,
|
||||
}
|
||||
|
||||
impl Querier {
|
||||
pub fn new(engine: SharedEngine) -> Self {
|
||||
Self { engine }
|
||||
pub fn new(engine: &SharedEngine) -> Self {
|
||||
Self {
|
||||
engine: engine.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_available_roots(&self) -> Option<Vec<String>> {
|
||||
@@ -31,7 +36,10 @@ impl Querier {
|
||||
path.len()
|
||||
);
|
||||
|
||||
self.engine
|
||||
.get(root, &path.iter().map(|i| i.as_str()).collect::<Vec<_>>())
|
||||
let item = self
|
||||
.engine
|
||||
.get(root, &path.iter().map(|i| i.as_str()).collect::<Vec<_>>());
|
||||
|
||||
item
|
||||
}
|
||||
}
|
118
crates/hyperlog-tui/src/querier/remote.rs
Normal file
118
crates/hyperlog-tui/src/querier/remote.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use hyperlog_core::log::GraphItem;
|
||||
use hyperlog_protos::hyperlog::{
|
||||
graph_client::GraphClient, graph_item::Contents, GetAvailableRootsRequest, GetRequest,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use tonic::transport::Channel;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Clone)]
|
||||
pub struct Querier {
|
||||
channel: Channel,
|
||||
}
|
||||
|
||||
#[allow(dead_code, unused_variables)]
|
||||
impl Querier {
|
||||
pub async fn new(channel: Channel) -> anyhow::Result<Self> {
|
||||
Ok(Self { channel })
|
||||
}
|
||||
|
||||
pub async fn get_available_roots(&self) -> anyhow::Result<Option<Vec<String>>> {
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(GetAvailableRootsRequest {});
|
||||
let response = client.get_available_roots(request).await?;
|
||||
|
||||
let roots = response.into_inner();
|
||||
|
||||
if roots.roots.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(roots.roots))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
&self,
|
||||
root: &str,
|
||||
path: impl IntoIterator<Item = impl Into<String>>,
|
||||
) -> anyhow::Result<Option<GraphItem>> {
|
||||
let paths = path.into_iter().map(|i| i.into()).collect_vec();
|
||||
|
||||
tracing::debug!(
|
||||
"quering: root:({}), path:({}), len: ({}))",
|
||||
root,
|
||||
paths.join("."),
|
||||
paths.len()
|
||||
);
|
||||
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(GetRequest {
|
||||
root: root.into(),
|
||||
paths,
|
||||
});
|
||||
|
||||
let response = client.get(request).await?;
|
||||
|
||||
let graph_item = response.into_inner();
|
||||
|
||||
if let Some(item) = graph_item.item {
|
||||
let local_graph = transform_proto_to_local(&item);
|
||||
Ok(local_graph)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_proto_to_local(input: &hyperlog_protos::hyperlog::GraphItem) -> Option<GraphItem> {
|
||||
match &input.contents {
|
||||
Some(item) => match item {
|
||||
Contents::User(user) => {
|
||||
let mut items = BTreeMap::new();
|
||||
|
||||
for (key, value) in &user.items {
|
||||
if let Some(item) = transform_proto_to_local(value) {
|
||||
items.insert(key.clone(), item);
|
||||
}
|
||||
}
|
||||
|
||||
Some(GraphItem::User(items))
|
||||
}
|
||||
Contents::Section(section) => {
|
||||
let mut items = BTreeMap::new();
|
||||
|
||||
for (key, value) in §ion.items {
|
||||
if let Some(item) = transform_proto_to_local(value) {
|
||||
items.insert(key.clone(), item);
|
||||
}
|
||||
}
|
||||
|
||||
Some(GraphItem::Section(items))
|
||||
}
|
||||
Contents::Item(item) => Some(GraphItem::Item {
|
||||
title: item.title.clone(),
|
||||
description: item.description.clone(),
|
||||
state: match &item.item_state {
|
||||
Some(state) => match state {
|
||||
hyperlog_protos::hyperlog::item_graph_item::ItemState::NotDone(_) => {
|
||||
hyperlog_core::log::ItemState::NotDone
|
||||
}
|
||||
hyperlog_protos::hyperlog::item_graph_item::ItemState::Done(_) => {
|
||||
hyperlog_core::log::ItemState::Done
|
||||
}
|
||||
},
|
||||
None => hyperlog_core::log::ItemState::NotDone,
|
||||
},
|
||||
}),
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::{engine::Engine, log::GraphItem};
|
||||
use hyperlog_core::log::GraphItem;
|
||||
|
||||
use crate::engine::Engine;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedEngine {
|
@@ -1,6 +1,6 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use hyperlog_core::state::State;
|
||||
use crate::core_state::State;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedState {
|
||||
|
@@ -149,10 +149,9 @@ impl Storage {
|
||||
mod test {
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use hyperlog_core::log::GraphItem;
|
||||
use similar_asserts::assert_eq;
|
||||
|
||||
use crate::log::GraphItem;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
@@ -7,6 +7,7 @@ repository = "https://git.front.kjuulh.io/kjuulh/hyperlog"
|
||||
[dependencies]
|
||||
hyperlog-core.workspace = true
|
||||
hyperlog-tui.workspace = true
|
||||
hyperlog-server = { workspace = true, optional = true }
|
||||
|
||||
anyhow.workspace = true
|
||||
tokio.workspace = true
|
||||
@@ -15,21 +16,17 @@ tracing-subscriber.workspace = true
|
||||
clap.workspace = true
|
||||
dotenv.workspace = true
|
||||
axum.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
uuid.workspace = true
|
||||
|
||||
serde = { version = "1.0.201", features = ["derive"] }
|
||||
sqlx = { version = "0.7.4", features = [
|
||||
"runtime-tokio",
|
||||
"tls-rustls",
|
||||
"postgres",
|
||||
"uuid",
|
||||
"time",
|
||||
] }
|
||||
uuid = { version = "1.8.0", features = ["v4"] }
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
bus = "2.4.1"
|
||||
dirs = "5.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
similar-asserts = "1.5.0"
|
||||
tempfile = "3.10.1"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
include_server = ["dep:hyperlog-server"]
|
||||
|
@@ -1 +0,0 @@
|
||||
-- Add migration script here
|
@@ -1,22 +1,44 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use hyperlog_core::{commander, state};
|
||||
|
||||
use crate::server::serve;
|
||||
use clap::{Parser, Subcommand, ValueEnum};
|
||||
use hyperlog_tui::{
|
||||
commander,
|
||||
core_state::{Backend, State},
|
||||
};
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Command {
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
|
||||
#[arg(long, default_value = "local")]
|
||||
backend: BackendArg,
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Clone)]
|
||||
enum BackendArg {
|
||||
Local,
|
||||
Remote,
|
||||
}
|
||||
|
||||
impl From<BackendArg> for Backend {
|
||||
fn from(value: BackendArg) -> Self {
|
||||
match value {
|
||||
BackendArg::Local => Backend::Local,
|
||||
BackendArg::Remote => Backend::Remote,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
#[cfg(feature = "include_server")]
|
||||
Serve {
|
||||
#[arg(env = "SERVICE_HOST", long, default_value = "127.0.0.1:3000")]
|
||||
host: SocketAddr,
|
||||
#[arg(env = "EXTERNAL_HOST", long, default_value = "127.0.0.1:3000")]
|
||||
external_host: std::net::SocketAddr,
|
||||
#[arg(env = "INTERNAL_HOST", long, default_value = "127.0.0.1:3001")]
|
||||
internal_host: std::net::SocketAddr,
|
||||
#[arg(env = "EXTERNAL_GRPC_HOST", long, default_value = "127.0.0.1:4000")]
|
||||
external_grpc_host: std::net::SocketAddr,
|
||||
},
|
||||
Exec {
|
||||
#[command(subcommand)]
|
||||
@@ -70,20 +92,37 @@ pub async fn execute() -> anyhow::Result<()> {
|
||||
tracing_subscriber::fmt::init();
|
||||
}
|
||||
|
||||
let state = state::State::new()?;
|
||||
let backend = cli.backend;
|
||||
|
||||
match cli.command {
|
||||
Some(Commands::Serve { host }) => {
|
||||
#[cfg(feature = "include_server")]
|
||||
Some(Commands::Serve {
|
||||
external_host,
|
||||
internal_host,
|
||||
external_grpc_host,
|
||||
}) => {
|
||||
tracing::info!("Starting service");
|
||||
|
||||
serve(host).await?;
|
||||
hyperlog_server::serve(hyperlog_server::ServeOptions {
|
||||
external_http: external_host,
|
||||
internal_http: internal_host,
|
||||
external_grpc: external_grpc_host,
|
||||
})
|
||||
.await?;
|
||||
}
|
||||
Some(Commands::Exec { commands }) => match commands {
|
||||
ExecCommands::CreateRoot { root } => state
|
||||
Some(Commands::Exec { commands }) => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
match commands {
|
||||
ExecCommands::CreateRoot { root } => {
|
||||
state
|
||||
.commander
|
||||
.execute(commander::Command::CreateRoot { root })?,
|
||||
.execute(commander::Command::CreateRoot { root })
|
||||
.await?
|
||||
}
|
||||
ExecCommands::CreateSection { root, path } => {
|
||||
state.commander.execute(commander::Command::CreateSection {
|
||||
state
|
||||
.commander
|
||||
.execute(commander::Command::CreateSection {
|
||||
root,
|
||||
path: path
|
||||
.unwrap_or_default()
|
||||
@@ -91,10 +130,14 @@ pub async fn execute() -> anyhow::Result<()> {
|
||||
.map(|s| s.to_string())
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect::<Vec<String>>(),
|
||||
})?
|
||||
})
|
||||
.await?
|
||||
}
|
||||
},
|
||||
Some(Commands::Query { commands }) => match commands {
|
||||
}
|
||||
}
|
||||
Some(Commands::Query { commands }) => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
match commands {
|
||||
QueryCommands::Get { root, path } => {
|
||||
let res = state.querier.get(
|
||||
&root,
|
||||
@@ -107,21 +150,27 @@ pub async fn execute() -> anyhow::Result<()> {
|
||||
|
||||
println!("{}", output);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(Commands::CreateRoot { name }) => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
state
|
||||
.commander
|
||||
.execute(commander::Command::CreateRoot { root: name })?;
|
||||
.execute(commander::Command::CreateRoot { root: name })
|
||||
.await?;
|
||||
println!("Root was successfully created, now run:\n\n$ hyperlog");
|
||||
}
|
||||
Some(Commands::Info {}) => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
println!("graph stored at: {}", state.storage.info()?)
|
||||
}
|
||||
Some(Commands::ClearLock {}) => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
state.storage.clear_lock_file();
|
||||
println!("cleared lock file");
|
||||
}
|
||||
None => {
|
||||
let state = State::new(backend.into()).await?;
|
||||
hyperlog_tui::execute(state).await?;
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,4 @@
|
||||
mod cli;
|
||||
pub(crate) mod server;
|
||||
pub(crate) mod state;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
|
@@ -1,42 +1 @@
|
||||
use std::{net::SocketAddr, sync::Arc};
|
||||
|
||||
use axum::{extract::MatchedPath, http::Request, routing::get, Router};
|
||||
use tower_http::trace::TraceLayer;
|
||||
|
||||
use crate::state::{SharedState, State};
|
||||
|
||||
async fn root() -> &'static str {
|
||||
"Hello, hyperlog!"
|
||||
}
|
||||
|
||||
pub async fn serve(host: SocketAddr) -> anyhow::Result<()> {
|
||||
let state = SharedState(Arc::new(State::new().await?));
|
||||
|
||||
let app = Router::new()
|
||||
.route("/", get(root))
|
||||
.with_state(state.clone())
|
||||
.layer(
|
||||
TraceLayer::new_for_http().make_span_with(|request: &Request<_>| {
|
||||
// Log the matched route's path (with placeholders not filled in).
|
||||
// Use request.uri() or OriginalUri if you want the real path.
|
||||
let matched_path = request
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(MatchedPath::as_str);
|
||||
|
||||
tracing::info_span!(
|
||||
"http_request",
|
||||
method = ?request.method(),
|
||||
matched_path,
|
||||
some_other_field = tracing::field::Empty,
|
||||
)
|
||||
}), // ...
|
||||
);
|
||||
|
||||
tracing::info!("listening on {}", host);
|
||||
let listener = tokio::net::TcpListener::bind(host).await.unwrap();
|
||||
axum::serve(listener, app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
@@ -1,37 +1 @@
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
use anyhow::Context;
|
||||
use sqlx::{Pool, Postgres};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedState(pub Arc<State>);
|
||||
|
||||
impl Deref for SharedState {
|
||||
type Target = Arc<State>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub _db: Pool<Postgres>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub async fn new() -> anyhow::Result<Self> {
|
||||
let db = sqlx::PgPool::connect(
|
||||
&std::env::var("DATABASE_URL").context("DATABASE_URL is not set")?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
sqlx::migrate!("migrations/crdb")
|
||||
.set_locking(false)
|
||||
.run(&db)
|
||||
.await?;
|
||||
|
||||
let _ = sqlx::query("SELECT 1;").fetch_one(&db).await?;
|
||||
|
||||
Ok(Self { _db: db })
|
||||
}
|
||||
}
|
||||
|
@@ -15,3 +15,9 @@ please:
|
||||
api_url: https://git.front.kjuulh.io
|
||||
actions:
|
||||
rust:
|
||||
|
||||
scripts:
|
||||
dev:
|
||||
type: shell
|
||||
install:
|
||||
type: shell
|
||||
|
484
demo.cast
484
demo.cast
@@ -1,187 +1,297 @@
|
||||
{"version": 2, "width": 121, "height": 31, "timestamp": 1715413204, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
|
||||
[0.28385, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[0.356577, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0m\u001b[1;31mrs \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[0.35759, "o", "\u001b[6 q"]
|
||||
[0.358564, "o", "\u001b[6 q"]
|
||||
[0.358794, "o", "\u001b[?2004h"]
|
||||
[1.196152, "o", "c"]
|
||||
[1.200489, "o", "\b\u001b[32mc\u001b[39m"]
|
||||
[1.241408, "o", "\b\u001b[32mc\u001b[39m\u001b[90mlear\u001b[39m\b\b\b\b"]
|
||||
[1.340197, "o", "\b\u001b[32mc\u001b[32ma\u001b[39m\u001b[39m \u001b[39m \u001b[39m \b\b\b"]
|
||||
[1.369897, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"]
|
||||
[1.37249, "o", "\u001b[90mrgo run\u001b[39m\b\b\b\b\b\b\b"]
|
||||
[1.424331, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"]
|
||||
[1.592372, "o", "\b\u001b[1m\u001b[31mr\u001b[1m\u001b[31mg\u001b[0m\u001b[39m"]
|
||||
[1.666639, "o", "\b\u001b[1m\u001b[31mg\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[1.671179, "o", "\b\b\b\b\b\u001b[0m\u001b[32mc\u001b[0m\u001b[32ma\u001b[0m\u001b[32mr\u001b[0m\u001b[32mg\u001b[0m\u001b[32mo\u001b[39m"]
|
||||
[2.080073, "o", "\b\u001b[32mo\u001b[32m \u001b[39m"]
|
||||
[2.084222, "o", "\b\b\u001b[32mo\u001b[39m\u001b[39m "]
|
||||
[2.246919, "o", "\u001b[39mr"]
|
||||
[2.252663, "o", "\b\u001b[4mr\u001b[24m"]
|
||||
[2.347417, "o", "\b\u001b[4mr\u001b[39m\u001b[4mu\u001b[24m"]
|
||||
[2.351929, "o", "\b\b\u001b[24mr\u001b[24mu"]
|
||||
[2.501908, "o", "\u001b[39mn"]
|
||||
[2.800437, "o", "\u001b[?1l\u001b>"]
|
||||
[2.800875, "o", "\u001b[?2004l"]
|
||||
[2.807783, "o", "\u001b[0 q"]
|
||||
[2.808107, "o", "\r\r\n"]
|
||||
[3.35867, "o", "\u001b[1m\u001b[32m Finished\u001b[0m `dev` profile [unoptimized + debuginfo] target(s) in 0.49s\r\n"]
|
||||
[3.365514, "o", "\u001b[1m\u001b[32m Running\u001b[0m `target/debug/hyperlog`\r\n"]
|
||||
[3.740156, "o", "\u001b[?1049h"]
|
||||
[3.743543, "o", "\u001b[1;1H\u001b[38;5;2mhyperlog\u001b[2;1H\u001b[39m─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[3;1Hsomething\u001b[3;11H~\u001b[3;13H(items:\u001b[3;21H3)\u001b[4;5Ha\u001b[4;7H~\u001b[4;9H(items:\u001b[4;17H2)\u001b[5;5H\u001b[38;5;8m...\u001b[6;5H\u001b[39mc\u001b[6;7H~\u001b[6;9H(items:\u001b[6;17H0)\u001b[31;2H--\u001b[31;5HVIEW\u001b[31;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.001932, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.260086, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.458203, "o", "\u001b[3;1H\u001b[38;2;255;165;0msomething ~ (items: 3)\u001b[5;5H\u001b[39m \u001b[5;9H[\u001b[5;11H]\u001b[5;13Hitem\u001b[6;5H \u001b[6;7H \u001b[6;9H[ ] something\u001b[7;5Hb\u001b[7;7H~\u001b[7;9H(items:\u001b[7;17H1)\u001b[8;9H[\u001b[8;11H]\u001b[8;13Hitem\u001b[9;5Hc\u001b[9;7H~\u001b[9;9H(items:\u001b[9;17H0)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.716099, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.974526, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.235168, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.471231, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.670365, "o", "\u001b[31;2Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.776552, "o", "\u001b[31;3Hh\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.907952, "o", "\u001b[31;4Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.035263, "o", "\u001b[31;5Ht\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.187704, "o", "\u001b[31;6H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.299824, "o", "\u001b[31;7Ha\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.438169, "o", "\u001b[31;8Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.577971, "o", "\u001b[31;9Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.836633, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.968556, "o", "\u001b[31;9H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.106562, "o", "\u001b[31;8H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.251411, "o", "\u001b[31;7H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.395354, "o", "\u001b[31;6H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.54286, "o", "\u001b[31;5H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.731859, "o", "\u001b[31;5Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.989235, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.121554, "o", "\u001b[31;6H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.299653, "o", "\u001b[31;7Ha\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.399794, "o", "\u001b[31;8Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.549891, "o", "\u001b[31;9Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.785214, "o", "\u001b[9;17H5\u001b[10;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[10;13Hitem\u001b[11;9H\u001b[38;5;8m...\u001b[12;9H\u001b[39m[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[12;13Hitem-d\u001b[31;1H -- VIEW --\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.044234, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.303396, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.554819, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.813405, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.073436, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.077833, "o", "\u001b[3;1Hsomething ~ (items: 3)\u001b[5;5H\u001b[38;5;8m...\u001b[5;9H\u001b[39m \u001b[5;11H \u001b[5;13H \u001b[6;5Hc\u001b[6;7H~\u001b[6;9H(items: 5) \u001b[7;5H \u001b[7;7H \u001b[7;9H \u001b[7;17H \u001b[8;9H \u001b[8;11H \u001b[8;13H \u001b[9;5H \u001b[9;7H \u001b[9;9H \u001b[9;17H \u001b[10;9H \u001b[10;13H \u001b[11;9H \u001b[12;9H \u001b[12;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.33073, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.590022, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.750514, "o", "\u001b[3;1H\u001b[38;2;255;165;0msomething ~ (items: 3)\u001b[5;5H\u001b[39m \u001b[5;9H[\u001b[5;11H]\u001b[5;13Hitem\u001b[6;5H \u001b[6;7H \u001b[6;9H[ ] something\u001b[7;5Hb\u001b[7;7H~\u001b[7;9H(items:\u001b[7;17H1)\u001b[8;9H[\u001b[8;11H]\u001b[8;13Hitem\u001b[9;5Hc\u001b[9;7H~\u001b[9;9H(items:\u001b[9;17H5)\u001b[10;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[10;13Hitem\u001b[11;9H\u001b[38;5;8m...\u001b[12;9H\u001b[39m[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[12;13Hitem-d\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.009542, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.101987, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.310184, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.569878, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.646194, "o", "\u001b[3;1Hsomething ~ (items: 3)\u001b[4;5H\u001b[38;2;255;165;0ma ~ (items: 2)\u001b[7;5H\u001b[39m \u001b[7;7H \u001b[7;9H \u001b[7;17H \u001b[8;5Hb\u001b[8;7H~\u001b[8;9H(items: 1)\u001b[9;5H \u001b[9;7H \u001b[9;9H[ ] item \u001b[10;5Hc\u001b[10;7H~\u001b[10;9H(items: 5)\u001b[11;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[11;13Hitem\u001b[12;9H\u001b[38;5;8m...\u001b[12;13H\u001b[39m \u001b[13;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[13;13Hitem-d\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.905614, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.9512, "o", "\u001b[4;5Ha ~ (items: 2)\u001b[7;5H\u001b[38;2;255;165;0mb ~ (items: 1)\u001b[8;5H\u001b[39m \u001b[8;7H \u001b[8;9H[ ] item \u001b[9;9H \u001b[9;11H \u001b[9;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.171517, "o", "\u001b[7;5Hb ~ (items: 1)\u001b[9;5H\u001b[38;2;255;165;0mc ~ (items: 5)\u001b[10;5H\u001b[39m \u001b[10;7H \u001b[10;9H[\u001b[38;2;127;255;0mx\u001b[39m] item \u001b[11;17H-a\u001b[12;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[12;13Hitem-b\u001b[13;18Hc\u001b[14;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[14;13Hitem-d\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.428831, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.687811, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.710816, "o", "\u001b[4;5Hc\u001b[4;17H5\u001b[5;9H\u001b[38;2;255;165;0m[x] item\u001b[6;10H\u001b[38;2;127;255;0mx\u001b[6;13H\u001b[39mitem-a \u001b[7;5H \u001b[7;7H \u001b[7;9H[\u001b[38;2;127;255;0mx\u001b[39m] item-b\u001b[8;10H\u001b[38;2;127;255;0mx\u001b[8;17H\u001b[39m-c\u001b[9;5H [\u001b[38;2;127;255;0mx\u001b[39m] item-d\u001b[10;9H \u001b[10;13H \u001b[11;9H \u001b[11;13H \u001b[12;9H \u001b[12;13H \u001b[13;9H \u001b[13;13H \u001b[14;9H \u001b[14;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.970938, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.229238, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.402499, "o", "\u001b[5;10H\u001b[38;2;255;165;0m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.661623, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.709679, "o", "\u001b[5;9H[ ] item\u001b[6;9H\u001b[38;2;255;165;0m[x] item-a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.969177, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.980927, "o", "\u001b[6;10H\u001b[38;2;255;165;0m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[14.240333, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[14.453095, "o", "\u001b[4;5Ha\u001b[4;17H2\u001b[6;9H[ ] something\u001b[7;5Hb\u001b[7;7H~\u001b[7;9H(items: 1)\u001b[8;10H \u001b[8;17H \u001b[9;5H\u001b[38;2;255;165;0mc ~ (items: 5)\u001b[10;9H\u001b[39m[\u001b[10;11H]\u001b[10;13Hitem\u001b[11;9H[\u001b[11;11H]\u001b[11;13Hitem-a\u001b[12;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[12;13Hitem-b\u001b[13;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[13;13Hitem-c\u001b[14;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[14;13Hitem-d\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[14.638745, "o", "\u001b[3;1H\u001b[38;2;255;165;0msomething ~ (items: 3)\u001b[9;5H\u001b[39mc ~ (items: 5)\u001b[11;9H\u001b[38;5;8m...\u001b[11;13H\u001b[39m \u001b[12;18Hd\u001b[13;9H \u001b[13;13H \u001b[14;9H \u001b[14;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[14.897952, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.0327, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.291396, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.549911, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.658184, "o", "\u001b[31;2Hh\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.809793, "o", "\u001b[31;3Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[15.949529, "o", "\u001b[31;4Hd\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.096886, "o", "\u001b[31;5He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.254773, "o", "\u001b[31;6H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.470747, "o", "\u001b[31;7Hd\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.551481, "o", "\u001b[31;8Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.709128, "o", "\u001b[31;9Hn\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[16.827375, "o", "\u001b[31;10He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.005258, "o", "\u001b[9;17H2\u001b[11;9H[ ]\u001b[11;13Hitem-a\u001b[12;9H \u001b[12;13H \u001b[31;1H -- VIEW --\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.263877, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.523133, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.570586, "o", "\u001b[3;1Hsomething ~ (items: 3)\u001b[4;5H\u001b[38;2;255;165;0ma ~ (items: 2)\u001b[7;5H\u001b[39m \u001b[7;7H \u001b[7;9H \u001b[7;17H \u001b[8;5Hb\u001b[8;7H~\u001b[8;9H(items: 1)\u001b[9;5H \u001b[9;7H \u001b[9;9H[ ] item \u001b[10;5Hc\u001b[10;7H~\u001b[10;9H(items: 2)\u001b[11;17H \u001b[12;9H[\u001b[12;11H]\u001b[12;13Hitem-a\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.829292, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.986953, "o", "\u001b[4;5Ha ~ (items: 2)\u001b[7;5H\u001b[38;2;255;165;0mb ~ (items: 1)\u001b[8;5H\u001b[39m \u001b[8;7H \u001b[8;9H[ ] item \u001b[9;9H \u001b[9;11H \u001b[9;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.191003, "o", "\u001b[7;5Hb ~ (items: 1)\u001b[9;5H\u001b[38;2;255;165;0mc ~ (items: 2)\u001b[10;5H\u001b[39m \u001b[10;7H \u001b[10;9H[ ] item \u001b[11;17H-a\u001b[12;9H \u001b[12;11H \u001b[12;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.44961, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.709575, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.968623, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.193329, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.45148, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.45588, "o", "\u001b[31;2Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.506083, "o", "\u001b[31;3Hh\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.767374, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.772362, "o", "\u001b[31;4Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.031712, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.156021, "o", "\u001b[31;4H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.414819, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.422959, "o", "\u001b[31;4Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.564511, "o", "\u001b[31;5Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.686556, "o", "\u001b[31;6H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.891174, "o", "\u001b[31;7Ha\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.014086, "o", "\u001b[31;8Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.168709, "o", "\u001b[31;9Hl\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.427999, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.436176, "o", "\u001b[9;17H\u001b[38;2;255;165;0m5\u001b[12;9H\u001b[39m[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[12;13Hitem-b\u001b[13;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[13;13Hitem-c\u001b[14;9H[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[14;13Hitem-d\u001b[31;1H -- VIEW --\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.695984, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.957095, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.04995, "o", "\u001b[7;5H\u001b[38;2;255;165;0mb ~ (items: 1)\u001b[9;5H\u001b[39m \u001b[10;5Hc\u001b[10;7H~\u001b[10;9H(items: 5)\u001b[11;17H \u001b[12;9H\u001b[38;5;8m...\u001b[12;13H\u001b[39m \u001b[13;18Hd\u001b[14;9H \u001b[14;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.311034, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.568875, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.795873, "o", "\u001b[4;5Hb\u001b[4;17H1\u001b[5;9H\u001b[38;2;255;165;0m[ ] item\u001b[6;9H\u001b[39m \u001b[6;11H \u001b[6;13H \u001b[7;5H \u001b[8;9H \u001b[8;11H \u001b[8;13H \u001b[10;5H \u001b[10;7H \u001b[10;9H \u001b[10;17H \u001b[11;9H \u001b[11;11H \u001b[11;13H \u001b[12;9H \u001b[13;9H \u001b[13;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.054478, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.24198, "o", "\u001b[5;10H\u001b[38;2;255;165;0mx\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.500408, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.646615, "o", "\u001b[4;5Ha\u001b[4;17H2\u001b[5;9H[ ] item\u001b[6;9H[\u001b[6;11H]\u001b[6;13Hsomething\u001b[7;5H\u001b[38;2;255;165;0mb ~ (items: 1)\u001b[8;9H\u001b[39m[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[8;13Hitem\u001b[10;5Hc\u001b[10;7H~\u001b[10;9H(items:\u001b[10;17H5)\u001b[11;9H[\u001b[11;11H]\u001b[11;13Hitem\u001b[12;9H\u001b[38;5;8m...\u001b[13;9H\u001b[39m[\u001b[38;2;127;255;0mx\u001b[39m]\u001b[13;13Hitem-d\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.90405, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.162272, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.335133, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.590424, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.825885, "o", "\u001b[31;2Hh\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.994253, "o", "\u001b[31;3Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.102804, "o", "\u001b[31;4Hd\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.2455, "o", "\u001b[31;5He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.327672, "o", "\u001b[31;6H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.47797, "o", "\u001b[31;7Hd\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.603255, "o", "\u001b[31;8Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.749521, "o", "\u001b[31;9Hn\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.862533, "o", "\u001b[31;10He\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.026357, "o", "\u001b[7;17H\u001b[38;2;255;165;0m0\u001b[8;5H\u001b[39mc\u001b[8;7H~\u001b[8;9H(items: 2)\u001b[9;9H[\u001b[9;11H]\u001b[9;13Hitem\u001b[10;5H \u001b[10;7H \u001b[10;9H[ ] item-a\u001b[11;9H \u001b[11;11H \u001b[11;13H \u001b[12;9H \u001b[13;9H \u001b[13;13H \u001b[31;1H -- VIEW --\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.285752, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.546241, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.80384, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.061451, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.318053, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.435387, "o", "\u001b[31;1H: \u001b[31;5H \u001b[31;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.678991, "o", "\u001b[31;2Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.801458, "o", "\u001b[?1049l\u001b[?25h"]
|
||||
[27.803865, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[27.863445, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0m\u001b[1;31mrs \u001b[0m\u001b[33m25s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[27.864508, "o", "\u001b[6 q"]
|
||||
[27.865397, "o", "\u001b[6 q"]
|
||||
[27.865567, "o", "\u001b[?2004h"]
|
||||
[28.397871, "o", "c"]
|
||||
[28.402538, "o", "\b\u001b[32mc\u001b[39m"]
|
||||
[28.440037, "o", "\b\u001b[32mc\u001b[39m\u001b[90margo run\u001b[39m\u001b[8D"]
|
||||
[28.478861, "o", "\b\u001b[32mc\u001b[32ml\u001b[39m\u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \u001b[39m \b\b\b\b\b\b\b"]
|
||||
[28.484213, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ml\u001b[0m\u001b[39m"]
|
||||
[28.505375, "o", "\u001b[90mear\u001b[39m\b\b\b"]
|
||||
[28.619796, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ml\u001b[1m\u001b[31me\u001b[0m\u001b[39m"]
|
||||
[28.674043, "o", "\b\u001b[1m\u001b[31me\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"]
|
||||
[28.762176, "o", "\b\u001b[1m\u001b[31ma\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"]
|
||||
[28.764782, "o", "\b\b\b\b\b\u001b[0m\u001b[32mc\u001b[0m\u001b[32ml\u001b[0m\u001b[32me\u001b[0m\u001b[32ma\u001b[0m\u001b[32mr\u001b[39m"]
|
||||
[28.816778, "o", "\u001b[?1l\u001b>"]
|
||||
[28.817025, "o", "\u001b[?2004l"]
|
||||
[28.821673, "o", "\u001b[0 q"]
|
||||
[28.82202, "o", "\r\r\n"]
|
||||
[28.866023, "o", "\u001b[3J\u001b[H\u001b[2J"]
|
||||
[28.866306, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[28.910374, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0m\u001b[1;31mrs \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[28.911365, "o", "\u001b[6 q"]
|
||||
[28.912156, "o", "\u001b[6 q"]
|
||||
[28.91233, "o", "\u001b[?2004h"]
|
||||
[29.510648, "o", "\u001b[?2004l\r\r\n"]
|
||||
{"version": 2, "width": 123, "height": 33, "timestamp": 1715717726, "env": {"SHELL": "/bin/zsh", "TERM": "xterm-256color"}}
|
||||
[0.289876, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[0.363463, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[0.364423, "o", "\u001b[6 q"]
|
||||
[0.365312, "o", "\u001b[6 q"]
|
||||
[0.365501, "o", "\u001b[?2004h"]
|
||||
[1.080115, "o", "c"]
|
||||
[1.082107, "o", "\b\u001b[32mc\u001b[39m"]
|
||||
[1.101402, "o", "\b\u001b[32mc\u001b[39m\u001b[90margo run --release -- --backend remote\u001b[39m\u001b[38D"]
|
||||
[1.169782, "o", "\b\u001b[32mc\u001b[32ma\u001b[39m"]
|
||||
[1.189287, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[0m\u001b[39m"]
|
||||
[1.249073, "o", "\b\b\u001b[1m\u001b[31mc\u001b[1m\u001b[31ma\u001b[1m\u001b[31mr\u001b[0m\u001b[39m"]
|
||||
[1.43174, "o", "\b\u001b[1m\u001b[31mr\u001b[1m\u001b[31mg\u001b[0m\u001b[39m"]
|
||||
[1.578255, "o", "\b\u001b[1m\u001b[31mg\u001b[1m\u001b[31mo\u001b[0m\u001b[39m"]
|
||||
[1.580674, "o", "\b\b\b\b\b\u001b[0m\u001b[32mc\u001b[0m\u001b[32ma\u001b[0m\u001b[32mr\u001b[0m\u001b[32mg\u001b[0m\u001b[32mo\u001b[39m"]
|
||||
[2.050954, "o", "\u001b[39m \u001b[39mr\u001b[39mu\u001b[39mn\u001b[39m \u001b[39m-\u001b[39m-\u001b[39mr\u001b[39me\u001b[39ml\u001b[39me\u001b[39ma\u001b[39ms\u001b[39me\u001b[39m \u001b[39m-\u001b[39m-\u001b[39m \u001b[39m-\u001b[39m-\u001b[39mb\u001b[39ma\u001b[39mc\u001b[39mk\u001b[39me\u001b[39mn\u001b[39md\u001b[39m \u001b[39mr\u001b[39me\u001b[39mm\u001b[39mo\u001b[39mt\u001b[39me"]
|
||||
[2.553848, "o", "\u001b[?1l\u001b>"]
|
||||
[2.554218, "o", "\u001b[?2004l"]
|
||||
[2.56397, "o", "\u001b[0 q"]
|
||||
[2.564341, "o", "\r\r\n"]
|
||||
[2.891881, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `std::net::SocketAddr`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog/src/cli.rs:1:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::net::SocketAddr;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\r\n\r\n"]
|
||||
[2.892626, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `GraphItem`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:1:26\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::{GraphItem, ItemState};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\r\n\r\n"]
|
||||
[2.892638, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `hyperlog_core::log::GraphItem`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_section.rs:1:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::GraphItem;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\r\n\r\n"]
|
||||
[2.892645, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused imports: `GraphItem`, `ItemState`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:1:26\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::{GraphItem, ItemState};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n"]
|
||||
[2.892769, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `sqlx::types::Json`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:2:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m2\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse sqlx::types::Json;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:102:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m102\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variab"]
|
||||
[2.892853, "o", "le: `path`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:103:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m103\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m path,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `path: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `title`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:104:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m104\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m title,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `title: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `description`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:105:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;"]
|
||||
[2.89292, "o", "12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m105\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m description,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `description: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `state`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:106:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m106\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m state,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `state: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:108:35\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m108\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::ToggleItem { root, p"]
|
||||
[2.892978, "o", "ath } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `path`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:108:41\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m108\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::ToggleItem { root, path } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `path: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:29\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m "]
|
||||
[2.893044, "o", "\u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `src`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:35\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `src: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dest`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:40\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m "]
|
||||
[2.89314, "o", " \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `dest: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `req`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:26:33\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m26\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_req`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `root_name` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:31:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m29\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Root {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[3"]
|
||||
[2.893227, "o", "8;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m30\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m31\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root_name: String,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `id` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:36:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m35\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Section {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m-------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m36\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0"]
|
||||
[2.893323, "o", "m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `root_name` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_section.rs:19:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Root {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m18\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m19\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root_name: String,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `id` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_graph.rs:37:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m36\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Node {\u001b[0m\r\n\u001b[0m \u001b"]
|
||||
[2.893379, "o", "[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m37\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Node` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: multiple associated items are never used\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_graph.rs:152:16\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m151\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m impl Engine {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m-----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12massociated items in this implementation\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m152\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b"]
|
||||
[2.893458, "o", "[0m pub fn engine_from_str(input: &str) -> anyhow::Result<Self> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m158\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn to_str(&self) -> anyhow::Result<String> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m219\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn get_mut(&mut self, root: &str, path: &[&str]) -> Option<&mut GraphItem> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m225\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn take(&mut self, root: &str, path: &[&str]) -> Option<GraphItem> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m231\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m"]
|
||||
[2.893531, "o", "\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn section_move(\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m268\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn delete(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m274\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn toggle_item(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m290\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn update_item(\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m341\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m "]
|
||||
[2.893617, "o", " pub fn get_roots(&self) -> Option<Vec<String>> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n"]
|
||||
[2.894153, "o", "\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog` (bin \"hyperlog\") generated 1 warning (run `cargo fix --bin \"hyperlog\"` to apply 1 suggestion)\r\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog-server` (lib) generated 20 warnings (run `cargo fix --lib -p hyperlog-server` to apply 4 suggestions)\r\n\u001b[1m\u001b[32m Finished\u001b[0m `release` profile [optimized] target(s) in 0.28s\r\n"]
|
||||
[2.903509, "o", "\u001b[1m\u001b[32m Running\u001b[0m `target/release/hyperlog --backend remote`\r\n"]
|
||||
[3.072772, "o", "\u001b[?1049h"]
|
||||
[3.088809, "o", "\u001b[1;1H\u001b[38;5;2mhyperlog\u001b[2;1H\u001b[39m───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[3;1Hsomething\u001b[3;11H~\u001b[3;13H(items:\u001b[3;21H2)\u001b[4;5Hi-can-do-this\u001b[4;19H~\u001b[4;21H(items:\u001b[4;29H1)\u001b[5;5Hsomething\u001b[5;15H~\u001b[5;17H(items:\u001b[5;25H3)\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[3.99447, "o", "\u001b[3;1H\u001b[38;2;255;165;0msomething ~ (items: 2)\u001b[5;5H\u001b[39m [ ] with-\u001b[5;23H \u001b[5;25H \u001b[6;5Hsomething\u001b[6;15H~\u001b[6;17H(items:\u001b[6;25H3)\u001b[7;9H[\u001b[7;11H]\u001b[7;13Hand-another\u001b[8;9H\u001b[38;5;8m...\u001b[9;9H\u001b[39m[\u001b[9;11H]\u001b[9;13Hitem\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[4.842467, "o", "\u001b[3;1Hsomething ~ (items: 2)\u001b[5;5Hsomething ~ (\u001b[5;23H:\u001b[5;25H3)\u001b[6;5H \u001b[6;15H \u001b[6;17H \u001b[6;25H \u001b[7;9H \u001b[7;11H \u001b[7;13H \u001b[8;9H \u001b[9;9H \u001b[9;11H \u001b[9;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.187689, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.37781, "o", "\u001b[33;2Hc\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.583938, "o", "\u001b[33;3Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[5.744397, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.118579, "o", "\u001b[33;5Hi\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[6.262015, "o", "\u001b[33;6Ht\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.099969, "o", "\u001b[33;7H-\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.211383, "o", "\u001b[33;8Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.326906, "o", "\u001b[33;9Ho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.40033, "o", "\u001b[33;10Hr\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.546151, "o", "\u001b[33;11Hk\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.670118, "o", "\u001b[33;12Hs\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.876666, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.877584, "o", "\u001b[33;1H \u001b[33;5H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.878029, "o", "\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.878496, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.891922, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.892175, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.901476, "o", "\u001b[3;1Hit-works ~ (items: 0) \u001b[4;1Hsomething ~ (items:\u001b[4;21H2) \u001b[4;29H \u001b[5;5Hi-can-do-this ~ (items:\u001b[5;29H1)\u001b[6;5Hsomething\u001b[6;15H~\u001b[6;17H(items:\u001b[6;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[7.901907, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[8.476726, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 0)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.1102, "o", "\u001b[1;10H~\u001b[1;12Hcreate\u001b[1;19Hitem\u001b[3;1H\u001b[38;5;8mpath: kjuulh.it-works \u001b[4;1H \u001b[5;1H\u001b[39m┌title────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[6;1H│\u001b[38;5;0m\u001b[48;5;5m \u001b[6;5H\u001b[39m\u001b[49m \u001b[6;15H \u001b[6;17H \u001b[6;25H \u001b[6;123H│\u001b[7;1H└───────────────────────────────────────────────────────────────────────────────────"]
|
||||
[9.110401, "o", "──────────────────────────────────────┘\u001b[8;1H\u001b[38;5;8m┌description──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[9;1H│ │\u001b[10;1H└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[33;5H\u001b[39mEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?"]
|
||||
[9.110539, "o", "25l"]
|
||||
[9.875008, "o", "\u001b[6;2HI\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[9.979676, "o", "\u001b[6;3H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.159581, "o", "\u001b[6;4Hc\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.224824, "o", "\u001b[6;5Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.344486, "o", "\u001b[6;6Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.424478, "o", "\u001b[6;7H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.616037, "o", "\u001b[6;8Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.717012, "o", "\u001b[6;9Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.867539, "o", "\u001b[6;10Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[10.965244, "o", "\u001b[6;11H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.038338, "o", "\u001b[6;12Hi\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.146079, "o", "\u001b[6;13Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.224729, "o", "\u001b[6;14He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.298028, "o", "\u001b[6;15Hm\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.493598, "o", "\u001b[6;16Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[11.859838, "o", "\u001b[6;17H \u001b[33;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.18879, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.27651, "o", "\u001b[33;2Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.348568, "o", "\u001b[33;3Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.518587, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.519161, "o", "\u001b[1;10H \u001b[1;12H \u001b[1;19H \u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 0)\u001b[39m \u001b[4;1Hsomething ~ (items: 2) \u001b[5;1H i-can-do-this ~ (items: 1) \u001b[6;1H \u001b[6;4H something ~ (items:\u001b[6;25H3)\u001b[6;123H \u001b[7;1H \u001b[8;1H \u001b[9;1H \u001b[10;1H \u001b[33;1H \u001b[39"]
|
||||
[12.519414, "o", "m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.51976, "o", "\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.520255, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.542732, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.542941, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.552336, "o", "\u001b[3;20H\u001b[38;2;255;165;0m1\u001b[4;1H\u001b[39m [ ] I-can-add-items\u001b[5;5H \u001b[5;19H \u001b[5;21H \u001b[5;29H \u001b[6;1Hsomething ~ (items: 2) \u001b[6;25H \u001b[7;5Hi-can-do-this\u001b[7;19H~\u001b[7;21H(items:\u001b[7;29H1)\u001b[8;5Hsomething\u001b[8;15H~\u001b[8;17H(items:\u001b[8;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[12.552493, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.299017, "o", "\u001b[3;1Hit-works ~ (items: 1)\u001b[4;5H\u001b[38;2;255;165;0m[ ] I-can-add-items\u001b[6;1H\u001b[39m \u001b[6;11H \u001b[6;13H \u001b[6;21H \u001b[7;5H \u001b[7;19H \u001b[7;21H \u001b[7;29H \u001b[8;5H \u001b[8;15H \u001b[8;17H \u001b[8;25H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.686285, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.68677, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[13.74053, "o", "\u001b[31mWell, this is embarrassing.\n\nhyperlog-tui had a problem and crashed. To help us diagnose the problem you can send us a crash report.\n\nWe have generated a report file at \"/var/folders/qp/6jwp97j95qg2ks56vrxt_l1m0000gn/T/report-1778ea7a-cf43-4c76-b260-d88c7bacebcc.toml\". Submit an issue or email with the subject of \"hyperlog-tui Crash Report\" and include the report as an attachment.\n\n\nWe take privacy seriously, and do not perform any automated error collection. In order to improve the software, we rely on people to submit reports.\n\nThank you kindly!\n\u001b[0m"]
|
||||
[15.682146, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 1)\u001b[4;5H\u001b[39m[ ] I-can-add-items\u001b[6;1Hsomething\u001b[6;11H~\u001b[6;13H(items:\u001b[6;21H2)\u001b[7;5Hi-can-do-this\u001b[7;19H~\u001b[7;21H(items:\u001b[7;29H1)\u001b[8;5Hsomething\u001b[8;15H~\u001b[8;17H(items:\u001b[8;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.020829, "o", "\u001b[1;10H~\u001b[1;12Hcreate\u001b[1;19Hitem\u001b[3;1H\u001b[38;5;8mpath: kjuulh.it-works \u001b[4;1H \u001b[5;1H\u001b[39m┌title────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[6;1H│\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[6;11H \u001b[6;13H \u001b[6;21H \u001b[6;123H│\u001b[7;1H└──────────────────────────────────────────────────────────────────────────────────────"]
|
||||
[17.020983, "o", "───────────────────────────────────┘\u001b[8;1H\u001b[38;5;8m┌description──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[9;1H│ │\u001b[10;1H└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[33;5H\u001b[39mEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.678389, "o", "\u001b[6;2Ho\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.804009, "o", "\u001b[6;3Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.865221, "o", "\u001b[6;4H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[17.982226, "o", "\u001b[6;5Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.06543, "o", "\u001b[6;6Hh\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.171003, "o", "\u001b[6;7He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.259035, "o", "\u001b[6;8H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.503667, "o", "\u001b[6;9Hb\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.613161, "o", "\u001b[6;10Ha\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.683243, "o", "\u001b[6;11Hc\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.753564, "o", "\u001b[6;12Hk\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.913502, "o", "\u001b[6;13He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[18.992754, "o", "\u001b[6;14Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[19.124873, "o", "\u001b[6;15Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.230883, "o", "\u001b[6;16H \u001b[33;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.543105, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.627545, "o", "\u001b[33;2Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.704119, "o", "\u001b[33;3Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.83678, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.837696, "o", "\u001b[1;10H \u001b[1;12H \u001b[1;19H \u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 1)\u001b[39m \u001b[4;1H [ ] I-can-add-items \u001b[5;1H \u001b[6;1Hs\u001b[6;3Hme\u001b[6;7Hing ~ (items:\u001b[6;21H2)\u001b[6;123H \u001b[7;1H i-can-do-this ~ (items: 1) \u001b[8;1H something ~ (items: 3) \u001b[9;1H \u001b[10;1H \u001b[33;1H \u001b[39"]
|
||||
[20.837737, "o", "m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.838339, "o", "\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.838812, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.85079, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.851148, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.855853, "o", "\u001b[3;20H\u001b[38;2;255;165;0m2\u001b[5;5H\u001b[39m[\u001b[5;7H]\u001b[5;9Hon-the-backend\u001b[6;1H \u001b[6;11H \u001b[6;13H \u001b[6;21H \u001b[7;1Hsomething ~ (items:\u001b[7;21H2) \u001b[7;29H \u001b[8;5Hi-can-do-this ~ (items:\u001b[8;29H1)\u001b[9;5Hsomething\u001b[9;15H~\u001b[9;17H(items:\u001b[9;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[20.856076, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.341665, "o", "\u001b[3;1Hit-works ~ (items: 2)\u001b[6;1H\u001b[38;2;255;165;0msomething ~ (items: 2)\u001b[7;1H\u001b[39m i-can-do-this ~\u001b[7;21H(items:\u001b[7;29H1)\u001b[8;5H [ ] with-items \u001b[8;29H \u001b[10;9H[\u001b[10;11H]\u001b[10;13Hand-another\u001b[11;9H\u001b[38;5;8m...\u001b[12;9H\u001b[39m[\u001b[12;11H]\u001b[12;13Hitem\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.516868, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 2)\u001b[6;1H\u001b[39m \u001b[7;1Hsomething ~ (items:\u001b[7;21H2) \u001b[7;29H \u001b[8;5Hi-can-do-this ~ (items:\u001b[8;29H1)\u001b[10;9H \u001b[10;11H \u001b[10;13H \u001b[11;9H \u001b[12;9H \u001b[12;11H \u001b[12;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[21.734986, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.153707, "o", "\u001b[1;10H~\u001b[1;12Hcreate\u001b[1;19Hitem\u001b[3;1H\u001b[38;5;8mpath: kjuulh.it-works \u001b[4;1H \u001b[5;1H\u001b[39m┌title────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[6;1H│\u001b[38;5;0m\u001b[48;5;5m \u001b[6;123H\u001b[39m\u001b[49m│\u001b[7;1H└───────────────────────────────────────────────────────────────────────────────────────────────────"]
|
||||
[22.153915, "o", "──────────────────────┘\u001b[8;1H\u001b[38;5;8m┌description──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐\u001b[9;1H│ │\u001b[10;1H└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘\u001b[33;5H\u001b[39mEDIT\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.83143, "o", "\u001b[6;2Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[22.930796, "o", "\u001b[6;3Ho\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.07636, "o", "\u001b[6;4Hg\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.231599, "o", "\u001b[6;5Hg\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.308648, "o", "\u001b[6;6Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.420762, "o", "\u001b[6;7He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.470615, "o", "\u001b[6;8H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.62503, "o", "\u001b[6;9Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.710239, "o", "\u001b[6;10Ho\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.826044, "o", "\u001b[6;11He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.935563, "o", "\u001b[6;12Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[23.94551, "o", "\u001b[6;13Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.15886, "o", "\u001b[6;14H'\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.231506, "o", "\u001b[6;15Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.292527, "o", "\u001b[6;16H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.45627, "o", "\u001b[6;17Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.521203, "o", "\u001b[6;18He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.689421, "o", "\u001b[6;19He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[24.849768, "o", "\u001b[6;20Hm\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.075116, "o", "\u001b[6;21H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.198095, "o", "\u001b[6;22Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.276978, "o", "\u001b[6;23Ho\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.372935, "o", "\u001b[6;24H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.782976, "o", "\u001b[6;24H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[25.934207, "o", "\u001b[6;23H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.081359, "o", "\u001b[6;22H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.223661, "o", "\u001b[6;21H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.367423, "o", "\u001b[6;20H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.515238, "o", "\u001b[6;19H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.659355, "o", "\u001b[6;18H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.803997, "o", "\u001b[6;17H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[26.953345, "o", "\u001b[6;16H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.095016, "o", "\u001b[6;15H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.244151, "o", "\u001b[6;14H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.393007, "o", "\u001b[6;13H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.535424, "o", "\u001b[6;12H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.683701, "o", "\u001b[6;11H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.834869, "o", "\u001b[6;10H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[27.979099, "o", "\u001b[6;9H\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.205228, "o", "\u001b[6;9Hi\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.335979, "o", "\u001b[6;10Hs\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.402599, "o", "\u001b[6;11Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.557636, "o", "\u001b[6;12H'\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.65723, "o", "\u001b[6;13Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.724086, "o", "\u001b[6;14H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[28.89684, "o", "\u001b[6;15Hi\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.052154, "o", "\u001b[6;16Hm\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.168321, "o", "\u001b[6;17Hp\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.337871, "o", "\u001b[6;18Hl\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.444157, "o", "\u001b[6;19He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.519464, "o", "\u001b[6;20Hm\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.621293, "o", "\u001b[6;21He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.69862, "o", "\u001b[6;22Hn\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.783626, "o", "\u001b[6;23Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[29.865553, "o", "\u001b[6;24He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.065802, "o", "\u001b[6;25Hd\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.132494, "o", "\u001b[6;26H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.296093, "o", "\u001b[6;27Hy\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.333876, "o", "\u001b[6;28He\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.432812, "o", "\u001b[6;29Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.507427, "o", "\u001b[6;30H \u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.652257, "o", "\u001b[6;31Ht\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.693191, "o", "\u001b[6;32Hh\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[30.845259, "o", "\u001b[6;33Ho\u001b[38;5;0m\u001b[48;5;5m \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[31.687752, "o", "\u001b[6;34H \u001b[33;5HVIEW\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[32.058966, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[32.119782, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[32.242868, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[33.6518, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[33.815445, "o", "\u001b[33;2Hw\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[33.932085, "o", "\u001b[33;3Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.356435, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.357091, "o", "\u001b[1;10H \u001b[1;12H \u001b[1;19H \u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 2)\u001b[39m \u001b[4;1H [ ] I-can-add-items \u001b[5;1H [ ] on-the-backend \u001b[6;1H \u001b[6;9H \u001b[6;15H \u001b[6;27H \u001b[6;31H \u001b[6;123H \u001b[7;1Hsomething ~ (items: 2) \u001b[8;1H i-can-do-this ~ (items: 1) \u001b[9;1H something ~ (items: 3) \u001b[10;1H "]
|
||||
[34.357262, "o", " \u001b[33;1H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.357672, "o", "\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.358206, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.386872, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.387286, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.397925, "o", "\u001b[3;20H\u001b[38;2;255;165;0m3\u001b[6;5H\u001b[39m[\u001b[6;7H]\u001b[6;9Htoggle-isn't-implemented-yet-tho\u001b[7;1H \u001b[7;11H \u001b[7;13H \u001b[7;21H \u001b[8;1Hsomething ~ (items:\u001b[8;21H2) \u001b[8;29H \u001b[9;5Hi-can-do-this ~ (items:\u001b[9;29H1)\u001b[10;5Hsomething\u001b[10;15H~\u001b[10;17H(items:\u001b[10;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[34.398137, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[36.157515, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[36.609848, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[36.785846, "o", "\u001b[33;2Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[40.410158, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[40.410779, "o", "\u001b[33;1H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[40.411947, "o", "\u001b[?1049l\u001b[?25h"]
|
||||
[40.417589, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[40.471035, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\u001b[33m37s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[40.471969, "o", "\u001b[6 q"]
|
||||
[40.472835, "o", "\u001b[6 q"]
|
||||
[40.473001, "o", "\u001b[?2004h"]
|
||||
[41.797938, "o", "\r\r\n"]
|
||||
[41.821612, "o", "\u001b[?1049h\u001b[?1000h\u001b[?1002h\u001b[?1003h\u001b[?1015h\u001b[?1006h\u001b[?2004h\u001b[>13u"]
|
||||
[41.976945, "o", "\u001b[1;2H\u001b[1mAtuin v18.2.0\u001b[1;26H\u001b[22m\u001b[38;5;8m \u001b[1m<esc>\u001b[22m: exit, \u001b[1m<tab>\u001b[22m: edit, \u001b[1m<enter>\u001b[22m: run, \u001b[1m<ctrl-o>\u001b[22m: inspect history count: 23952\u001b[2;3H\u001b[1m\u001b[38;5;15m\u001b[48;5;0mSearch\u001b[2;10H\u001b[22m\u001b[39m\u001b[49m│\u001b[2;12HInspect\u001b[3;2H╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮\u001b[4;2H│\u001b[4;6H\u001b[38;5;2m0s\u001b[4;13H\u001b[38;5;4m1h ago\u001b[4;20H\u001b[39mpsql\u001b[4;25H-h\u001b[4;28Hlocalhost\u001b[4;38H-p\u001b[4;41H26257\u001b[4;47H-U\u001b[4;50Hroot\u001b[4;55H-d\u001b[4;58Hdefaultdb\u001b[4;122H│\u001b[5;2H│\u001b[5;6H\u001b[38;5;1m124ms\u001b[5;13H\u001b[38;5;4m1h ago\u001b[5;20H\u001b[39mgrpcurl\u001b[5;28H-import-path\u001b[5;41Hcrates/hyperlog-protos/proto\u001b[5;70H-proto\u001b[5;77Hhyperlog.proto\u001b[5;92H-d\u001b[5;95H'{\"root\":\u001b[5;105H\"kjuulh\",\u001b[5;115H\"path\":│\u001b[6;2H│\u001b[6;6H\u001b[38;"]
|
||||
[41.977026, "o", "5;1m33ms\u001b[6;13H\u001b[38;5;4m1h ago\u001b[6;20H\u001b[39mgrpcurl\u001b[6;28H-import-path\u001b[6;41Hcrates/hyperlog-protos/proto\u001b[6;70H-proto\u001b[6;77Hhyperlog.proto\u001b[6;92H-plaintext\u001b[6;103Hlocalhost:4000\u001b[6;118Hhype│\u001b[7;2H│\u001b[7;6H\u001b[38;5;1m688ms\u001b[7;13H\u001b[38;5;4m1h ago\u001b[7;20H\u001b[39mcargo\u001b[7;26Hrun\u001b[7;30H--\u001b[7;33H--backend\u001b[7;43Hremote\u001b[7;50Hcreate-root\u001b[7;122H│\u001b[8;2H│\u001b[8;6H\u001b[38;5;2m712ms\u001b[8;13H\u001b[38;5;4m1h ago\u001b[8;20H\u001b[39mcargo\u001b[8;26Hrun\u001b[8;30H--\u001b[8;33H--backend\u001b[8;43Hremote\u001b[8;50Hcreate-root\u001b[8;62H--name\u001b[8;69Hkjuulh\u001b[8;122H│\u001b[9;2H│\u001b[9;6H\u001b[38;5;1m721ms\u001b[9;13H\u001b[38;5;4m1h ago\u001b[9;20H\u001b[39mcargo\u001b[9;26Hrun\u001b[9;30H--\u001b[9;33H--backend\u001b[9;43Hremote\u001b[9;50Hcreate-section\u001b[9;65H--name\u001b[9;72Hkjuulh\u001b[9;122H│\u001b[10;2H│\u001b[10;6H\u001b[38;5;2m684ms\u001b[10;13H\u001b[38;5;4m1h ago\u001b[10;20H\u001b[39mcargo\u001b[10;26Hrun\u001b[10;30H--\u001b[10;33H--backend\u001b[10;43Hremote\u001b[10;50H-h\u001b[10;122H│\u001b[11;2H│\u001b[11;6H\u001b[38;5;1m746ms\u001b[11;13H\u001b[38;5;4m1h ago\u001b[11;20H\u001b[39mcargo\u001b[11;26Hrun\u001b[11;30H--\u001b[11;33H--backend\u001b[11;43Hremote\u001b[11;50Hexec\u001b[11;122H│\u001b[12;2H│\u001b[12;6H\u001b[38;5;1m698ms\u001b[12;13H\u001b[38;5;4m1"]
|
||||
[41.977185, "o", "h ago\u001b[12;20H\u001b[39mcargo\u001b[12;26Hrun\u001b[12;30H--\u001b[12;33H--backend\u001b[12;43Hremote\u001b[12;50Hexec\u001b[12;55Hcreate-section\u001b[12;122H│\u001b[13;2H│\u001b[13;6H\u001b[38;5;1m727ms\u001b[13;13H\u001b[38;5;4m1h ago\u001b[13;20H\u001b[39mcargo\u001b[13;26Hrun\u001b[13;30H--\u001b[13;33H--backend\u001b[13;43Hremote\u001b[13;50Hexec\u001b[13;55Hcreate-section\u001b[13;70H--root\u001b[13;77Hkjuulh\u001b[13;122H│\u001b[14;2H│\u001b[14;6H\u001b[38;5;2m685ms\u001b[14;13H\u001b[38;5;4m1h ago\u001b[14;20H\u001b[39mcargo\u001b[14;26Hrun\u001b[14;30H--\u001b[14;33H--backend\u001b[14;43Hremote\u001b[14;50Hexec\u001b[14;55Hcreate-section\u001b[14;70H--root\u001b[14;77Hkjuulh\u001b[14;84H-h\u001b[14;122H│\u001b[15;2H│\u001b[15;6H\u001b[38;5;2m688ms\u001b[15;13H\u001b[38;5;4m1h ago\u001b[15;20H\u001b[39mcargo\u001b[15;26Hrun\u001b[15;30H--\u001b[15;33H--backend\u001b[15;43Hremote\u001b[15;50Hexec\u001b[15;55Hcreate-section\u001b[15;70H--root\u001b[15;77Hkjuulh\u001b[15;84H--path\u001b[15;91Hsomething\u001b[15;122H│\u001b[16;2H│\u001b[16;6H\u001b[38;5;2m742ms\u001b[16;13H\u001b[38;5;4m1h ago\u001b[16;20H\u001b[39mcargo\u001b[16;26Hrun\u001b[16;30H--\u001b[16;33H--backend\u001b[16;43Hremote\u001b[16;50Hexec\u001b[16;55Hcreate-section\u001b[16;70H--root\u001b[16;77Hkjuulh\u001b[16;84H--path\u001b[16;91Hsomething.something\u001b[16;122H│\u001b[17;2H│\u001b[17;6H\u001b[38;5;2m1s\u001b"]
|
||||
[41.97723, "o", "[17;13H\u001b[38;5;4m1h ago\u001b[17;20H\u001b[39mcargo\u001b[17;26Hrun\u001b[17;30H--\u001b[17;33H--backend\u001b[17;43Hremote\u001b[17;50Hexec\u001b[17;55H-h\u001b[17;122H│\u001b[18;2H│\u001b[18;6H\u001b[38;5;2m31ms\u001b[18;13H\u001b[38;5;4m1h ago\u001b[18;20H\u001b[39mz\u001b[18;22Hhyperlog\u001b[18;122H│\u001b[19;2H│\u001b[19;6H\u001b[38;5;2m2s\u001b[19;13H\u001b[38;5;4m1h ago\u001b[19;20H\u001b[39mrm\u001b[19;23H-rf\u001b[19;27Htarget\u001b[19;122H│\u001b[20;2H│\u001b[20;6H\u001b[38;5;1m1s\u001b[20;13H\u001b[38;5;4m1h ago\u001b[20;20H\u001b[39mcargo\u001b[20;26Hrun\u001b[20;30H--\u001b[20;33H--backend\u001b[20;43Hremote\u001b[20;122H│\u001b[21;2H│\u001b[21;4H9\u001b[21;6H\u001b[38;5;2m1s\u001b[21;13H\u001b[38;5;4m1h ago\u001b[21;20H\u001b[39mgca\u001b[21;24H\"feat:\u001b[21;31Hcan\u001b[21;35Hget\u001b[21;39Hactual\u001b[21;46Havailable\u001b[21;56Hroots\"\u001b[21;122H│\u001b[22;2H│\u001b[22;4H8\u001b[22;6H\u001b[38;5;2m2s\u001b[22;13H\u001b[38;5;4m1h ago\u001b[22;20H\u001b[39mgp\u001b[22;122H│\u001b[23;2H│\u001b[23;4H7\u001b[23;6H\u001b[38;5;2m22ms\u001b[23;13H\u001b[38;5;4m1h ago\u001b[23;20H\u001b[39mclear\u001b[23;122H│\u001b[24;2H│\u001b[24;4H6\u001b[24;6H\u001b[38;5;1m86ms\u001b[24;12H\u001b[38;5;4m57m ago\u001b[24;20H\u001b[39mgrpcurl\u001b[24;28H-import-path\u001b[24;41Hcrates/hyperlog-protos/proto\u001b[24;70H-proto\u001b[24;77Hhyperlog.proto\u001b[24;92H-d\u001b[24;95H'{\"root\":\u001b[24;105H\"kjuulh\"}'\u001b["]
|
||||
[41.977287, "o", "24;116H-plain│\u001b[25;2H│\u001b[25;4H5\u001b[25;6H\u001b[38;5;2m10s\u001b[25;12H\u001b[38;5;4m16m ago\u001b[25;20H\u001b[39mhyperlog\u001b[25;122H│\u001b[26;2H│\u001b[26;4H4\u001b[26;6H\u001b[38;5;2m38s\u001b[26;13H\u001b[38;5;4m3m ago\u001b[26;20H\u001b[39mcargo\u001b[26;26Hrun\u001b[26;30H--\u001b[26;33H--backend\u001b[26;43Hremote\u001b[26;122H│\u001b[27;2H│\u001b[27;4H3\u001b[27;6H\u001b[38;5;1m49s\u001b[27;13H\u001b[38;5;4m2m ago\u001b[27;20H\u001b[39mcargo\u001b[27;26Hrun\u001b[27;30H--release\u001b[27;40H--\u001b[27;43H--remote\u001b[27;52Hbackend\u001b[27;122H│\u001b[28;2H│\u001b[28;4H2\u001b[28;6H\u001b[38;5;2m42s\u001b[28;13H\u001b[38;5;4m1m ago\u001b[28;20H\u001b[39mcargo\u001b[28;26Hrun\u001b[28;30H--release\u001b[28;40H--\u001b[28;43H--backend\u001b[28;53Hremote\u001b[28;122H│\u001b[29;2H│\u001b[29;4H1\u001b[29;6H\u001b[38;5;2m0s\u001b[29;12H\u001b[38;5;4m42s ago\u001b[29;20H\u001b[39masciinema\u001b[29;30Hrec\u001b[29;34Hdemo.cast\u001b[29;44H--overwrite\u001b[29;122H│\u001b[30;2H│\u001b[30;4H>\u001b[30;6H\u001b[38;5;2m37s\u001b[30;12H\u001b[38;5;4m39s ago\u001b[1m\u001b[38;5;1m cargo run --release -- --backend remote\u001b[30;122H\u001b[22m\u001b[39m│\u001b[31;2H│───────────────────────────────────────────────────────"]
|
||||
[41.97742, "o", "────────────────────────────────────────────────────────────────│\u001b[32;2H│[\u001b[32;8HGLOBAL\u001b[32;18H]\u001b[32;122H│\u001b[33;2H╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25h\u001b[32;20H"]
|
||||
[42.066499, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25h\u001b[32;20H"]
|
||||
[42.319608, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25h\u001b[32;20H"]
|
||||
[42.571829, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25h\u001b[32;20H"]
|
||||
[42.727815, "o", "\u001b[?1000h\u001b[?1002h\u001b[?1003h\u001b[?1015h\u001b[?1006h\u001b[?1006l\u001b[?1015l\u001b[?1003l\u001b[?1002l\u001b[?1000l"]
|
||||
[42.728273, "o", "\u001b[<1u\u001b[?1049l\u001b[?1006l\u001b[?1015l\u001b[?1003l\u001b[?1002l\u001b[?1000l\u001b[?2004l"]
|
||||
[42.802586, "o", "\u001b[A\r\u001b[A\u001b[A\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\u001b[33m37s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[42.803675, "o", "cargo run --release -- --backend remote"]
|
||||
[42.809234, "o", "\u001b[39D\u001b[32mc\u001b[32ma\u001b[32mr\u001b[32mg\u001b[32mo\u001b[39m\u001b[34C"]
|
||||
[42.809682, "o", "\u001b[?1l\u001b>"]
|
||||
[42.809828, "o", "\u001b[?2004l"]
|
||||
[42.814158, "o", "\u001b[0 q"]
|
||||
[42.814414, "o", "\r\r\n"]
|
||||
[43.125266, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `GraphItem`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:1:26\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::{GraphItem, ItemState};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `hyperlog_core::log::GraphItem`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_section.rs:1:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::GraphItem;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused i"]
|
||||
[43.125298, "o", "mports: `GraphItem`, `ItemState`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:1:26\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse hyperlog_core::log::{GraphItem, ItemState};\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n"]
|
||||
[43.125414, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `sqlx::types::Json`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:2:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m2\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse sqlx::types::Json;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:102:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m102\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_variables)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variab"]
|
||||
[43.125455, "o", "le: `path`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:103:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m103\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m path,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `path: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `title`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:104:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m104\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m title,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `title: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `description`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:105:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;"]
|
||||
[43.125526, "o", "12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m105\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m description,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `description: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `state`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:106:17\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m106\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m state,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `state: _`\u001b[0m\r\n\r\n"]
|
||||
[43.125576, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:108:35\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m108\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::ToggleItem { root, path } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `path`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:108:41\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m108\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::ToggleItem { root, path } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `path: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b["]
|
||||
[43.125659, "o", "1m: unused variable: `root`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:29\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `root: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `src`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:35\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `src: _`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `dest`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0"]
|
||||
[43.125748, "o", "m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/commands.rs:109:40\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m109\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m Command::Move { root, src, dest } => todo!(),\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: try ignoring the field: `dest: _`\u001b[0m\r\n\r\n"]
|
||||
[43.125824, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused variable: `req`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_available_roots.rs:26:33\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m26\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33mhelp: if this is intentional, prefix it with an underscore: `_req`\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `root_name` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:31:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m29\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Root {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m30\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12"]
|
||||
[43.125897, "o", "m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m31\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root_name: String,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(dead_code)]` on by default\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `id` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_item.rs:36:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m35\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Section {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m-------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m36\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `root_name` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m"]
|
||||
[43.126022, "o", "\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/create_section.rs:19:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m17\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Root {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m18\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m19\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m root_name: String,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: field `id` is never read\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_graph.rs:37:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m36\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0mstruct Node {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m----\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12mfield in this struct\u001b[0"]
|
||||
[43.126115, "o", "m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m37\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m id: uuid::Uuid,\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `Node` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis\u001b[0m\r\n\r\n\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: multiple associated items are never used\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog-server/src/services/get_graph.rs:152:16\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m151\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m impl Engine {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m-----------\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12massociated items in this implementation\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m152\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn engine_from_str(input: &str) -> anyhow::Result<Self> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m "]
|
||||
[43.126211, "o", " \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m158\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn to_str(&self) -> anyhow::Result<String> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m219\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn get_mut(&mut self, root: &str, path: &[&str]) -> Option<&mut GraphItem> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m225\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn take(&mut self, root: &str, path: &[&str]) -> Option<GraphItem> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m231\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn section_move(\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b"]
|
||||
[43.126253, "o", "[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m268\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn delete(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m274\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn toggle_item(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m290\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn update_item(\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m...\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m341\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m pub fn get_roots(&self) -> Option<Vec<String>> {\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0"]
|
||||
[43.126325, "o", "m\u001b[1m\u001b[33m^^^^^^^^^\u001b[0m\r\n\r\n"]
|
||||
[43.126992, "o", "\u001b[0m\u001b[1m\u001b[33mwarning\u001b[0m\u001b[0m\u001b[1m: unused import: `std::net::SocketAddr`\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m--> \u001b[0m\u001b[0mcrates/hyperlog/src/cli.rs:1:5\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m\u001b[1m\u001b[38;5;12m1\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0muse std::net::SocketAddr;\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[33m^^^^^^^^^^^^^^^^^^^^\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m|\u001b[0m\r\n\u001b[0m \u001b[0m\u001b[0m\u001b[1m\u001b[38;5;12m= \u001b[0m\u001b[0m\u001b[1mnote\u001b[0m\u001b[0m: `#[warn(unused_imports)]` on by default\u001b[0m\r\n\r\n"]
|
||||
[43.127883, "o", "\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog-server` (lib) generated 20 warnings (run `cargo fix --lib -p hyperlog-server` to apply 4 suggestions)\r\n\u001b[1m\u001b[33mwarning\u001b[0m\u001b[1m:\u001b[0m `hyperlog` (bin \"hyperlog\") generated 1 warning (run `cargo fix --bin \"hyperlog\"` to apply 1 suggestion)\r\n"]
|
||||
[43.1279, "o", "\u001b[1m\u001b[32m Finished\u001b[0m `release` profile [optimized] target(s) in 0.29s\r\n"]
|
||||
[43.137727, "o", "\u001b[1m\u001b[32m Running\u001b[0m `target/release/hyperlog --backend remote`\r\n"]
|
||||
[43.314772, "o", "\u001b[?1049h"]
|
||||
[43.323208, "o", "\u001b[1;1H\u001b[38;5;2mhyperlog\u001b[2;1H\u001b[39m───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[3;1Hit-works\u001b[3;10H~\u001b[3;12H(items:\u001b[3;20H3)\u001b[4;5H[\u001b[4;7H]\u001b[4;9HI-can-add-items\u001b[5;5H\u001b[38;5;8m...\u001b[6;5H\u001b[39m[\u001b[6;7H]\u001b[6;9Htoggle-isn't-implemented-yet-tho\u001b[8;1Hsomething\u001b[8;11H~\u001b[8;13H(items:\u001b[8;21H2)\u001b[9;5Hi-can-do-this\u001b[9;19H~\u001b[9;21H(items:\u001b[9;29H1)\u001b[10;5Hsomething\u001b[10;15H~\u001b[10;17H(items:\u001b[10;25H3)\u001b[33;2H--\u001b[33;5HVIEW\u001b[33;10H--\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[43.929581, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 3)\u001b[5;5H\u001b[39m[ ]\u001b[5;9Hon-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[44.075108, "o", "\u001b[3;1Hit-works ~ (items: 3)\u001b[4;5H\u001b[38;2;255;165;0m[ ] I-can-add-items\u001b[8;1H\u001b[39m \u001b[8;11H \u001b[8;13H \u001b[8;21H \u001b[9;5H \u001b[9;19H \u001b[9;21H \u001b[9;29H \u001b[10;5H \u001b[10;15H \u001b[10;17H \u001b[10;25H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[44.874727, "o", "\u001b[4;5H[ ] I-can-add-items\u001b[5;5H\u001b[38;2;255;165;0m[ ] on-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[45.061332, "o", "\u001b[5;5H[ ] on-the-backend\u001b[6;5H\u001b[38;2;255;165;0m[ ] toggle-isn't-implemented-yet-tho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[45.206712, "o", "\u001b[5;5H\u001b[38;2;255;165;0m[ ] on-the-backend\u001b[6;5H\u001b[39m[ ] toggle-isn't-implemented-yet-tho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[45.37008, "o", "\u001b[4;5H\u001b[38;2;255;165;0m[ ] I-can-add-items\u001b[5;5H\u001b[39m[ ] on-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[45.673509, "o", "\u001b[4;5H[ ] I-can-add-items\u001b[5;5H\u001b[38;2;255;165;0m[ ] on-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[46.007542, "o", "\u001b[4;5H\u001b[38;2;255;165;0m[ ] I-can-add-items\u001b[5;5H\u001b[39m[ ] on-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[46.189383, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[46.345585, "o", "\u001b[4;5H[ ] I-can-add-items\u001b[5;5H\u001b[38;2;255;165;0m[ ] on-the-backend\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[46.567759, "o", "\u001b[5;5H[ ] on-the-backend\u001b[6;5H\u001b[38;2;255;165;0m[ ] toggle-isn't-implemented-yet-tho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[46.698547, "o", "\u001b[5;5H\u001b[38;2;255;165;0m[ ] on-the-backend\u001b[6;5H\u001b[39m[ ] toggle-isn't-implemented-yet-tho\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[47.043763, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 3)\u001b[5;5H\u001b[39m[ ] on-the-backend\u001b[8;1Hsomething\u001b[8;11H~\u001b[8;13H(items:\u001b[8;21H2)\u001b[9;5Hi-can-do-this\u001b[9;19H~\u001b[9;21H(items:\u001b[9;29H1)\u001b[10;5Hsomething\u001b[10;15H~\u001b[10;17H(items:\u001b[10;25H3)\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[47.720694, "o", "\u001b[3;1Hit-works ~ (items: 3)\u001b[5;5H\u001b[38;5;8m...\u001b[5;9H\u001b[39m \u001b[8;1H\u001b[38;2;255;165;0msomething ~ (items: 2)\u001b[10;5H\u001b[39m [ ] with-\u001b[10;23H \u001b[10;25H \u001b[11;5Hsomething\u001b[11;15H~\u001b[11;17H(items:\u001b[11;25H3)\u001b[12;9H[\u001b[12;11H]\u001b[12;13Hand-another\u001b[13;9H\u001b[38;5;8m...\u001b[14;9H\u001b[39m[\u001b[14;11H]\u001b[14;13Hitem\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[47.90605, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[48.194979, "o", "\u001b[3;1H\u001b[38;2;255;165;0mit-works ~ (items: 3)\u001b[5;5H\u001b[39m[ ]\u001b[5;9Hon-the-backend\u001b[8;1Hsomething ~ (items: 2)\u001b[10;5Hsomething ~ (\u001b[10;23H:\u001b[10;25H3)\u001b[11;5H \u001b[11;15H \u001b[11;17H \u001b[11;25H \u001b[12;9H \u001b[12;11H \u001b[12;13H \u001b[13;9H \u001b[14;9H \u001b[14;11H \u001b[14;13H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[48.367241, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[49.205066, "o", "\u001b[33;1H: \u001b[33;5H \u001b[33;10H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[49.360314, "o", "\u001b[33;2Hq\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[49.565543, "o", "\u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[49.567508, "o", "\u001b[33;1H \u001b[39m\u001b[49m\u001b[59m\u001b[0m\u001b[?25l"]
|
||||
[49.568849, "o", "\u001b[?1049l\u001b[?25h"]
|
||||
[49.570699, "o", "\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[49.627447, "o", "\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\u001b[33m6s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[49.628604, "o", "\u001b[6 q"]
|
||||
[49.629752, "o", "\u001b[6 q"]
|
||||
[49.62996, "o", "\u001b[?2004h"]
|
||||
[51.218262, "o", "\u001b[2 q"]
|
||||
[51.265765, "o", "\r\r\u001b[A\u001b[A\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\u001b[33m6s\u001b[0m \r\n\u001b[32m❮\u001b[0m \u001b[K"]
|
||||
[52.056217, "o", "\u001b[6 q"]
|
||||
[52.095427, "o", "\r\r\u001b[A\u001b[A\u001b[0m\u001b[27m\u001b[24m\u001b[J\r\n\u001b[38;2;255;153;102mhyperlog\u001b[0m \u001b[90mmain\u001b[0m\u001b[38;2;255;153;102m \u001b[0mis \u001b[1;38;5;208m📦 \u001b[0m\u001b[1;38;5;208mv0.1.0\u001b[0m \u001b[1;31mrs \u001b[0m\u001b[33m6s\u001b[0m \r\n\u001b[38;2;255;153;102m❯\u001b[0m \u001b[K"]
|
||||
[52.478423, "o", "\u001b[?2004l\r\r\n"]
|
||||
|
15
scripts/dev.sh
Executable file
15
scripts/dev.sh
Executable file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
echo "starting services"
|
||||
docker compose -f templates/docker-compose.yaml up -d --remove-orphans
|
||||
|
||||
sleep 5
|
||||
|
||||
tear_down() {
|
||||
echo "cleaning up services in the background"
|
||||
(docker compose -f templates/docker-compose.yaml down -v &) > /dev/null 2>&1
|
||||
}
|
||||
|
||||
trap tear_down SIGINT
|
||||
|
||||
RUST_LOG=info,hyperlog=trace cargo watch -x 'run -F include_server -- serve'
|
5
scripts/install.sh
Executable file
5
scripts/install.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
cargo install --path crates/hyperlog --force
|
@@ -1,4 +1,3 @@
|
||||
version: "3"
|
||||
services:
|
||||
crdb:
|
||||
restart: 'always'
|
||||
@@ -11,5 +10,5 @@ services:
|
||||
retries: 5
|
||||
start_period: '20s'
|
||||
ports:
|
||||
- 8080:8080
|
||||
- 28080:8080
|
||||
- '26257:26257'
|
||||
|
Reference in New Issue
Block a user