wip: connect to github
This commit is contained in:
253
src/query.rs
253
src/query.rs
@@ -1,9 +1,9 @@
|
||||
use gpui::{AppContext, BorrowAppContext};
|
||||
use std::{any::Any, collections::HashMap, marker::PhantomData};
|
||||
use std::{any::Any, collections::HashMap, marker::PhantomData, ops::Deref};
|
||||
|
||||
pub trait QueryFn: Clone + 'static {
|
||||
pub(crate) trait QueryFn: Clone + 'static {
|
||||
type Data: 'static;
|
||||
type Error: 'static;
|
||||
type Error: std::fmt::Debug + 'static;
|
||||
type Context: Context;
|
||||
|
||||
fn key(&self) -> &'static str;
|
||||
@@ -11,7 +11,13 @@ pub trait QueryFn: Clone + 'static {
|
||||
async fn run(&self, c: &Self::Context) -> Result<Self::Data, Self::Error>;
|
||||
}
|
||||
|
||||
struct Query {
|
||||
pub(crate) trait QueryAppContext: gpui::AppContext {
|
||||
fn ready<T>(value: T) -> Self::Result<T>;
|
||||
|
||||
fn map_result<T, U>(result: Self::Result<T>, f: impl FnOnce(T) -> U) -> Self::Result<U>;
|
||||
}
|
||||
|
||||
pub(crate) struct Query {
|
||||
key: &'static str,
|
||||
data: QueryData,
|
||||
}
|
||||
@@ -51,7 +57,7 @@ where
|
||||
ent
|
||||
});
|
||||
|
||||
cx.observe(&ent.raw, move |_, ent, cx| {
|
||||
cx.observe(&ent, move |_, ent, cx| {
|
||||
let query = ent.read(cx);
|
||||
if matches!(query.data, QueryData::Stale) {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
@@ -62,15 +68,6 @@ where
|
||||
})
|
||||
.detach();
|
||||
|
||||
let cloned_ent = ent.clone();
|
||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||
cx.observe(&query_context, move |_, _, cx| {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
store.invalidate_query(&cloned_ent, cx);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
ent
|
||||
}
|
||||
|
||||
@@ -82,7 +79,7 @@ where
|
||||
{
|
||||
let ent = cx.update_global::<Store<F::Context>, _>(|store, cx| store.entity_for(&query_fn, cx));
|
||||
|
||||
cx.observe(&ent.raw, move |_, ent, cx| {
|
||||
cx.observe(&ent, move |_, ent, cx| {
|
||||
let query = ent.read(cx);
|
||||
if matches!(query.data, QueryData::Stale) {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
@@ -93,37 +90,91 @@ where
|
||||
})
|
||||
.detach();
|
||||
|
||||
let cloned_ent = ent.clone();
|
||||
let query_context = cx.global::<Store<F::Context>>().query_context.clone();
|
||||
cx.observe(&query_context, move |_, _, cx| {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
store.invalidate_query(&cloned_ent, cx);
|
||||
});
|
||||
})
|
||||
.detach();
|
||||
|
||||
ent
|
||||
}
|
||||
|
||||
pub async fn fetch_query<F>(query_fn: F, cx: &mut gpui::AsyncApp) -> anyhow::Result<Entity<F>>
|
||||
where
|
||||
F: QueryFn,
|
||||
{
|
||||
let ent = cx.update(|cx| {
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| store.ensure_query_data(&query_fn, cx))
|
||||
})?;
|
||||
|
||||
enum WaitState {
|
||||
Cached,
|
||||
Waiting {
|
||||
rx: futures::channel::oneshot::Receiver<()>,
|
||||
sub: gpui::Subscription,
|
||||
},
|
||||
}
|
||||
|
||||
loop {
|
||||
let wait_state = cx.update(|cx| {
|
||||
let is_done = ent.read_with(cx, |query, _| {
|
||||
matches!(query.data, QueryData::Some(_) | QueryData::Err(_))
|
||||
});
|
||||
if is_done {
|
||||
WaitState::Cached
|
||||
} else {
|
||||
let (tx, rx) = futures::channel::oneshot::channel();
|
||||
let ent = ent.clone();
|
||||
let mut tx = Some(tx);
|
||||
|
||||
let sub = cx.observe(&ent, move |ent, cx| {
|
||||
let is_done = ent.read_with(cx, |query, _| {
|
||||
matches!(query.data, QueryData::Some(_) | QueryData::Err(_))
|
||||
});
|
||||
if is_done && let Some(tx) = tx.take() {
|
||||
tx.send(());
|
||||
}
|
||||
});
|
||||
|
||||
WaitState::Waiting { rx, sub }
|
||||
}
|
||||
})?;
|
||||
|
||||
match wait_state {
|
||||
WaitState::Cached => {
|
||||
return Ok(ent);
|
||||
}
|
||||
WaitState::Waiting { rx, sub } => {
|
||||
let _sub = sub;
|
||||
let _ = rx.await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F> Entity<F>
|
||||
where
|
||||
F: QueryFn,
|
||||
Store<F::Context>: gpui::Global,
|
||||
{
|
||||
pub fn refetch(&self, cx: &mut gpui::Context<F::Context>) {
|
||||
pub fn refetch<E>(&self, cx: &mut gpui::Context<E>)
|
||||
where
|
||||
E: 'static,
|
||||
{
|
||||
cx.update_global::<Store<F::Context>, _>(|store, cx| {
|
||||
store.invalidate_query(self, cx);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_query<'a, F, T>(
|
||||
query: &Entity<F>,
|
||||
cx: &'a gpui::Context<T>,
|
||||
) -> QueryStatus<'a, F::Data, F::Error>
|
||||
impl<F> Deref for Entity<F>
|
||||
where
|
||||
F: QueryFn,
|
||||
{
|
||||
type Target = gpui::Entity<Query>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_query<'a, F>(query: &Entity<F>, cx: &'a gpui::App) -> QueryStatus<'a, F::Data, F::Error>
|
||||
where
|
||||
F: QueryFn,
|
||||
T: 'static,
|
||||
{
|
||||
let state = query.raw.read(cx);
|
||||
|
||||
@@ -143,45 +194,55 @@ where
|
||||
C: Context,
|
||||
{
|
||||
query_data: HashMap<String, gpui::Entity<Query>>,
|
||||
query_context: gpui::Entity<C>,
|
||||
query_context: C,
|
||||
}
|
||||
|
||||
impl<C> Store<C>
|
||||
where
|
||||
C: Context + 'static,
|
||||
{
|
||||
pub fn new(ctx: C, cx: &mut gpui::App) -> Self {
|
||||
pub fn new(ctx: C) -> Self {
|
||||
Self {
|
||||
query_context: cx.new(|_| ctx),
|
||||
query_context: ctx,
|
||||
query_data: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn entity_for<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity<Q>
|
||||
where
|
||||
Q: QueryFn<Context = C>,
|
||||
T: 'static,
|
||||
{
|
||||
let raw = self
|
||||
.query_data
|
||||
.entry(query.key().into())
|
||||
.or_insert_with(|| {
|
||||
cx.new(|_| Query {
|
||||
key: query.key(),
|
||||
data: QueryData::Pending,
|
||||
})
|
||||
})
|
||||
.clone();
|
||||
|
||||
Entity {
|
||||
raw,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
pub(crate) fn update_query_context(&mut self, f: impl FnOnce(&mut C)) {
|
||||
f(&mut self.query_context);
|
||||
}
|
||||
|
||||
fn ensure_query_data<Q, T>(&mut self, query: &Q, cx: &mut gpui::Context<T>) -> Entity<Q>
|
||||
fn entity_for<Q, CX>(&mut self, query: &Q, cx: &mut CX) -> CX::Result<Entity<Q>>
|
||||
where
|
||||
Q: QueryFn<Context = C>,
|
||||
CX: QueryAppContext,
|
||||
{
|
||||
if let Some(raw) = self.query_data.get(query.key()) {
|
||||
return CX::ready(Entity {
|
||||
raw: raw.clone(),
|
||||
_marker: PhantomData,
|
||||
});
|
||||
}
|
||||
|
||||
let key = query.key();
|
||||
|
||||
CX::map_result(
|
||||
cx.new(|_| Query {
|
||||
key: query.key(),
|
||||
data: QueryData::Pending,
|
||||
}),
|
||||
|raw| {
|
||||
self.query_data.insert(key.into(), raw.clone());
|
||||
Entity {
|
||||
raw,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn ensure_query_data<Q>(&mut self, query: &Q, cx: &mut gpui::App) -> Entity<Q>
|
||||
where
|
||||
T: 'static,
|
||||
Q: QueryFn<Context = C>,
|
||||
{
|
||||
let entity = self.entity_for(query, cx);
|
||||
@@ -191,20 +252,19 @@ where
|
||||
});
|
||||
|
||||
if should_execute {
|
||||
self.execute_query(query, cx).detach();
|
||||
self.execute_query_detached(query, cx).detach();
|
||||
}
|
||||
|
||||
entity
|
||||
}
|
||||
|
||||
fn execute_query<Q, T>(
|
||||
fn execute_query_detached<Q>(
|
||||
&mut self,
|
||||
query: &Q,
|
||||
cx: &mut gpui::Context<T>,
|
||||
cx: &mut gpui::App,
|
||||
) -> gpui::Task<anyhow::Result<()>>
|
||||
where
|
||||
Q: QueryFn<Context = C>,
|
||||
T: 'static,
|
||||
{
|
||||
let entity = self.entity_for(query, cx);
|
||||
|
||||
@@ -216,14 +276,21 @@ where
|
||||
let q = query.clone();
|
||||
let query_context = self.query_context.clone();
|
||||
|
||||
cx.spawn(async move |_, cx| {
|
||||
let c = query_context.read_with(cx, |c, _| c.clone())?;
|
||||
let result = q.run(&c).await;
|
||||
cx.spawn(async move |cx| {
|
||||
println!("[query] {}", q.key());
|
||||
|
||||
let result = q.run(&query_context).await;
|
||||
|
||||
entity.raw.update(cx, |state, cx| {
|
||||
state.data = match result {
|
||||
Ok(data) => QueryData::Some(Box::new(data)),
|
||||
Err(err) => QueryData::Err(Box::new(err)),
|
||||
Ok(data) => {
|
||||
println!("[query] OK {}", q.key());
|
||||
QueryData::Some(Box::new(data))
|
||||
}
|
||||
Err(err) => {
|
||||
println!("[query] ERR {:?}: {:?}", q.key(), err);
|
||||
QueryData::Err(Box::new(err))
|
||||
}
|
||||
};
|
||||
cx.notify();
|
||||
})?;
|
||||
@@ -232,6 +299,34 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
async fn execute_query<'a, F>(&mut self, query_fn: &F, cx: &'a mut gpui::AsyncApp) -> Entity<F>
|
||||
where
|
||||
F: QueryFn<Context = C>,
|
||||
{
|
||||
let entity = self.entity_for(query_fn, cx).unwrap();
|
||||
|
||||
entity.update(cx, |query, cx| {
|
||||
query.data = QueryData::Loading;
|
||||
cx.notify();
|
||||
});
|
||||
|
||||
let result = query_fn.run(&self.query_context).await;
|
||||
|
||||
entity
|
||||
.raw
|
||||
.update(cx, |query, cx| {
|
||||
query.data = match result {
|
||||
Ok(data) => QueryData::Some(Box::new(data)),
|
||||
Err(err) => QueryData::Err(Box::new(err)),
|
||||
};
|
||||
cx.notify();
|
||||
true
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
entity
|
||||
}
|
||||
|
||||
fn invalidate_query<E, F>(&self, entity: &Entity<F>, cx: &mut gpui::Context<E>)
|
||||
where
|
||||
E: 'static,
|
||||
@@ -249,4 +344,34 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryAppContext for gpui::App {
|
||||
fn ready<T>(value: T) -> Self::Result<T> {
|
||||
value
|
||||
}
|
||||
|
||||
fn map_result<T, U>(result: Self::Result<T>, f: impl FnOnce(T) -> U) -> Self::Result<U> {
|
||||
f(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryAppContext for gpui::AsyncApp {
|
||||
fn ready<T>(value: T) -> Self::Result<T> {
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
fn map_result<T, U>(result: Self::Result<T>, f: impl FnOnce(T) -> U) -> Self::Result<U> {
|
||||
result.map(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, E> QueryAppContext for gpui::Context<'a, E> {
|
||||
fn ready<T>(value: T) -> Self::Result<T> {
|
||||
value
|
||||
}
|
||||
|
||||
fn map_result<T, U>(result: Self::Result<T>, f: impl FnOnce(T) -> U) -> Self::Result<U> {
|
||||
f(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> gpui::Global for Store<C> where C: Context + 'static {}
|
||||
|
||||
Reference in New Issue
Block a user