some bs
This commit is contained in:
@@ -7,3 +7,4 @@ edition = "2024"
|
|||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
gpui = { version = "*" }
|
gpui = { version = "*" }
|
||||||
paste = "1.0"
|
paste = "1.0"
|
||||||
|
reqwest = "0.13.2"
|
||||||
|
|||||||
@@ -1 +1,9 @@
|
|||||||
|
use crate::query;
|
||||||
|
|
||||||
pub(crate) mod repo;
|
pub(crate) mod repo;
|
||||||
|
|
||||||
|
pub struct QueryContext {
|
||||||
|
pub(crate) http: reqwest::Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl query::Context for QueryContext {}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
use crate::api::QueryContext;
|
||||||
use crate::query;
|
use crate::query;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct List;
|
pub struct List;
|
||||||
|
|
||||||
impl query::QueryFn for List {
|
impl query::QueryFn<QueryContext> for List {
|
||||||
type Data = ();
|
type Data = ();
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
@@ -11,7 +12,6 @@ impl query::QueryFn for List {
|
|||||||
"repo.list"
|
"repo.list"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&self) -> Result<Self::Data, Self::Error> {
|
async fn run(&self, c: &QueryContext) -> Result<Self::Data, Self::Error> {
|
||||||
todo!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use gpui::{div, prelude::*};
|
use gpui::{div, prelude::*};
|
||||||
|
|
||||||
use crate::app;
|
use crate::{api, app};
|
||||||
use crate::query;
|
use crate::query;
|
||||||
use crate::dashboard;
|
use crate::dashboard;
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
@@ -9,7 +9,7 @@ use crate::titlebar;
|
|||||||
pub struct Global {
|
pub struct Global {
|
||||||
pub safe_area: gpui::Bounds<gpui::Pixels>,
|
pub safe_area: gpui::Bounds<gpui::Pixels>,
|
||||||
pub current_theme: theme::Theme,
|
pub current_theme: theme::Theme,
|
||||||
pub query_store: query::Store
|
pub query_store: query::Store<api::QueryContext>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Chrome {}
|
pub struct Chrome {}
|
||||||
@@ -53,6 +53,6 @@ pub fn current_theme<'a, E>(cx: &'a gpui::Context<E>) -> &'a theme::Theme {
|
|||||||
&cx.global::<Global>().current_theme
|
&cx.global::<Global>().current_theme
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query_store<'a, E>(cx: &'a gpui::Context<E>) -> &'a query::Store {
|
pub fn query_store<'a, E>(cx: &'a gpui::Context<E>) -> &'a query::Store<api::QueryContext> {
|
||||||
&cx.global::<Global>().query_store
|
&cx.global::<Global>().query_store
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,9 @@ fn setup_application(cx: &mut gpui::App) {
|
|||||||
let global = app::Global {
|
let global = app::Global {
|
||||||
safe_area: bounds(point(px(0.), px(0.)), size(px(72.), px(12.))),
|
safe_area: bounds(point(px(0.), px(0.)), size(px(72.), px(12.))),
|
||||||
current_theme: cx.window_appearance().into(),
|
current_theme: cx.window_appearance().into(),
|
||||||
query_store: query::Store::new(),
|
query_store: query::Store::new(api::QueryContext {
|
||||||
|
http: reqwest::Client::new(),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
let top_left = global.safe_area.origin;
|
let top_left = global.safe_area.origin;
|
||||||
|
|||||||
109
src/query.rs
109
src/query.rs
@@ -2,13 +2,16 @@ use crate::app;
|
|||||||
use gpui::{AppContext, BorrowAppContext};
|
use gpui::{AppContext, BorrowAppContext};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
|
|
||||||
pub trait QueryFn: Clone + 'static {
|
pub trait QueryFn<C>: Clone + 'static
|
||||||
|
where
|
||||||
|
C: Context + 'static,
|
||||||
|
{
|
||||||
type Data: 'static;
|
type Data: 'static;
|
||||||
type Error: 'static;
|
type Error: 'static;
|
||||||
|
|
||||||
fn key(&self) -> &'static str;
|
fn key(&self) -> &'static str;
|
||||||
|
|
||||||
async fn run(&self) -> Result<Self::Data, Self::Error>;
|
async fn run(&self, c: &C) -> Result<Self::Data, Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum QueryStatus<'a, Data, Error> {
|
pub enum QueryStatus<'a, Data, Error> {
|
||||||
@@ -17,25 +20,29 @@ pub enum QueryStatus<'a, Data, Error> {
|
|||||||
Err(&'a Error),
|
Err(&'a Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn use_query<F, T>(query_fn: F, cx: &mut gpui::Context<T>)
|
pub fn use_query<F, T, C>(query_fn: F, cx: &mut gpui::Context<T>)
|
||||||
where
|
where
|
||||||
F: QueryFn,
|
C: Context + 'static,
|
||||||
|
F: QueryFn<C>,
|
||||||
T: 'static,
|
T: 'static,
|
||||||
|
Store<C>: gpui::Global,
|
||||||
{
|
{
|
||||||
let ent = cx.update_global::<app::Global, _>(|global, cx| {
|
let ent = cx.update_global::<Store<C>, _>(|store, cx| store.ensure_query_data(&query_fn, cx));
|
||||||
let entity = global.query_store.ensure_query(&query_fn, cx);
|
|
||||||
|
|
||||||
if entity.read_with(cx, |state, _| matches!(state.data, QueryData::Pending)) {
|
|
||||||
global.query_store.execute_query(query_fn, entity.clone(), cx).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
entity
|
|
||||||
});
|
|
||||||
|
|
||||||
cx.observe(&ent, |_, _, cx| {
|
cx.observe(&ent, |_, _, cx| {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
let query_context = cx.global::<Store<C>>().query_context.clone();
|
||||||
|
|
||||||
|
cx.observe(&query_context, move |_, _, cx| {
|
||||||
|
cx.update_global::<Store<C>, _>(|store, cx| {
|
||||||
|
store.invalidate_query(&query_fn, cx);
|
||||||
|
store.ensure_query_data(&query_fn, cx);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ================= Store ==================
|
// ================= Store ==================
|
||||||
@@ -47,26 +54,37 @@ pub(crate) struct QueryState {
|
|||||||
pub(crate) enum QueryData {
|
pub(crate) enum QueryData {
|
||||||
Pending,
|
Pending,
|
||||||
Loading,
|
Loading,
|
||||||
|
Stale,
|
||||||
Some(Box<dyn Any>),
|
Some(Box<dyn Any>),
|
||||||
Err(Box<dyn Any>),
|
Err(Box<dyn Any>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type Entity = gpui::Entity<QueryState>;
|
pub(crate) type Entity = gpui::Entity<QueryState>;
|
||||||
|
|
||||||
pub struct Store {
|
pub(crate) trait Context {}
|
||||||
|
|
||||||
|
pub struct Store<C>
|
||||||
|
where
|
||||||
|
C: Context,
|
||||||
|
{
|
||||||
query_data: std::collections::HashMap<String, Entity>,
|
query_data: std::collections::HashMap<String, Entity>,
|
||||||
|
query_context: gpui::Entity<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Store {
|
impl<C> Store<C>
|
||||||
pub fn new() -> Self {
|
where
|
||||||
|
C: Context + 'static,
|
||||||
|
{
|
||||||
|
pub fn new<E>(ctx: C, cx: &mut gpui::Context<E>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
query_data: std::collections::HashMap::new(),
|
query_data: std::collections::HashMap::new(),
|
||||||
|
query_context: cx.new(|_| ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ensure_query<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity
|
fn entity_for<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity
|
||||||
where
|
where
|
||||||
Q: QueryFn,
|
Q: QueryFn<C>,
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
self.query_data
|
self.query_data
|
||||||
@@ -79,23 +97,43 @@ impl Store {
|
|||||||
.clone()
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn ensure_query_data<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity
|
||||||
|
where
|
||||||
|
T: 'static,
|
||||||
|
Q: QueryFn<C>,
|
||||||
|
{
|
||||||
|
let entity = self.entity_for(query, cx);
|
||||||
|
|
||||||
|
let should_execute = entity.read_with(cx, |state, _| {
|
||||||
|
matches!(state.data, QueryData::Pending | QueryData::Stale)
|
||||||
|
});
|
||||||
|
|
||||||
|
if should_execute {
|
||||||
|
self.execute_query(query, cx).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
entity
|
||||||
|
}
|
||||||
|
|
||||||
fn execute_query<Q, T>(
|
fn execute_query<Q, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
query: Q,
|
query: &Q,
|
||||||
entity: Entity,
|
|
||||||
cx: &mut gpui::Context<T>,
|
cx: &mut gpui::Context<T>,
|
||||||
) -> gpui::Task<anyhow::Result<()>>
|
) -> gpui::Task<anyhow::Result<()>>
|
||||||
where
|
where
|
||||||
Q: QueryFn,
|
Q: QueryFn<C>,
|
||||||
T: 'static,
|
T: 'static,
|
||||||
{
|
{
|
||||||
|
let entity = self.entity_for(query, cx);
|
||||||
|
|
||||||
entity.update(cx, |state, cx| {
|
entity.update(cx, |state, cx| {
|
||||||
state.data = QueryData::Loading;
|
state.data = QueryData::Loading;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.spawn(async move |_, cx| {
|
cx.spawn(async move |_, cx| {
|
||||||
let result = query.run().await;
|
let query_context = self.query_context.read(cx);
|
||||||
|
let result = query.run(query_context).await;
|
||||||
|
|
||||||
entity.update(cx, |state, cx| {
|
entity.update(cx, |state, cx| {
|
||||||
state.data = match result {
|
state.data = match result {
|
||||||
@@ -109,15 +147,36 @@ impl Store {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_query<'a, Q, E>(&self, query_fn: Q, cx: &'a gpui::Context<E>) -> QueryStatus<'a, Q::Data, Q::Error> where Q: QueryFn {
|
fn invalidate_query<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>)
|
||||||
|
where
|
||||||
|
Q: QueryFn<C>,
|
||||||
|
{
|
||||||
|
if let Some(entity) = self.query_data.get(query.key()) {
|
||||||
|
entity.update(cx, |query, cx| {
|
||||||
|
if !matches!(query.data, QueryData::Loading) {
|
||||||
|
query.data = QueryData::Stale;
|
||||||
|
cx.notify()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn read_query<'a, Q, E>(
|
||||||
|
&self,
|
||||||
|
query_fn: Q,
|
||||||
|
cx: &'a gpui::Context<E>,
|
||||||
|
) -> QueryStatus<'a, Q::Data, Q::Error>
|
||||||
|
where
|
||||||
|
Q: QueryFn<C>,
|
||||||
|
{
|
||||||
let Some(ent) = self.query_data.get(query_fn.key()) else {
|
let Some(ent) = self.query_data.get(query_fn.key()) else {
|
||||||
return QueryStatus::Loading;
|
return QueryStatus::Loading;
|
||||||
};
|
};
|
||||||
let QueryState { data } = ent.read(cx);
|
let QueryState { data } = ent.read(cx);
|
||||||
match data {
|
match data {
|
||||||
QueryData::Loading | QueryData::Pending => QueryStatus::Loading,
|
QueryData::Loading | QueryData::Pending | QueryData::Stale => QueryStatus::Loading,
|
||||||
QueryData::Some(data) => QueryStatus::Loaded(data.downcast_ref::<Q::Data>().unwrap()),
|
QueryData::Some(data) => QueryStatus::Loaded(data.downcast_ref::<Q::Data>().unwrap()),
|
||||||
QueryData::Err(error) => QueryStatus::Err(error.downcast_ref::<Q::Error>().unwrap())
|
QueryData::Err(error) => QueryStatus::Err(error.downcast_ref::<Q::Error>().unwrap()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user