Database basics
This commit is contained in:
parent
7d392ed3fa
commit
0969bff603
6 changed files with 216 additions and 138 deletions
53
flake.lock
53
flake.lock
|
@ -15,6 +15,21 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1637014545,
|
||||||
|
"narHash": "sha256-26IZAc5yzlD9FlDT54io1oqG/bBoyka+FJk5guaX4x4=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "bba5dcc8e0b20ab664967ad83d24d64cb64ec4f4",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"naersk": {
|
"naersk": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
|
@ -61,11 +76,47 @@
|
||||||
"type": "indirect"
|
"type": "indirect"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nixpkgs_3": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1637453606,
|
||||||
|
"narHash": "sha256-Gy6cwUswft9xqsjWxFYEnx/63/qzaFUwatcbV5GF/GQ=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8afc4e543663ca0a6a4f496262cd05233737e732",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"oxalica": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_3"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1655606998,
|
||||||
|
"narHash": "sha256-6XIQEwmoldCE3lzI54VQxD2tFJoeRsjRMYWkthtGRRw=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "4b600525be94c23b44c42441f33460343fafe7a1",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-utils": "flake-utils",
|
"flake-utils": "flake-utils",
|
||||||
"naersk": "naersk",
|
"naersk": "naersk",
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2",
|
||||||
|
"oxalica": "oxalica"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
61
flake.nix
61
flake.nix
|
@ -1,33 +1,38 @@
|
||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
naersk.url = "github:nix-community/naersk";
|
oxalica.url = "github:oxalica/rust-overlay";
|
||||||
};
|
naersk.url = "github:nix-community/naersk";
|
||||||
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, flake-utils, naersk }:
|
outputs = { self, nixpkgs, flake-utils, naersk, oxalica }:
|
||||||
flake-utils.lib.eachDefaultSystem (
|
flake-utils.lib.eachDefaultSystem (system:
|
||||||
system: let
|
let pkgs = import nixpkgs { inherit system; overlays = [ oxalica.overlay ]; };
|
||||||
pkgs = nixpkgs.legacyPackages."${system}";
|
naersk-lib = naersk.lib."${system}";
|
||||||
naersk-lib = naersk.lib."${system}";
|
in rec {
|
||||||
in
|
# `nix build`
|
||||||
rec {
|
packages."hermit" = naersk-lib.buildPackage {
|
||||||
# `nix build`
|
pname = "hermit";
|
||||||
packages."hermit" = naersk-lib.buildPackage {
|
root = ./.;
|
||||||
pname = "hermit";
|
};
|
||||||
root = ./.;
|
|
||||||
};
|
defaultPackage = packages."hermit";
|
||||||
defaultPackage = packages."hermit";
|
|
||||||
|
|
||||||
# `nix run`
|
# `nix run`
|
||||||
apps."hermit"= flake-utils.lib.mkApp {
|
apps."hermit"= flake-utils.lib.mkApp {
|
||||||
drv = packages."hermit";
|
drv = packages."hermit";
|
||||||
};
|
};
|
||||||
defaultApp = apps."hermit";
|
|
||||||
|
|
||||||
# `nix develop`
|
defaultApp = apps."hermit";
|
||||||
devShell = pkgs.mkShell {
|
|
||||||
nativeBuildInputs = with pkgs; [ rustc cargo openssl pkgconfig ];
|
# `nix develop`
|
||||||
};
|
devShell = pkgs.mkShell {
|
||||||
}
|
nativeBuildInputs = with pkgs; [
|
||||||
);
|
(rust-bin.selectLatestNightlyWith (t: t.default))
|
||||||
|
openssl
|
||||||
|
pkgconfig
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
//! ActivityPub implementation code and related abstractions.
|
//! ActivityPub implementation code and related abstractions.
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::{ Id, Activity, err, Result, Error, sign, ctx::Context };
|
use crate::{ Id, Activity, err, Result, Error, sign, ctx::Context };
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
pub enum Create {
|
pub enum Create {
|
||||||
Note { object: Note }
|
Note { object: Note }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Create> for Activity { fn from (a: Create) -> Activity { Activity::Create (a) } }
|
impl From<Create> for Activity { fn from (a: Create) -> Activity { Activity::Create (a) } }
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
pub enum Follow {
|
pub enum Follow {
|
||||||
Actor { object: Actor }
|
Actor { object: Actor }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Follow> for Activity { fn from (a: Follow) -> Activity { Activity::Follow (a) } }
|
impl From<Follow> for Activity { fn from (a: Follow) -> Activity { Activity::Follow (a) } }
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
pub enum Accept {
|
pub enum Accept {
|
||||||
Follow { object: Follow }
|
Follow { object: Follow }
|
||||||
}
|
}
|
||||||
|
@ -26,12 +27,12 @@ pub enum Accept {
|
||||||
impl From<Accept> for Activity { fn from (a: Accept) -> Activity { Activity::Accept (a) } }
|
impl From<Accept> for Activity { fn from (a: Accept) -> Activity { Activity::Accept (a) } }
|
||||||
|
|
||||||
/// An entity that publishes activities.
|
/// An entity that publishes activities.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize, sqlx::FromRow)]
|
||||||
pub struct Actor {
|
pub struct Actor {
|
||||||
id: Id,
|
id: Id,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
pub struct Note {
|
pub struct Note {
|
||||||
id: Id,
|
id: Id,
|
||||||
}
|
}
|
||||||
|
|
123
src/db/mod.rs
123
src/db/mod.rs
|
@ -1,13 +1,17 @@
|
||||||
//! Database abstraction layer used by Hermit.
|
//! Database abstraction layer used by Hermit.
|
||||||
|
|
||||||
use crate::{ Id, Result };
|
use std::borrow::Borrow;
|
||||||
|
use std::{marker::PhantomData, pin::Pin};
|
||||||
|
|
||||||
|
use crate::{ Id, Result, ap::Actor, err };
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use sqlx::{Executor, pool::PoolConnection};
|
use sqlx::{Executor, pool::PoolConnection, database::HasArguments};
|
||||||
|
use sqlx::{ FromRow, Either::Right };
|
||||||
|
|
||||||
/// `const ()` but in Rust
|
/// `const ()` but in Rust
|
||||||
fn void <T> (_: T) -> () { () }
|
fn void <T> (_: T) -> () { () }
|
||||||
|
|
||||||
type Database = sqlx::Postgres;
|
pub(crate) type Database = sqlx::Postgres;
|
||||||
|
|
||||||
/// Specifies how to connect to the database.
|
/// Specifies how to connect to the database.
|
||||||
pub struct Config {}
|
pub struct Config {}
|
||||||
|
@ -28,32 +32,27 @@ impl Client {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch the data mapped to the given `key` from the database.
|
pub async fn query <'e, T: 'e> (&self, query: Query<'e, T>) -> Pin<Box<dyn Stream<Item = Result<T>> + Send + 'e>> {
|
||||||
pub async fn get <T> (&self, key: T::Key) -> Result<Option<T>>
|
let Query (q, f) = query;
|
||||||
where
|
let stream = q
|
||||||
T: Get,
|
.fetch_many(&self.pool)
|
||||||
{
|
.filter_map(async move |r| {
|
||||||
self.with_conn(|c| T::get(key, c))
|
match r.map_err(err) {
|
||||||
.await
|
Ok (Right (row)) => Some (f(row)),
|
||||||
|
Err (error) => Some (Err (error)),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Box::pin (stream)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perfom an insertion on the database.
|
pub async fn get <'q, T: 'q> (&self, query: Query<'q, T>) -> Result<Option<T>> {
|
||||||
pub async fn insert <T> (&mut self, data: T) -> Result<()>
|
self.query(query)
|
||||||
where
|
.await
|
||||||
T: Insert,
|
.next()
|
||||||
{
|
.await
|
||||||
self.with_conn(|c| data.set(c))
|
.transpose()
|
||||||
.await
|
|
||||||
.map(void)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Delete something from the database.
|
|
||||||
pub async fn delete <T> (&mut self, key: T::Key) -> Result<()>
|
|
||||||
where
|
|
||||||
T: Delete,
|
|
||||||
{
|
|
||||||
self.with_conn(|c| T::del(key, c))
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles the getting-a-connection logic.
|
/// Handles the getting-a-connection logic.
|
||||||
|
@ -62,8 +61,6 @@ impl Client {
|
||||||
F: FnOnce (&mut PoolConnection<Database>) -> O,
|
F: FnOnce (&mut PoolConnection<Database>) -> O,
|
||||||
O: Future<Output = Result<T>>,
|
O: Future<Output = Result<T>>,
|
||||||
{
|
{
|
||||||
use crate::err;
|
|
||||||
|
|
||||||
self.pool
|
self.pool
|
||||||
.acquire()
|
.acquire()
|
||||||
.map_err(err)
|
.map_err(err)
|
||||||
|
@ -75,52 +72,24 @@ impl Client {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Object: Sized {
|
type Q<'a> = sqlx::query::Query<'a, Database, <Database as HasArguments<'a>>::Arguments>;
|
||||||
type Key: Eq;
|
type Row = <Database as sqlx::Database>::Row;
|
||||||
fn key (&self) -> &Self::Key;
|
|
||||||
}
|
pub struct Query <'a, T> (Q<'a>, fn (Row) -> Result<T>);
|
||||||
|
|
||||||
pub trait Insert: Object {
|
/// Generate a query that gets an [`Actor`] by its [`Id`].
|
||||||
type Future: Future<Output = Result<Self::Key>>;
|
pub fn get_actor <'a> (id: &'a Id) -> Query<'a, Actor> {
|
||||||
fn set <'e, E> (self, exec: E) -> Self::Future
|
|
||||||
where
|
// Prepare a query
|
||||||
E: Executor<'e>;
|
let query = sqlx::query("select * from actors where id = $1")
|
||||||
}
|
.bind(id);
|
||||||
|
|
||||||
pub trait Delete: Object {
|
// Return an sql query which will result in a series of rows,
|
||||||
type Future: Future<Output = Result<()>>;
|
// and a decoder function that will translate each row to a
|
||||||
fn del <'e, E> (key: Self::Key, exec: E) -> Self::Future where E: Executor<'e>;
|
// value of type `Actor`.
|
||||||
}
|
Query (query, |row: Row| {
|
||||||
|
let data = Actor::from_row(&row)?;
|
||||||
pub trait Get: Object {
|
Ok (data)
|
||||||
type Future: Future<Output = Result<Option<Self>>>;
|
})
|
||||||
fn get <'e, E> (key: Self::Key, exec: E) -> Self::Future where E: Executor<'e>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub mod ops {
|
|
||||||
|
|
||||||
//! Database operations (queries and updates).
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
pub struct Following {
|
|
||||||
pub from: Id,
|
|
||||||
pub to: Id,
|
|
||||||
pub id: Id,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Object for Following {
|
|
||||||
type Key = Id;
|
|
||||||
fn key (&self) -> &Self::Key { &self.id }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Insert for Following {
|
|
||||||
type Future = future::BoxFuture<'static, Result<Id>>;
|
|
||||||
fn set <'e, E> (self, exec: E) -> Self::Future
|
|
||||||
where
|
|
||||||
E: Executor<'e>
|
|
||||||
{
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
60
src/lib.rs
60
src/lib.rs
|
@ -1,7 +1,11 @@
|
||||||
|
#![feature(async_closure)]
|
||||||
//! # The Hermit ActivityPub server
|
//! # The Hermit ActivityPub server
|
||||||
//!
|
//!
|
||||||
//! This library contains the types and trait impls that make up the ActivityPub
|
//! This library contains the types and trait impls that make up the ActivityPub
|
||||||
//! support and database interaction for the Hermit ActivityPub server.
|
//! support and database interaction for the Hermit ActivityPub server.
|
||||||
|
#![feature(generic_associated_types)]
|
||||||
|
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
// Expose the `Id` type in the crate root
|
// Expose the `Id` type in the crate root
|
||||||
pub use id::Id;
|
pub use id::Id;
|
||||||
|
@ -14,7 +18,7 @@ pub mod db;
|
||||||
pub mod ap;
|
pub mod ap;
|
||||||
|
|
||||||
/// The Activity supertype used in abstractions over any kind of activity.
|
/// The Activity supertype used in abstractions over any kind of activity.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Serialize)]
|
||||||
pub enum Activity {
|
pub enum Activity {
|
||||||
/// Create a post.
|
/// Create a post.
|
||||||
Create (ap::Create),
|
Create (ap::Create),
|
||||||
|
@ -62,18 +66,19 @@ pub (crate) fn err (e: impl Into<Error>) -> Error { e.into() }
|
||||||
|
|
||||||
mod id {
|
mod id {
|
||||||
|
|
||||||
use std::str::FromStr;
|
use std::{str::FromStr, error::Error};
|
||||||
|
|
||||||
use serde::{ Deserialize, Serialize };
|
use serde::{ Deserialize, Serialize };
|
||||||
|
use sqlx::database::{HasArguments, HasValueRef};
|
||||||
|
use url::ParseError;
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
|
|
||||||
/// An ActivityPub identifier.
|
/// An ActivityPub identifier.
|
||||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
|
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
pub struct Id (reqwest::Url);
|
pub struct Id (reqwest::Url);
|
||||||
|
|
||||||
impl crate::IntoUrl for Id {
|
|
||||||
fn into_url (self) -> Option<url::Url> { Some (self.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Id {
|
impl FromStr for Id {
|
||||||
type Err = url::ParseError;
|
type Err = url::ParseError;
|
||||||
|
|
||||||
|
@ -82,6 +87,31 @@ mod id {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Id {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl sqlx::Type<db::Database> for Id {
|
||||||
|
fn type_info () -> <db::Database as sqlx::Database>::TypeInfo {
|
||||||
|
String::type_info()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'q> sqlx::Encode<'q, db::Database> for Id {
|
||||||
|
fn encode_by_ref(&self, buf: &mut <db::Database as HasArguments<'q>>::ArgumentBuffer) -> sqlx::encode::IsNull {
|
||||||
|
self.0.to_string().encode_by_ref(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'r> sqlx::Decode<'r, db::Database> for Id {
|
||||||
|
fn decode(value: <db::Database as HasValueRef<'r>>::ValueRef) -> Result<Self, sqlx::error::BoxDynError> {
|
||||||
|
<String as sqlx::Decode<db::Database>>::decode(value)
|
||||||
|
.map(|s| s.parse().expect("Failed to parse ID as URL"))
|
||||||
|
.map(Id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod ctx {
|
mod ctx {
|
||||||
|
@ -89,9 +119,9 @@ mod ctx {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use serde_json::Value;
|
use serde_json::{Value, to_value};
|
||||||
|
|
||||||
use crate::{ conf::Config, db, Result, sign::Sign, ap, Activity };
|
use crate::{ conf::Config, db, Result, sign::Sign, ap::{self, Actor}, Activity, Id, err };
|
||||||
|
|
||||||
/// The context of a thread/task.
|
/// The context of a thread/task.
|
||||||
///
|
///
|
||||||
|
@ -224,6 +254,20 @@ mod ctx {
|
||||||
|
|
||||||
Ok (value)
|
Ok (value)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch a value from the database by trying all the ActivityPub
|
||||||
|
/// records.
|
||||||
|
async fn db_fetch (&self, id: Id) -> Result<Option<Value>> {
|
||||||
|
|
||||||
|
if let Some (data) = self.db.get(db::get_actor(&id)).await? {
|
||||||
|
return to_value(data)
|
||||||
|
.map_err(err)
|
||||||
|
.map(Some)
|
||||||
|
}
|
||||||
|
|
||||||
|
todo!()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempt to dereference to a [`Create`](ap::Create) activity.
|
/// Attempt to dereference to a [`Create`](ap::Create) activity.
|
||||||
|
|
46
src/main.rs
46
src/main.rs
|
@ -1,11 +1,11 @@
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use futures::stream;
|
|
||||||
use hermit::{ Context, Error, db, sign, Activity, };
|
use hermit::{ Context, Error, db, sign, Activity, };
|
||||||
use hermit::conf::Config;
|
use hermit::conf::Config;
|
||||||
use tokio::sync::{mpsc, broadcast};
|
use tokio::sync::{mpsc, broadcast};
|
||||||
use task::Executor;
|
use task::Executor;
|
||||||
use tokio_stream::wrappers::{ReceiverStream, BroadcastStream};
|
use tokio_stream::wrappers::ReceiverStream;
|
||||||
|
|
||||||
/// Module that contains all the API endpoints and frontend pages
|
/// Module that contains all the API endpoints and frontend pages
|
||||||
/// used by Hermit.
|
/// used by Hermit.
|
||||||
|
@ -68,6 +68,8 @@ async fn main () {
|
||||||
ctrl_tx,
|
ctrl_tx,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Redefine `ctrl_tx`: it is now the transmitter that transmits
|
||||||
|
// *from* `Ctrl`.
|
||||||
let (ctrl_tx, _rx) = broadcast::channel(256);
|
let (ctrl_tx, _rx) = broadcast::channel(256);
|
||||||
|
|
||||||
ctx.run (task::Auto {
|
ctx.run (task::Auto {
|
||||||
|
@ -93,32 +95,28 @@ fn mk_channel <T> (size: usize) -> (mpsc::Sender<T>, ReceiverStream<T>) {
|
||||||
(tx, rx)
|
(tx, rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn err (e: impl Into<Error>) -> Error { e.into() }
|
|
||||||
|
|
||||||
mod task {
|
mod task {
|
||||||
|
|
||||||
//! Async tasks, communicating with each other across threads through generic
|
//! Async tasks, communicating with each other across threads through generic
|
||||||
//! streams and sinks.
|
//! streams and sinks.
|
||||||
|
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
use tokio::sync::{mpsc, broadcast};
|
use tokio::sync::{
|
||||||
|
broadcast,
|
||||||
|
mpsc,
|
||||||
|
};
|
||||||
use futures::prelude::*;
|
use futures::prelude::*;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use crate::web;
|
use crate::{
|
||||||
use crate::sign::Sign;
|
flow::Flow,
|
||||||
use crate::{flow::Flow, Activity, ctrl::Message, Context};
|
ctrl::Message,
|
||||||
|
sign::Sign,
|
||||||
|
Activity,
|
||||||
|
Context,
|
||||||
|
web,
|
||||||
|
};
|
||||||
|
|
||||||
impl<S> Executor for Context<S>
|
/// Something that can execute a [`Task`].
|
||||||
where
|
|
||||||
S: Sign + Send + Sync + 'static
|
|
||||||
{
|
|
||||||
fn run (&self, task: impl Task) {
|
|
||||||
let ctx: Context<S> = self.clone();
|
|
||||||
tokio::spawn(task.run(ctx));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Something that can execute a task.
|
|
||||||
pub trait Executor {
|
pub trait Executor {
|
||||||
|
|
||||||
/// Perform a [`Task`].
|
/// Perform a [`Task`].
|
||||||
|
@ -137,6 +135,16 @@ mod task {
|
||||||
where
|
where
|
||||||
S: Sign + Send + Sync + 'static;
|
S: Sign + Send + Sync + 'static;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Executor for Context<S>
|
||||||
|
where
|
||||||
|
S: Sign + Send + Sync + 'static
|
||||||
|
{
|
||||||
|
fn run (&self, task: impl Task) {
|
||||||
|
let ctx: Context<S> = self.clone();
|
||||||
|
tokio::spawn(task.run(ctx));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The main web server.
|
/// The main web server.
|
||||||
|
|
Loading…
Reference in a new issue