Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: setup default tracing subscriber #564

Merged
merged 6 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/tuono/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ path = "src/lib.rs"
[dependencies]
clap = { version = "4.5.4", features = ["derive", "cargo"] }
watchexec = "5.0.0"
tracing = "0.1.41"
tracing-subscriber = {version = "0.3.19", features = ["env-filter"]}
miette = "7.2.0"
watchexec-signals = "4.0.0"
tokio = { version = "1", features = ["full"] }
Expand Down
16 changes: 10 additions & 6 deletions crates/tuono/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use std::path::PathBuf;
use std::process::Child;
use std::process::Command;
use std::process::Stdio;
use tracing::error;
use tuono_internal::config::Config;

use crate::route::Route;
Expand Down Expand Up @@ -176,15 +177,18 @@ impl App {

pub fn build_react_prod(&self) {
if !Path::new(BUILD_JS_SCRIPT).exists() {
eprintln!("Failed to find the build script. Please run `npm install`");
error!("Failed to find the build script. Please run `npm install`");
std::process::exit(1);
}
let output = Command::new(BUILD_JS_SCRIPT)
.output()
.expect("Failed to build the react source");

let output = Command::new(BUILD_JS_SCRIPT).output().unwrap_or_else(|_| {
error!("Failed to build the react source");
std::process::exit(1);
});

if !output.status.success() {
eprintln!("Failed to build the react source");
eprintln!("Error: {}", String::from_utf8_lossy(&output.stderr));
error!("Failed to build the react source");
error!("Error: {}", String::from_utf8_lossy(&output.stderr));
std::process::exit(1);
}
}
Expand Down
44 changes: 36 additions & 8 deletions crates/tuono/src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,30 @@ use spinners::{Spinner, Spinners};
use std::path::PathBuf;
use std::thread::sleep;
use std::time::Duration;
use tracing::{error, trace};

use crate::app::App;
use crate::mode::Mode;

fn exit_gracefully_with_error(msg: &str) -> ! {
error!(msg);
std::process::exit(1);
}

pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
if no_js_emit {
println!("Rust build successfully finished");
return;
std::process::exit(0);
}

if ssg && app.has_dynamic_routes() {
// TODO: allow dynamic routes static generation
println!("Cannot statically build dynamic routes");
return;
std::process::exit(1);
}

app.build_tuono_config()
.expect("Failed to build tuono.config.ts");
.unwrap_or_else(|_| exit_gracefully_with_error("Failed to build tuono.config.ts"));

let mut app_build_spinner = Spinner::new(Spinners::Dots, "Building app...".into());

Expand All @@ -35,44 +41,66 @@ pub fn build(mut app: App, ssg: bool, no_js_emit: bool) {
let mut app_build_static_spinner =
Spinner::new(Spinners::Dots, "Static site generation".into());

let mut exit_gracefully_with_error = |msg: &str| -> ! {
app_build_static_spinner.stop_with_message("\u{274C} Build failed\n".into());
exit_gracefully_with_error(msg)
};

let static_dir = PathBuf::from("out/static");

if static_dir.is_dir() {
std::fs::remove_dir_all(&static_dir).expect("Failed to clear the out/static folder");
std::fs::remove_dir_all(&static_dir).unwrap_or_else(|_| {
exit_gracefully_with_error("Failed to clear the out/static folder")
});
}

std::fs::create_dir(&static_dir).expect("Failed to create static output dir");
std::fs::create_dir(&static_dir)
.unwrap_or_else(|_| exit_gracefully_with_error("Failed to create static output dir"));

copy(
"./out/client",
static_dir,
&CopyOptions::new().overwrite(true).content_only(true),
)
.expect("Failed to clone assets into static output folder");
.unwrap_or_else(|_| {
exit_gracefully_with_error("Failed to clone assets into static output folder")
});

// Start the server
#[allow(clippy::zombie_processes)]
let mut rust_server = app.run_rust_server();

let mut exit_and_shut_server = |msg: &str| -> ! {
_ = rust_server.kill();
exit_gracefully_with_error(msg)
};

let reqwest_client = reqwest::blocking::Client::builder()
.user_agent("")
.build()
.expect("Failed to build reqwest client");
.unwrap_or_else(|_| exit_and_shut_server("Failed to build reqwest client"));

// Wait for server
let mut is_server_ready = false;
let config = app.config.as_ref().unwrap();

while !is_server_ready {
trace!("Checking server availability");
let server_url = format!("http://{}:{}", config.server.host, config.server.port);
if reqwest_client.get(&server_url).send().is_ok() {
is_server_ready = true;
} else {
trace!("Server not ready yet. Sleeping for 1 second");
}
sleep(Duration::from_secs(1));
}

trace!("Server is ready, starting static site generation");

for (_, route) in app.route_map {
route.save_ssg_file(&reqwest_client);
if let Err(msg) = route.save_ssg_file(&reqwest_client) {
exit_and_shut_server(&msg);
}
}

// Close server
Expand Down
20 changes: 20 additions & 0 deletions crates/tuono/src/cli.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::path::PathBuf;

use clap::{Parser, Subcommand};
use tracing::{span, Level};
use tracing_subscriber::EnvFilter;

use crate::app::App;
use crate::build;
Expand Down Expand Up @@ -61,10 +63,20 @@ fn init_tuono_folder(mode: Mode) -> std::io::Result<App> {
}

pub fn app() -> std::io::Result<()> {
tracing_subscriber::fmt()
// Not need for the time since the code is synchronous
.without_time()
.with_env_filter(EnvFilter::from_default_env())
.init();

let args = Args::parse();

match args.action {
Actions::Dev => {
let span = span!(Level::TRACE, "DEV");

let _guard = span.enter();

let mut app = init_tuono_folder(Mode::Dev)?;

app.build_tuono_config()
Expand All @@ -75,6 +87,10 @@ pub fn app() -> std::io::Result<()> {
watch::watch().unwrap();
}
Actions::Build { ssg, no_js_emit } => {
let span = span!(Level::TRACE, "BUILD");

let _guard = span.enter();

let app = init_tuono_folder(Mode::Prod)?;

build::build(app, ssg, no_js_emit);
Expand All @@ -84,6 +100,10 @@ pub fn app() -> std::io::Result<()> {
template,
head,
} => {
let span = span!(Level::TRACE, "NEW");

let _guard = span.enter();

scaffold_project::create_new_project(folder_name, template, head);
}
}
Expand Down
97 changes: 77 additions & 20 deletions crates/tuono/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::fs::File;
use std::io;
use std::path::PathBuf;
use std::str::FromStr;
use tracing::trace;

fn has_dynamic_path(route: &str) -> bool {
let regex = Regex::new(r"\[(.*?)\]").expect("Failed to create the regex");
Expand Down Expand Up @@ -141,35 +142,72 @@ impl Route {
self.axum_info = Some(AxumInfo::new(self))
}

pub fn save_ssg_file(&self, reqwest: &Client) {
pub fn save_ssg_file(&self, reqwest: &Client) -> Result<(), String> {
if self.is_api() {
return Ok(());
}

let path = &self.path.replace("index", "");

let mut response = reqwest
.get(format!("http://localhost:3000{path}"))
.send()
.unwrap();
let url = format!("http://localhost:3000{path}");

trace!("Requesting the page: {}", url);
let mut response = match reqwest.get(format!("http://localhost:3000{path}")).send() {
Ok(response) => response,
Err(_) => return Err(format!("Failed to get the response: {}", url)),
};

let file_path = self.output_file_path();

let parent_dir = file_path.parent().unwrap();
let parent_dir = match file_path.parent() {
Some(parent_dir) => parent_dir,
None => {
return Err(format!(
"Failed to get the parent directory {:?}",
file_path
))
}
};

if !parent_dir.is_dir() {
create_all(parent_dir, false).expect("Failed to create parent directories");
if !parent_dir.is_dir() || create_all(parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
parent_dir
));
}

let mut file = File::create(file_path).expect("Failed to create the HTML file");
trace!("Saving the HTML file: {:?}", file_path);

io::copy(&mut response, &mut file).expect("Failed to write the HTML on the file");
let mut file = match File::create(&file_path) {
Ok(file) => file,
Err(_) => return Err(format!("Failed to create the file: {:?}", file_path)),
};

if io::copy(&mut response, &mut file).is_err() {
return Err(format!("Failed to write the file: {:?}", file_path));
}

// Saving also the server response
if self.axum_info.is_some() {
let data_file_path = PathBuf::from(&format!("out/static/__tuono/data{path}"));

let data_parent_dir = data_file_path.parent().unwrap();
trace!("The route is an axum route, saving the JSON file");

let data_file_path = PathBuf::from(&format!("out/static/__tuono/data{path}.json"));

let data_parent_dir = match data_file_path.parent() {
Some(parent_dir) => parent_dir,
None => {
return Err(format!(
"Failed to get the parent directory {:?}",
data_file_path
))
}
};

if !data_parent_dir.is_dir() {
create_all(data_parent_dir, false)
.expect("Failed to create data parent directories");
if !data_parent_dir.is_dir() && create_all(data_parent_dir, false).is_err() {
return Err(format!(
"Failed to create the parent directory {:?}",
data_parent_dir
));
}

let base = Url::parse("http://localhost:3000/__tuono/data").unwrap();
Expand All @@ -182,13 +220,32 @@ impl Route {
.join(pathname)
.expect("Failed to build the reqwest URL");

let mut response = reqwest.get(url).send().unwrap();
trace!("Requesting the JSON file: {}", url);

let mut response = match reqwest.get(url.clone()).send() {
Ok(response) => {
trace!("Successfully got the response for: {url}");
response
}
Err(_) => return Err(format!("Failed to get the response: {url}")),
};

let mut data_file =
File::create(data_file_path).expect("Failed to create the JSON file");
let mut data_file = match File::create(&data_file_path) {
Ok(file) => file,
Err(_) => {
return Err(format!(
"Failed to create the JSON file: {:?}",
data_file_path
))
}
};

io::copy(&mut response, &mut data_file).expect("Failed to write the JSON on the file");
if io::copy(&mut response, &mut data_file).is_err() {
return Err("Failed to write the JSON file".to_string());
}
}

Ok(())
}

fn output_file_path(&self) -> PathBuf {
Expand Down
Loading