begin web server

new-idea
Avril 4 years ago
parent 4e71299f37
commit c51f87e8cb
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -838,3 +838,38 @@ cfg_if!{
const _:[();1] = [(); (($expr) as bool) as usize];
}
}
/// Clone this object with the same name.
#[macro_export] macro_rules! clone {
($ident:ident) => {let $ident = $ident.clone();}
}
/// Report this `Result`'s `Err` if it is one. Ignore if not.
#[macro_export] macro_rules! report {
(try $result:expr) => {
match $result
{
Ok(v) => Some(v),
Err(err) => {
error!("{}:{} error: {:?}", file!(), line!(), err);
trace!(concat!("In statement ", stringify!($result)));
None
}
}
};
($result:expr) => {
{
let _ = report!(try $result);
}
};
}
/// Move this value into this block.
#[macro_export] macro_rules! mv {
($ident:ident) => (let $ident = $ident;);
($($ident:ident),+ $(,)?) => {
$(
mv!($ident);
)+
}
}

@ -212,6 +212,15 @@ impl<'a, F: FormatSpec + ?Sized> From<FormattedString<F>> for String
}
}
impl<F: FormatSpec + ?Sized> std::str::FromStr for FormattedString<F>
{
type Err = F::Error;
#[inline] fn from_str(s: &str) -> Result<Self, Self::Err> {
F::validate(s)?;
Ok(Self(s.to_owned(), PhantomData))
}
}
impl<'a, F: FormatSpec + ?Sized> TryFrom<String> for FormattedString<F>
{
type Error = F::Error;

@ -46,6 +46,8 @@ mod user;
mod post;
mod state;
mod web;
fn install() -> eyre::Result<()>
{
color_eyre::install()?;

@ -70,6 +70,34 @@ impl State
))
}
/// Insert a new user into state.
///
/// # Locks
/// This function holds the state write lock while performing insertions.
pub async fn insert_user(&self, user: User) -> SharedUser
{
let user_id = *user.id();
let nuser = Arc::new(RwLock::new(user));
let mut write = self.0.oneesan.write().await;
let idx = write.users.insert(Arc::clone(&nuser));
write.users_map.insert(user_id, idx);
nuser
}
/// Insert a new post into state.
///
/// # Locks
/// This function holds the state write lock while performing insertions.
pub async fn insert_post(&self, owner: UserID, post: Post) -> SharedPost
{
let post_id =*post.post_id();
let npost = Arc::new(RwLock::new(post));
let mut write = self.0.oneesan.write().await;
let idx = write.posts.insert(Arc::clone(&npost));
write.posts_map.insert(post_id, idx);
write.posts_user_map.entry(owner).or_insert_with(|| MaybeVec::new()).push(idx);
npost
}
/// Get a shared reference to a user by this ID, if it exists.
///

@ -0,0 +1,28 @@
use super::*;
use std::any::Any;
#[derive(Debug)]
pub enum CommandKind
{
/// Shutdown gracefully.
///
/// # Response
/// None.
GracefulShutdown,
}
/// A response from the interrupt channel.
/// Some command kinds may warrant a response.
pub type CommandResponse = Box<dyn Any + Send + 'static>;
/// A command to interrupt the web background task.
#[derive(Debug)]
pub(super) struct Command
{
pub(super) kind: CommandKind,
pub(super) response: oneshot::Sender<CommandResponse>, // If the interrupt stream produces no response for this query, the sender will just be dropped and the receiver will `Err`.
}
/// A channel to communicate with background task
#[derive(Debug, Clone)]
pub struct InterruptChannel(pub(super) mpsc::Sender<Command>);

@ -0,0 +1,83 @@
use super::*;
use state::State;
use futures::prelude::*;
use tokio::{
sync::{
mpsc,
oneshot,
},
};
mod command;
pub use command::*;
/// Serve this state with this interrupt signal
pub fn serve(state: State) -> (InterruptChannel, impl Future<Output = eyre::Result<()>> + 'static)
{
// interrupt handler
let (int_tx, mut int_rx) = mpsc::channel::<Command>(16);
let (grace_tx, grace_rx) = oneshot::channel::<()>();
let (s_int_tx, s_int_rx) = oneshot::channel::<()>();
// When this future completes, the server will initiate a graceful shutdown.
let graceful_shutdown = async move {
tokio::select!{
//_ = tokio::signal::ctrl_c() =>{} //The caller should handle this and then send `InterruptChannel` a `GracefulShutdown` event.
_ = grace_rx => {}
}
};
let h_int = tokio::spawn(async move {
let work = async {
// Handle commands from interrupt channel.
while let Some(com) = int_rx.recv().await
{
let resp = com.response; //sender for response
match com.kind {
CommandKind::GracefulShutdown => {
report!(grace_tx.send(()));
return;
},
}
}
};
let int = async {
let _ = tokio::join![
//tokio::signal::ctrl_c(),
s_int_rx,
];
};
tokio::select!{
_ = int => {info!("h_int shutdown due to interrupt");},
_ = work => {info!("h_int shutdown due to exhausted work stream or shutdown signal");},
}
});
let command_channel = InterruptChannel(int_tx);
// setup server
let server = {
// TODO: warp routing paths
clone!(command_channel);
async move {
mv![command_channel, // If we need to send commands to our own stream
state, // The program state
graceful_shutdown, // The graceful shutdown Future for warp.
//TODO: routing paths
];
// TODO: warp::try_serve...
}
};
(command_channel,
async move {
info!("Waiting on server future");
tokio::join![
server,
h_int,
].1?;
report!(s_int_tx.send(()));
Ok(())
})
}
Loading…
Cancel
Save