✨ Añade servidor web y primeras macros de PageTop
- Crea el crate raíz `pagetop` y configura el workspace con `helpers/pagetop-macros`. - Añade las macros #[pagetop::main] y #[pagetop::test] que envuelven Actix-web para crear aplicaciones y pruebas asíncronas sin depender explícitamente del framework. - Reexporta, mediante el módulo `service`, los tipos esenciales de Actix-web para gestionar servidores y servicios web. - Implementa `Application::{new, run, test}` para simplificar el arranque y la ejecución de tests. - Expone `pagetop::prelude` con las macros, `service` y `Application` para una API pública coherente. - Incorpora ejemplo `examples/app-basic.rs` que levanta un servidor web vacío con el código mínimo para hacerlo.
This commit is contained in:
parent
45e2882653
commit
cbee4c2cb8
13 changed files with 1818 additions and 1 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
# Ignora directorios de compilación
|
||||||
|
**/target
|
1509
Cargo.lock
generated
Normal file
1509
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
38
Cargo.toml
Normal file
38
Cargo.toml
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
[package]
|
||||||
|
name = "pagetop"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
description = """\
|
||||||
|
Un entorno de desarrollo para crear soluciones web modulares, extensibles y configurables.\
|
||||||
|
"""
|
||||||
|
categories = ["web-programming", "gui", "development-tools", "asynchronous"]
|
||||||
|
keywords = ["pagetop", "web", "framework", "frontend", "ssr"]
|
||||||
|
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-web = "4.11.0"
|
||||||
|
|
||||||
|
pagetop-macros.workspace = true
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"helpers/pagetop-macros",
|
||||||
|
]
|
||||||
|
|
||||||
|
[workspace.package]
|
||||||
|
repository = "https://git.cillero.es/manuelcillero/pagetop"
|
||||||
|
homepage = "https://pagetop.cillero.es"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
authors = ["Manuel Cillero <manuel@cillero.es>"]
|
||||||
|
|
||||||
|
[workspace.dependencies]
|
||||||
|
# Helpers
|
||||||
|
pagetop-macros = { version = "0.0", path = "helpers/pagetop-macros" }
|
16
README.md
16
README.md
|
@ -12,6 +12,20 @@
|
||||||
la creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y JavaScript.
|
la creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y JavaScript.
|
||||||
|
|
||||||
|
|
||||||
|
# ⚡️ Guía rápida
|
||||||
|
|
||||||
|
La aplicación más sencilla de `PageTop` se ve así:
|
||||||
|
|
||||||
|
```rust#ignore
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
#[pagetop::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
Application::new().run()?.await
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
# 📜 Licencia
|
# 📜 Licencia
|
||||||
|
|
||||||
El código está disponible bajo una doble licencia:
|
El código está disponible bajo una doble licencia:
|
||||||
|
@ -26,7 +40,7 @@ Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el es
|
||||||
el ecosistema Rust.
|
el ecosistema Rust.
|
||||||
|
|
||||||
|
|
||||||
# ✨ Contribuciones
|
# ✨ Contribuir
|
||||||
|
|
||||||
Cualquier contribución para añadir al proyecto se considerará automáticamente bajo la doble licencia
|
Cualquier contribución para añadir al proyecto se considerará automáticamente bajo la doble licencia
|
||||||
indicada arriba (MIT o Apache v2.0), sin términos o condiciones adicionales, tal y como permite la
|
indicada arriba (MIT o Apache v2.0), sin términos o condiciones adicionales, tal y como permite la
|
||||||
|
|
6
examples/app-basic.rs
Normal file
6
examples/app-basic.rs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
#[pagetop::main]
|
||||||
|
async fn main() -> std::io::Result<()> {
|
||||||
|
Application::new().run()?.await
|
||||||
|
}
|
21
helpers/pagetop-macros/Cargo.toml
Normal file
21
helpers/pagetop-macros/Cargo.toml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
[package]
|
||||||
|
name = "pagetop-macros"
|
||||||
|
version = "0.0.1"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
description = """\
|
||||||
|
Una colección de macros que mejoran la experiencia de desarrollo con PageTop.\
|
||||||
|
"""
|
||||||
|
categories = ["development-tools::procedural-macro-helpers", "web-programming"]
|
||||||
|
keywords = ["pagetop", "macros", "proc-macros", "codegen"]
|
||||||
|
|
||||||
|
repository.workspace = true
|
||||||
|
homepage.workspace = true
|
||||||
|
license.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "1.0.40"
|
36
helpers/pagetop-macros/README.md
Normal file
36
helpers/pagetop-macros/README.md
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
<h1>PageTop Macros</h1>
|
||||||
|
|
||||||
|
<p>Una colección de macros que mejoran la experiencia de desarrollo con <strong>PageTop</strong>.</p>
|
||||||
|
|
||||||
|
[](#-license)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Sobre PageTop
|
||||||
|
|
||||||
|
[PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la web
|
||||||
|
clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles y
|
||||||
|
configurables, basadas en HTML, CSS y JavaScript.
|
||||||
|
|
||||||
|
|
||||||
|
# 🚧 Advertencia
|
||||||
|
|
||||||
|
`PageTop` es un proyecto personal para aprender [Rust](https://www.rust-lang.org/es) y conocer su
|
||||||
|
ecosistema. Su API está sujeta a cambios frecuentes. No se recomienda su uso en producción, al menos
|
||||||
|
hasta que se libere la versión **1.0.0**.
|
||||||
|
|
||||||
|
|
||||||
|
# 📜 Licencia
|
||||||
|
|
||||||
|
El código está disponible bajo una doble licencia:
|
||||||
|
|
||||||
|
* **Licencia MIT**
|
||||||
|
([LICENSE-MIT](LICENSE-MIT) o también https://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
* **Licencia Apache, Versión 2.0**
|
||||||
|
([LICENSE-APACHE](LICENSE-APACHE) o también https://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
|
Puedes elegir la licencia que prefieras. Este enfoque de doble licencia es el estándar de facto en
|
||||||
|
el ecosistema Rust.
|
60
helpers/pagetop-macros/src/lib.rs
Normal file
60
helpers/pagetop-macros/src/lib.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
//! <div align="center">
|
||||||
|
//!
|
||||||
|
//! <h1>PageTop Macros</h1>
|
||||||
|
//!
|
||||||
|
//! <p>Una colección de macros que mejoran la experiencia de desarrollo con <strong>PageTop</strong>.</p>
|
||||||
|
//!
|
||||||
|
//! [](#-license)
|
||||||
|
//!
|
||||||
|
//! </div>
|
||||||
|
//!
|
||||||
|
//! ## Sobre PageTop
|
||||||
|
//!
|
||||||
|
//! [PageTop](https://docs.rs/pagetop) es un entorno de desarrollo que reivindica la esencia de la
|
||||||
|
//! web clásica para crear soluciones web SSR (*renderizadas en el servidor*) modulares, extensibles
|
||||||
|
//! y configurables, basadas en HTML, CSS y JavaScript.
|
||||||
|
|
||||||
|
use proc_macro::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
|
||||||
|
/// Define una función `main` asíncrona como punto de entrada de `PageTop`.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust#ignore
|
||||||
|
/// #[pagetop::main]
|
||||||
|
/// async fn main() {
|
||||||
|
/// async { println!("Hello world!"); }.await
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn main(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let mut output: TokenStream = (quote! {
|
||||||
|
#[::pagetop::service::rt::main(system = "::pagetop::service::rt::System")]
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
|
output.extend(item);
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define funciones de prueba asíncronas para usar con `PageTop`.
|
||||||
|
///
|
||||||
|
/// # Ejemplos
|
||||||
|
///
|
||||||
|
/// ```rust#ignore
|
||||||
|
/// #[pagetop::test]
|
||||||
|
/// async fn test() {
|
||||||
|
/// assert_eq!(async { "Hello world" }.await, "Hello world");
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
|
||||||
|
let mut output: TokenStream = (quote! {
|
||||||
|
#[::pagetop::service::rt::test(system = "::pagetop::service::rt::System")]
|
||||||
|
})
|
||||||
|
.into();
|
||||||
|
|
||||||
|
output.extend(item);
|
||||||
|
output
|
||||||
|
}
|
50
src/app.rs
Normal file
50
src/app.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
//! Prepara y ejecuta una aplicación creada con `Pagetop`.
|
||||||
|
|
||||||
|
use crate::service;
|
||||||
|
|
||||||
|
use std::io::Error;
|
||||||
|
|
||||||
|
pub struct Application;
|
||||||
|
|
||||||
|
impl Application {
|
||||||
|
/// Crea una instancia de la aplicación.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ejecuta el servidor web de la aplicación.
|
||||||
|
pub fn run(self) -> Result<service::Server, Error> {
|
||||||
|
// Prepara el servidor web.
|
||||||
|
Ok(service::HttpServer::new(move || Self::service_app())
|
||||||
|
.bind("localhost:8080")?
|
||||||
|
.run())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prepara el servidor web de la aplicación para pruebas.
|
||||||
|
pub fn test(
|
||||||
|
self,
|
||||||
|
) -> service::App<
|
||||||
|
impl service::Factory<
|
||||||
|
service::Request,
|
||||||
|
Config = (),
|
||||||
|
Response = service::Response<service::BoxBody>,
|
||||||
|
Error = service::Error,
|
||||||
|
InitError = (),
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
Self::service_app()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configura el servicio web de la aplicación.
|
||||||
|
fn service_app() -> service::App<
|
||||||
|
impl service::Factory<
|
||||||
|
service::Request,
|
||||||
|
Config = (),
|
||||||
|
Response = service::Response<service::BoxBody>,
|
||||||
|
Error = service::Error,
|
||||||
|
InitError = (),
|
||||||
|
>,
|
||||||
|
> {
|
||||||
|
service::App::new()
|
||||||
|
}
|
||||||
|
}
|
46
src/lib.rs
Normal file
46
src/lib.rs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//! <div align="center">
|
||||||
|
//!
|
||||||
|
//! <img src="https://raw.githubusercontent.com/manuelcillero/pagetop/main/static/banner.png" />
|
||||||
|
//!
|
||||||
|
//! <h1>PageTop</h1>
|
||||||
|
//!
|
||||||
|
//! <p>Un entorno de desarrollo para crear soluciones web modulares, extensibles y configurables.</p>
|
||||||
|
//!
|
||||||
|
//! [](#-license)
|
||||||
|
//!
|
||||||
|
//! <br>
|
||||||
|
//! </div>
|
||||||
|
//!
|
||||||
|
//! `PageTop` reivindica la esencia de la web clásica usando [Rust](https://www.rust-lang.org/es)
|
||||||
|
//! para la creación de soluciones web SSR (*renderizadas en el servidor*) basadas en HTML, CSS y
|
||||||
|
//! JavaScript.
|
||||||
|
//!
|
||||||
|
//! # ⚡️ Guía rápida
|
||||||
|
//!
|
||||||
|
//! La aplicación más sencilla de `PageTop` se ve así:
|
||||||
|
//!
|
||||||
|
//! ```rust#ignore
|
||||||
|
//! use pagetop::prelude::*;
|
||||||
|
//!
|
||||||
|
//! #[pagetop::main]
|
||||||
|
//! async fn main() -> std::io::Result<()> {
|
||||||
|
//! Application::new().run()?.await
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
|
// RE-EXPORTED *************************************************************************************
|
||||||
|
|
||||||
|
pub use pagetop_macros::{main, test};
|
||||||
|
|
||||||
|
// API *********************************************************************************************
|
||||||
|
|
||||||
|
// Gestión del servidor y servicios web.
|
||||||
|
pub mod service;
|
||||||
|
// Prepara y ejecuta la aplicación.
|
||||||
|
pub mod app;
|
||||||
|
|
||||||
|
// PRELUDE *****************************************************************************************
|
||||||
|
|
||||||
|
pub mod prelude;
|
11
src/prelude.rs
Normal file
11
src/prelude.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
//! *Prelude* de `PageTop`.
|
||||||
|
|
||||||
|
// RE-EXPORTED.
|
||||||
|
|
||||||
|
pub use crate::{main, test};
|
||||||
|
|
||||||
|
// API.
|
||||||
|
|
||||||
|
pub use crate::service;
|
||||||
|
|
||||||
|
pub use crate::app::Application;
|
9
src/service.rs
Normal file
9
src/service.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
//! Gestión del servidor y servicios web ([actix-web](https://docs.rs/actix-web)).
|
||||||
|
|
||||||
|
pub use actix_web::body::BoxBody;
|
||||||
|
pub use actix_web::dev::Server;
|
||||||
|
pub use actix_web::dev::ServiceFactory as Factory;
|
||||||
|
pub use actix_web::dev::ServiceRequest as Request;
|
||||||
|
pub use actix_web::dev::ServiceResponse as Response;
|
||||||
|
pub use actix_web::{http, rt, test};
|
||||||
|
pub use actix_web::{App, Error, HttpServer};
|
15
tests/service.rs
Normal file
15
tests/service.rs
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
use pagetop::prelude::*;
|
||||||
|
|
||||||
|
#[pagetop::test]
|
||||||
|
async fn homepage_returns_404() {
|
||||||
|
let app = service::test::init_service(Application::new().test()).await;
|
||||||
|
|
||||||
|
let req = service::test::TestRequest::get().uri("/").to_request();
|
||||||
|
let resp = service::test::call_service(&app, req).await;
|
||||||
|
|
||||||
|
// Comprueba el acceso a la ruta de inicio.
|
||||||
|
// assert_eq!(resp.status(), service::http::StatusCode::OK);
|
||||||
|
|
||||||
|
// Sin ruta de inicio se obtiene error 404, pero el test funciona.
|
||||||
|
assert_eq!(resp.status(), service::http::StatusCode::NOT_FOUND);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue