Skip to content

Commit

Permalink
Merge pull request #4 from leptos-rs/enocera/ci-cd
Browse files Browse the repository at this point in the history
feat(ci): add CI to the project
  • Loading branch information
benwis authored Nov 12, 2024
2 parents 249cf88 + 79bf829 commit d6d6469
Show file tree
Hide file tree
Showing 11 changed files with 163 additions and 109 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/main.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: CI

env:
CARGO_TERM_COLOR: always

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
make:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install cargo-make
uses: actions-rs/cargo@v1
with:
command: install
args: --debug cargo-make
- name: Run CI
uses: actions-rs/cargo@v1
with:
command: make
args: ci-flow
19 changes: 11 additions & 8 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@ edition = "2021"

[dependencies]
any_spawner = { git = "https://github.com/leptos-rs/leptos", features = ["futures-executor"] }
throw_error = { version = "0.2.0-rc1" }
hydration_context = { version = "0.2.0-rc1" }
throw_error = { git = "https://github.com/leptos-rs/leptos" }
hydration_context = { git = "https://github.com/leptos-rs/leptos" }
futures = "0.3.30"
wasi = "0.13.1+wasi-0.2.0"
leptos = { version = "0.7.0-rc1", features = ["nonce", "ssr"] }
leptos_meta = { version = "0.7.0-rc1", features = ["ssr"] }
leptos_router = { version = "0.7.0-rc1", features = ["ssr"] }
leptos_macro = { version = "0.7.0-rc1", features = ["generic"] }
leptos_integration_utils = { version = "0.7.0-rc1" }
server_fn = { version = "0.7.0-rc1", features = ["generic"] }
leptos = { git = "https://github.com/leptos-rs/leptos", features = ["nonce", "ssr"] }
leptos_meta = { git = "https://github.com/leptos-rs/leptos", features = ["ssr"] }
leptos_router = { git = "https://github.com/leptos-rs/leptos", features = ["ssr"] }
leptos_macro = { git = "https://github.com/leptos-rs/leptos", features = ["generic"] }
leptos_integration_utils = { git = "https://github.com/leptos-rs/leptos" }
server_fn = { git = "https://github.com/leptos-rs/leptos", features = ["generic"] }
http = "1.1.0"
parking_lot = "0.12.3"
bytes = "1.7.2"
routefinder = "0.5.4"
mime_guess = "2.0"
thiserror = "1.0.65"

[features]
islands-router = []
3 changes: 3 additions & 0 deletions Makefile.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[env]
CARGO_MAKE_RUN_CHECK_FORMAT = true
CARGO_MAKE_RUN_CLIPPY = true
2 changes: 2 additions & 0 deletions rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "nightly-2024-11-01"
12 changes: 12 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copied from https://github.com/leptos-rs/leptos/blob/main/rustfmt.toml
# to follow the same conventions across the organisation.

# Stable options
edition = "2021"
max_width = 80

# Unstable options
imports_granularity = "Crate"
format_strings = true
group_imports = "One"
format_code_in_doc_comments = true
42 changes: 26 additions & 16 deletions src/executor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,6 @@
//! [`Mode`] enum to trade-off reactivity for less host context switch
//! with the [`Mode::Stalled`] variant.
use std::{
cell::RefCell,
future::Future,
mem,
rc::Rc,
sync::{Arc, OnceLock},
task::{Context, Poll, Wake, Waker},
};

use any_spawner::CustomExecutor;
use futures::{
channel::mpsc::{UnboundedReceiver, UnboundedSender},
Expand All @@ -37,6 +28,14 @@ use futures::{
FutureExt, Stream,
};
use parking_lot::Mutex;
use std::{
cell::RefCell,
future::Future,
mem,
rc::Rc,
sync::{Arc, OnceLock},
task::{Context, Poll, Wake, Waker},
};
use wasi::{
clocks::monotonic_clock::{subscribe_duration, Duration},
io::poll::{poll, Pollable},
Expand Down Expand Up @@ -66,15 +65,19 @@ impl WaitPoll {
impl Future for WaitPoll {
type Output = ();

fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
fn poll(
self: std::pin::Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Self::Output> {
match &mut self.get_mut().0 {
this @ WaitPollInner::Unregistered(_) => {
let waker = Arc::new(WaitPollWaker::new(cx.waker()));

if let Some(sender) = POLLABLE_SINK.get() {
if let WaitPollInner::Unregistered(pollable) =
mem::replace(this, WaitPollInner::Registered(waker.clone()))
{
if let WaitPollInner::Unregistered(pollable) = mem::replace(
this,
WaitPollInner::Registered(waker.clone()),
) {
sender
.clone()
.unbounded_send(TableEntry(pollable, waker.into()))
Expand All @@ -85,7 +88,9 @@ impl Future for WaitPoll {
unreachable!();
}
} else {
panic!("cannot create a WaitPoll before creating an Executor");
panic!(
"cannot create a WaitPoll before creating an Executor"
);
}
}
WaitPollInner::Registered(waker) => {
Expand Down Expand Up @@ -181,13 +186,18 @@ impl Executor {
let (tx, mut rx) = futures::channel::oneshot::channel::<T::Output>();
self.spawn_local(Box::pin(fut.then(|val| async move {
if tx.send(val).is_err() {
panic!("failed to send the return value of the future passed to run_until");
panic!(
"failed to send the return value of the future passed to \
run_until"
);
}
})));

loop {
match rx.try_recv() {
Err(_) => panic!("internal error: sender of run until has been dropped"),
Err(_) => panic!(
"internal error: sender of run until has been dropped"
),
Ok(Some(val)) => return val,
Ok(None) => {
self.poll_local();
Expand Down
102 changes: 45 additions & 57 deletions src/handler.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#![forbid(unsafe_code)]

use std::sync::Arc;

use crate::{
response::{Body, Response, ResponseOptions},
utils::redirect,
CHUNK_BYTE_SIZE,
};
use bytes::Bytes;
use futures::{
stream::{self, once},
Expand All @@ -15,59 +18,29 @@ use http::{
use hydration_context::SsrSharedContext;
use leptos::{
prelude::{provide_context, Owner, ScopedFuture},
server_fn::{
codec::Encoding, http_export::Request, response::generic::Body as ServerFnBody, ServerFn,
ServerFnTraitObj,
},
IntoView,
};
use leptos_integration_utils::{ExtendResponse, PinnedStream};
use leptos_meta::ServerMetaContext;
use leptos_router::{
components::provide_server_redirect, location::RequestUrl, PathSegment, RouteList,
RouteListing, SsrMode,
components::provide_server_redirect, location::RequestUrl, PathSegment,
RouteList, RouteListing, SsrMode,
};
use mime_guess::MimeGuess;
use routefinder::Router;
use server_fn::middleware::Service;
use server_fn::{
codec::Encoding, http_export::Request, middleware::Service,
response::generic::Body as ServerFnBody, ServerFn, ServerFnTraitObj,
};
use std::sync::Arc;
use thiserror::Error;

use wasi::http::types::{IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam};

use crate::{
response::{Body, Response, ResponseOptions},
utils::redirect,
CHUNK_BYTE_SIZE,
use wasi::http::types::{
IncomingRequest, OutgoingBody, OutgoingResponse, ResponseOutparam,
};

/// Handle routing, static file serving and response tx using the low-level
/// `wasi:http` APIs.
///
/// ## Usage
///
/// Please, note that the handler expect to be run with a local Executor initiated.
///
/// ```
/// use leptos_wasi::prelude::Handler;
///
/// let conf = get_configuration(None).unwrap();
/// let leptos_options = conf.leptos_options;
///
/// Handler::build(request, response_out)
/// .expect("could not create handler")
/// // Those two functions should be called first because they can
/// // *shortcut* the handler, see "Performance Considerations".
///
/// // Any HTTP request prefixed with `/pkg` will call the passed
/// // `serve_static_files` function to deliver static files.
/// .static_files_handler("/pkg", serve_static_files)
/// .with_server_fn::<YourServerFn>()
/// // Fetch all available routes from your App.
/// .generate_routes(App)
/// // Actually process the request and write the response.
/// .handle_with_context(move || shell(leptos_options.clone()), || {}).await.expect("could not handle the request");
/// ```
///
/// ## Performance Considerations
///
/// This handler is optimised for the special case of WASI Components being spawned
Expand Down Expand Up @@ -101,7 +74,8 @@ pub struct Handler {
res_out: ResponseOutparam,

// *shortcut* if any is set
server_fn: Option<ServerFnTraitObj<Request<Bytes>, http::Response<ServerFnBody>>>,
server_fn:
Option<ServerFnTraitObj<Request<Bytes>, http::Response<ServerFnBody>>>,
preset_res: Option<Response>,
should_404: bool,

Expand All @@ -113,7 +87,10 @@ impl Handler {
/// Wraps the WASI resources to handle the request.
/// Could fail if the [`IncomingRequest`] cannot be converted to
/// a [`http:Request`].
pub fn build(req: IncomingRequest, res_out: ResponseOutparam) -> Result<Self, HandlerError> {
pub fn build(
req: IncomingRequest,
res_out: ResponseOutparam,
) -> Result<Self, HandlerError> {
Ok(Self {
req: crate::request::Request(req).try_into()?,
res_out,
Expand All @@ -136,14 +113,18 @@ impl Handler {
/// the call to [`Handler::handle_with_context`].
pub fn with_server_fn<T>(mut self) -> Self
where
T: ServerFn<ServerRequest = Request<Bytes>, ServerResponse = http::Response<ServerFnBody>>
+ 'static,
T: ServerFn<
ServerRequest = Request<Bytes>,
ServerResponse = http::Response<ServerFnBody>,
> + 'static,
{
if self.shortcut() {
return self;
}

if self.req.method() == T::InputEncoding::METHOD && self.req.uri().path() == T::PATH {
if self.req.method() == T::InputEncoding::METHOD
&& self.req.uri().path() == T::PATH
{
self.server_fn = Some(ServerFnTraitObj::new(
T::PATH,
T::InputEncoding::METHOD,
Expand Down Expand Up @@ -176,12 +157,9 @@ impl Handler {
return self;
}

if let Some(trimmed_url) = self
.req
.uri()
.path()
.strip_prefix(prefix.try_into().expect("you passed an invalid Uri").path())
{
if let Some(trimmed_url) = self.req.uri().path().strip_prefix(
prefix.try_into().expect("you passed an invalid Uri").path(),
) {
match handler(trimmed_url.to_string()) {
None => self.should_404 = true,
Some(body) => {
Expand All @@ -190,8 +168,10 @@ impl Handler {

res.headers_mut().insert(
http::header::CONTENT_TYPE,
HeaderValue::from_str(mime.first_or_octet_stream().as_ref())
.expect("internal error: could not parse MIME type"),
HeaderValue::from_str(
mime.first_or_octet_stream().as_ref(),
)
.expect("internal error: could not parse MIME type"),
);

self.preset_res = Some(Response(res));
Expand All @@ -204,7 +184,10 @@ impl Handler {

/// This mocks a request to the `app_fn` component to extract your
/// `<Router>`'s `<Routes>`.
pub fn generate_routes<IV>(self, app_fn: impl Fn() -> IV + 'static + Send + Clone) -> Self
pub fn generate_routes<IV>(
self,
app_fn: impl Fn() -> IV + 'static + Send + Clone,
) -> Self
where
IV: IntoView + 'static,
{
Expand All @@ -224,7 +207,11 @@ impl Handler {
where
IV: IntoView + 'static,
{
self.generate_routes_with_exclusions_and_context(app_fn, None, additional_context)
self.generate_routes_with_exclusions_and_context(
app_fn,
None,
additional_context,
)
}

/// This mocks a request to the `app_fn` component to extract your
Expand Down Expand Up @@ -451,7 +438,8 @@ impl Handler {
}

drop(output_stream);
OutgoingBody::finish(body, None).map_err(HandlerError::WasiResponseBody)?;
OutgoingBody::finish(body, None)
.map_err(HandlerError::WasiResponseBody)?;

Ok(())
}
Expand Down
Loading

0 comments on commit d6d6469

Please sign in to comment.