Fix 2265: close tui on success, custom tui subscriber (#2734)
This commit is contained in:
1 parent
189772a17b
commit
63e7aab4e8
10 files changed
+226
-54
No files matched your search
@@ -1,4 +1,6 @@
|
|||||||
use crate::builder::{BuildMessage, MessageType, Stage, UpdateBuildProgress, UpdateStage};
|
use crate::builder::{
|
||||||
|
BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage,
|
||||||
|
};
|
||||||
use crate::dioxus_crate::DioxusCrate;
|
use crate::dioxus_crate::DioxusCrate;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
@@ -67,7 +69,7 @@ pub(crate) fn process_assets(
|
|||||||
"Optimized static asset {}",
|
"Optimized static asset {}",
|
||||||
file_asset
|
file_asset
|
||||||
)),
|
)),
|
||||||
source: None,
|
source: MessageSource::Build,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
assets_finished += 1;
|
assets_finished += 1;
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ mod fullstack;
|
|||||||
mod prepare_html;
|
mod prepare_html;
|
||||||
mod progress;
|
mod progress;
|
||||||
mod web;
|
mod web;
|
||||||
pub use progress::{BuildMessage, MessageType, Stage, UpdateBuildProgress, UpdateStage};
|
pub use progress::{
|
||||||
|
BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress, UpdateStage,
|
||||||
|
};
|
||||||
|
|
||||||
/// A request for a project to be built
|
/// A request for a project to be built
|
||||||
pub struct BuildRequest {
|
pub struct BuildRequest {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
//! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root.
|
//! Build the HTML file to load a web application. The index.html file may be created from scratch or modified from the `index.html` file in the crate root.
|
||||||
|
|
||||||
use super::{BuildRequest, UpdateBuildProgress};
|
use super::{BuildRequest, UpdateBuildProgress};
|
||||||
|
use crate::builder::progress::MessageSource;
|
||||||
use crate::builder::Stage;
|
use crate::builder::Stage;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
@@ -191,7 +192,7 @@ impl BuildRequest {
|
|||||||
update: super::UpdateStage::AddMessage(super::BuildMessage {
|
update: super::UpdateStage::AddMessage(super::BuildMessage {
|
||||||
level: Level::WARN,
|
level: Level::WARN,
|
||||||
message: super::MessageType::Text(message),
|
message: super::MessageType::Text(message),
|
||||||
source: None,
|
source: MessageSource::Build,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ use anyhow::Context;
|
|||||||
use cargo_metadata::{diagnostic::Diagnostic, Message};
|
use cargo_metadata::{diagnostic::Diagnostic, Message};
|
||||||
use futures_channel::mpsc::UnboundedSender;
|
use futures_channel::mpsc::UnboundedSender;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
use std::fmt::Display;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
@@ -83,7 +84,7 @@ pub enum UpdateStage {
|
|||||||
pub struct BuildMessage {
|
pub struct BuildMessage {
|
||||||
pub level: Level,
|
pub level: Level,
|
||||||
pub message: MessageType,
|
pub message: MessageType,
|
||||||
pub source: Option<String>,
|
pub source: MessageSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
@@ -92,6 +93,34 @@ pub enum MessageType {
|
|||||||
Text(String),
|
Text(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the source of where a message came from.
|
||||||
|
///
|
||||||
|
/// The CLI will render a prefix according to the message type
|
||||||
|
/// but this prefix, [`MessageSource::to_string()`] shouldn't be used if a strict message source is required.
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum MessageSource {
|
||||||
|
/// Represents any message from the running application. Renders `[app]`
|
||||||
|
App,
|
||||||
|
/// Represents any generic message from the CLI. Renders `[dev]`
|
||||||
|
///
|
||||||
|
/// Usage of Tracing inside of the CLI will be routed to this type.
|
||||||
|
Dev,
|
||||||
|
/// Represents a message from the build process. Renders `[bld]`
|
||||||
|
///
|
||||||
|
/// This is anything emitted from a build process such as cargo and optimizations.
|
||||||
|
Build,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for MessageSource {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::App => write!(f, "app"),
|
||||||
|
Self::Dev => write!(f, "dev"),
|
||||||
|
Self::Build => write!(f, "bld"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<Diagnostic> for BuildMessage {
|
impl From<Diagnostic> for BuildMessage {
|
||||||
fn from(message: Diagnostic) -> Self {
|
fn from(message: Diagnostic) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@@ -104,7 +133,7 @@ impl From<Diagnostic> for BuildMessage {
|
|||||||
cargo_metadata::diagnostic::DiagnosticLevel::Help => Level::DEBUG,
|
cargo_metadata::diagnostic::DiagnosticLevel::Help => Level::DEBUG,
|
||||||
_ => Level::DEBUG,
|
_ => Level::DEBUG,
|
||||||
},
|
},
|
||||||
source: Some("cargo".to_string()),
|
source: MessageSource::Build,
|
||||||
message: MessageType::Cargo(message),
|
message: MessageType::Cargo(message),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,7 +235,7 @@ pub(crate) async fn build_cargo(
|
|||||||
update: UpdateStage::AddMessage(BuildMessage {
|
update: UpdateStage::AddMessage(BuildMessage {
|
||||||
level: Level::DEBUG,
|
level: Level::DEBUG,
|
||||||
message: MessageType::Text(line),
|
message: MessageType::Text(line),
|
||||||
source: None,
|
source: MessageSource::Build,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
settings::{self},
|
settings::{self},
|
||||||
|
tracer::CLILogControl,
|
||||||
DioxusCrate,
|
DioxusCrate,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
@@ -105,13 +106,13 @@ impl Serve {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn serve(mut self) -> anyhow::Result<()> {
|
pub async fn serve(mut self, log_control: CLILogControl) -> anyhow::Result<()> {
|
||||||
let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args)
|
let mut dioxus_crate = DioxusCrate::new(&self.build_arguments.target_args)
|
||||||
.context("Failed to load Dioxus workspace")?;
|
.context("Failed to load Dioxus workspace")?;
|
||||||
|
|
||||||
self.resolve(&mut dioxus_crate)?;
|
self.resolve(&mut dioxus_crate)?;
|
||||||
|
|
||||||
crate::serve::serve_all(self, dioxus_crate).await?;
|
crate::serve::serve_all(self, dioxus_crate, log_control).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ pub mod assets;
|
|||||||
pub mod dx_build_info;
|
pub mod dx_build_info;
|
||||||
pub mod serve;
|
pub mod serve;
|
||||||
pub mod tools;
|
pub mod tools;
|
||||||
|
pub mod tracer;
|
||||||
|
|
||||||
pub mod cli;
|
pub mod cli;
|
||||||
pub use cli::*;
|
pub use cli::*;
|
||||||
@@ -23,21 +24,16 @@ pub(crate) use settings::*;
|
|||||||
|
|
||||||
pub(crate) mod metadata;
|
pub(crate) mod metadata;
|
||||||
|
|
||||||
use std::env;
|
|
||||||
use tracing_subscriber::{prelude::*, EnvFilter, Layer};
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
use Commands::*;
|
use Commands::*;
|
||||||
|
|
||||||
const LOG_ENV: &str = "DIOXUS_LOG";
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> anyhow::Result<()> {
|
async fn main() -> anyhow::Result<()> {
|
||||||
let args = Cli::parse();
|
let args = Cli::parse();
|
||||||
|
|
||||||
build_tracing();
|
let log_control = tracer::build_tracing();
|
||||||
|
|
||||||
match args.action {
|
match args.action {
|
||||||
Translate(opts) => opts
|
Translate(opts) => opts
|
||||||
@@ -79,7 +75,7 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
.context(error_wrapper("Cleaning project failed")),
|
.context(error_wrapper("Cleaning project failed")),
|
||||||
|
|
||||||
Serve(opts) => opts
|
Serve(opts) => opts
|
||||||
.serve()
|
.serve(log_control)
|
||||||
.await
|
.await
|
||||||
.context(error_wrapper("Serving project failed")),
|
.context(error_wrapper("Serving project failed")),
|
||||||
|
|
||||||
@@ -94,21 +90,3 @@ async fn main() -> anyhow::Result<()> {
|
|||||||
fn error_wrapper(message: &str) -> String {
|
fn error_wrapper(message: &str) -> String {
|
||||||
format!("🚫 {message}:")
|
format!("🚫 {message}:")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tracing() {
|
|
||||||
// If {LOG_ENV} is set, default to env, otherwise filter to cli
|
|
||||||
// and manganis warnings and errors from other crates
|
|
||||||
let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info,manganis-cli-support=info");
|
|
||||||
if env::var(LOG_ENV).is_ok() {
|
|
||||||
filter = EnvFilter::from_env(LOG_ENV);
|
|
||||||
}
|
|
||||||
|
|
||||||
let sub =
|
|
||||||
tracing_subscriber::registry().with(tracing_subscriber::fmt::layer().with_filter(filter));
|
|
||||||
|
|
||||||
#[cfg(feature = "tokio-console")]
|
|
||||||
sub.with(console_subscriber::spawn()).init();
|
|
||||||
|
|
||||||
#[cfg(not(feature = "tokio-console"))]
|
|
||||||
sub.init();
|
|
||||||
}
|
|
||||||
@@ -6,9 +6,9 @@ use crate::serve::Serve;
|
|||||||
use crate::Result;
|
use crate::Result;
|
||||||
use dioxus_cli_config::Platform;
|
use dioxus_cli_config::Platform;
|
||||||
use futures_channel::mpsc::UnboundedReceiver;
|
use futures_channel::mpsc::UnboundedReceiver;
|
||||||
use futures_util::future::OptionFuture;
|
|
||||||
use futures_util::stream::select_all;
|
use futures_util::stream::select_all;
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
|
use futures_util::{future::OptionFuture, stream::FuturesUnordered};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
process::{Child, Command},
|
process::{Child, Command},
|
||||||
@@ -95,8 +95,20 @@ impl Builder {
|
|||||||
.map(|(platform, rx)| rx.map(move |update| (*platform, update))),
|
.map(|(platform, rx)| rx.map(move |update| (*platform, update))),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The ongoing builds directly
|
||||||
let results: OptionFuture<_> = self.build_results.as_mut().into();
|
let results: OptionFuture<_> = self.build_results.as_mut().into();
|
||||||
|
|
||||||
|
// The process exits
|
||||||
|
let mut process_exited = self
|
||||||
|
.children
|
||||||
|
.iter_mut()
|
||||||
|
.map(|(_, child)| async move {
|
||||||
|
let status = child.wait().await.ok();
|
||||||
|
|
||||||
|
BuilderUpdate::ProcessExited { status }
|
||||||
|
})
|
||||||
|
.collect::<FuturesUnordered<_>>();
|
||||||
|
|
||||||
// Wait for the next build result
|
// Wait for the next build result
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
Some(build_results) = results => {
|
Some(build_results) = results => {
|
||||||
@@ -111,6 +123,9 @@ impl Builder {
|
|||||||
// If we have a build progress, send it to the screen
|
// If we have a build progress, send it to the screen
|
||||||
Ok(BuilderUpdate::Progress { platform, update })
|
Ok(BuilderUpdate::Progress { platform, update })
|
||||||
}
|
}
|
||||||
|
Some(exit_status) = process_exited.next() => {
|
||||||
|
Ok(exit_status)
|
||||||
|
}
|
||||||
else => {
|
else => {
|
||||||
std::future::pending::<()>().await;
|
std::future::pending::<()>().await;
|
||||||
unreachable!("Pending cannot resolve")
|
unreachable!("Pending cannot resolve")
|
||||||
@@ -164,4 +179,7 @@ pub enum BuilderUpdate {
|
|||||||
Ready {
|
Ready {
|
||||||
results: Vec<BuildResult>,
|
results: Vec<BuildResult>,
|
||||||
},
|
},
|
||||||
|
ProcessExited {
|
||||||
|
status: Option<std::process::ExitStatus>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::builder::{Stage, UpdateBuildProgress, UpdateStage};
|
use crate::builder::{Stage, UpdateBuildProgress, UpdateStage};
|
||||||
use crate::cli::serve::Serve;
|
use crate::cli::serve::Serve;
|
||||||
use crate::dioxus_crate::DioxusCrate;
|
use crate::dioxus_crate::DioxusCrate;
|
||||||
|
use crate::tracer::CLILogControl;
|
||||||
use crate::Result;
|
use crate::Result;
|
||||||
use dioxus_cli_config::Platform;
|
use dioxus_cli_config::Platform;
|
||||||
use tokio::task::yield_now;
|
use tokio::task::yield_now;
|
||||||
@@ -45,7 +46,11 @@ use watcher::*;
|
|||||||
/// - Consume logs from the wasm for web/fullstack
|
/// - Consume logs from the wasm for web/fullstack
|
||||||
/// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server
|
/// - I want us to be able to detect a `server_fn` in the project and then upgrade from a static server
|
||||||
/// to a dynamic one on the fly.
|
/// to a dynamic one on the fly.
|
||||||
pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
pub async fn serve_all(
|
||||||
|
serve: Serve,
|
||||||
|
dioxus_crate: DioxusCrate,
|
||||||
|
log_control: CLILogControl,
|
||||||
|
) -> Result<()> {
|
||||||
let mut builder = Builder::new(&dioxus_crate, &serve);
|
let mut builder = Builder::new(&dioxus_crate, &serve);
|
||||||
|
|
||||||
// Start the first build
|
// Start the first build
|
||||||
@@ -53,7 +58,7 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
|||||||
|
|
||||||
let mut server = Server::start(&serve, &dioxus_crate);
|
let mut server = Server::start(&serve, &dioxus_crate);
|
||||||
let mut watcher = Watcher::start(&serve, &dioxus_crate);
|
let mut watcher = Watcher::start(&serve, &dioxus_crate);
|
||||||
let mut screen = Output::start(&serve).expect("Failed to open terminal logger");
|
let mut screen = Output::start(&serve, log_control).expect("Failed to open terminal logger");
|
||||||
|
|
||||||
let is_hot_reload = serve.server_arguments.hot_reload.unwrap_or(true);
|
let is_hot_reload = serve.server_arguments.hot_reload.unwrap_or(true);
|
||||||
|
|
||||||
@@ -143,6 +148,15 @@ pub async fn serve_all(serve: Serve, dioxus_crate: DioxusCrate) -> Result<()> {
|
|||||||
// And then finally tell the server to reload
|
// And then finally tell the server to reload
|
||||||
server.send_reload_command().await;
|
server.send_reload_command().await;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// If the process exited *cleanly*, we can exit
|
||||||
|
Ok(BuilderUpdate::ProcessExited { status, ..}) => {
|
||||||
|
if let Some(status) = status {
|
||||||
|
if status.success() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
server.send_build_error(err).await;
|
server.send_build_error(err).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
builder::{BuildMessage, MessageType, Stage, UpdateBuildProgress},
|
builder::{BuildMessage, MessageSource, MessageType, Stage, UpdateBuildProgress},
|
||||||
dioxus_crate::DioxusCrate,
|
dioxus_crate::DioxusCrate,
|
||||||
|
tracer::CLILogControl,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
builder::{BuildResult, UpdateStage},
|
builder::{BuildResult, UpdateStage},
|
||||||
@@ -23,6 +24,7 @@ use std::{
|
|||||||
io::{self, stdout},
|
io::{self, stdout},
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::atomic::Ordering,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
@@ -55,6 +57,7 @@ impl BuildProgress {
|
|||||||
|
|
||||||
pub struct Output {
|
pub struct Output {
|
||||||
term: Rc<RefCell<Option<TerminalBackend>>>,
|
term: Rc<RefCell<Option<TerminalBackend>>>,
|
||||||
|
log_control: CLILogControl,
|
||||||
|
|
||||||
// optional since when there's no tty there's no eventstream to read from - just stdin
|
// optional since when there's no tty there's no eventstream to read from - just stdin
|
||||||
events: Option<EventStream>,
|
events: Option<EventStream>,
|
||||||
@@ -88,12 +91,13 @@ enum Tab {
|
|||||||
type TerminalBackend = Terminal<CrosstermBackend<io::Stdout>>;
|
type TerminalBackend = Terminal<CrosstermBackend<io::Stdout>>;
|
||||||
|
|
||||||
impl Output {
|
impl Output {
|
||||||
pub fn start(cfg: &Serve) -> io::Result<Self> {
|
pub fn start(cfg: &Serve, log_control: CLILogControl) -> io::Result<Self> {
|
||||||
let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true);
|
let interactive = std::io::stdout().is_tty() && cfg.interactive.unwrap_or(true);
|
||||||
|
|
||||||
let mut events = None;
|
let mut events = None;
|
||||||
|
|
||||||
if interactive {
|
if interactive {
|
||||||
|
log_control.tui_enabled.store(true, Ordering::SeqCst);
|
||||||
enable_raw_mode()?;
|
enable_raw_mode()?;
|
||||||
stdout().execute(EnterAlternateScreen)?;
|
stdout().execute(EnterAlternateScreen)?;
|
||||||
|
|
||||||
@@ -138,6 +142,7 @@ impl Output {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
term: Rc::new(RefCell::new(term)),
|
term: Rc::new(RefCell::new(term)),
|
||||||
|
log_control,
|
||||||
events,
|
events,
|
||||||
_rustc_version,
|
_rustc_version,
|
||||||
_rustc_nightly,
|
_rustc_nightly,
|
||||||
@@ -176,8 +181,8 @@ impl Output {
|
|||||||
let has_running_apps = !self.running_apps.is_empty();
|
let has_running_apps = !self.running_apps.is_empty();
|
||||||
let next_stdout = self.running_apps.values_mut().map(|app| {
|
let next_stdout = self.running_apps.values_mut().map(|app| {
|
||||||
let future = async move {
|
let future = async move {
|
||||||
let (stdout, stderr) = match &mut app.stdout {
|
let (stdout, stderr) = match &mut app.output {
|
||||||
Some(stdout) => (stdout.stdout.next_line(), stdout.stderr.next_line()),
|
Some(out) => (out.stdout.next_line(), out.stderr.next_line()),
|
||||||
None => return futures_util::future::pending().await,
|
None => return futures_util::future::pending().await,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -199,29 +204,39 @@ impl Output {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let animation_timeout = tokio::time::sleep(Duration::from_millis(300));
|
let animation_timeout = tokio::time::sleep(Duration::from_millis(300));
|
||||||
|
let tui_log_rx = &mut self.log_control.tui_rx;
|
||||||
|
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
(platform, stdout, stderr) = next_stdout => {
|
(platform, stdout, stderr) = next_stdout => {
|
||||||
if let Some(stdout) = stdout {
|
if let Some(stdout) = stdout {
|
||||||
self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stdout_line.push_str(&stdout);
|
self.running_apps.get_mut(&platform).unwrap().output.as_mut().unwrap().stdout_line.push_str(&stdout);
|
||||||
self.push_log(platform, BuildMessage {
|
self.push_log(platform, BuildMessage {
|
||||||
level: Level::INFO,
|
level: Level::INFO,
|
||||||
message: MessageType::Text(stdout),
|
message: MessageType::Text(stdout),
|
||||||
source: Some("app".to_string()),
|
source: MessageSource::App,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if let Some(stderr) = stderr {
|
if let Some(stderr) = stderr {
|
||||||
self.set_tab(Tab::BuildLog);
|
self.set_tab(Tab::BuildLog);
|
||||||
|
|
||||||
self.running_apps.get_mut(&platform).unwrap().stdout.as_mut().unwrap().stderr_line.push_str(&stderr);
|
self.running_apps.get_mut(&platform).unwrap().output.as_mut().unwrap().stderr_line.push_str(&stderr);
|
||||||
self.build_progress.build_logs.get_mut(&platform).unwrap().messages.push(BuildMessage {
|
self.build_progress.build_logs.get_mut(&platform).unwrap().messages.push(BuildMessage {
|
||||||
level: Level::ERROR,
|
level: Level::ERROR,
|
||||||
message: MessageType::Text(stderr),
|
message: MessageType::Text(stderr),
|
||||||
source: Some("app".to_string()),
|
source: MessageSource::App,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Handle internal CLI tracing logs.
|
||||||
|
Some(log) = tui_log_rx.next() => {
|
||||||
|
self.push_log(self.platform, BuildMessage {
|
||||||
|
level: Level::INFO,
|
||||||
|
message: MessageType::Text(log),
|
||||||
|
source: MessageSource::Dev,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
event = user_input => {
|
event = user_input => {
|
||||||
if self.handle_events(event.unwrap().unwrap()).await? {
|
if self.handle_events(event.unwrap().unwrap()).await? {
|
||||||
return Ok(true)
|
return Ok(true)
|
||||||
@@ -238,6 +253,7 @@ impl Output {
|
|||||||
pub fn shutdown(&mut self) -> io::Result<()> {
|
pub fn shutdown(&mut self) -> io::Result<()> {
|
||||||
// if we're a tty then we need to disable the raw mode
|
// if we're a tty then we need to disable the raw mode
|
||||||
if self.interactive {
|
if self.interactive {
|
||||||
|
self.log_control.tui_enabled.store(false, Ordering::SeqCst);
|
||||||
disable_raw_mode()?;
|
disable_raw_mode()?;
|
||||||
stdout().execute(LeaveAlternateScreen)?;
|
stdout().execute(LeaveAlternateScreen)?;
|
||||||
self.drain_print_logs();
|
self.drain_print_logs();
|
||||||
@@ -366,7 +382,7 @@ impl Output {
|
|||||||
// we need to translate its styling into our own
|
// we need to translate its styling into our own
|
||||||
messages.first().unwrap_or(&String::new()).clone(),
|
messages.first().unwrap_or(&String::new()).clone(),
|
||||||
),
|
),
|
||||||
source: Some("app".to_string()),
|
source: MessageSource::App,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -375,8 +391,8 @@ impl Output {
|
|||||||
platform,
|
platform,
|
||||||
BuildMessage {
|
BuildMessage {
|
||||||
level: Level::ERROR,
|
level: Level::ERROR,
|
||||||
source: Some("app".to_string()),
|
source: MessageSource::Dev,
|
||||||
message: MessageType::Text(format!("Error parsing message: {err}")),
|
message: MessageType::Text(format!("Error parsing app message: {err}")),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -401,9 +417,12 @@ impl Output {
|
|||||||
pub fn push_log(&mut self, platform: Platform, message: BuildMessage) {
|
pub fn push_log(&mut self, platform: Platform, message: BuildMessage) {
|
||||||
let snapped = self.is_snapped(platform);
|
let snapped = self.is_snapped(platform);
|
||||||
|
|
||||||
if let Some(build) = self.build_progress.build_logs.get_mut(&platform) {
|
self.build_progress
|
||||||
build.stdout_logs.push(message);
|
.build_logs
|
||||||
}
|
.entry(platform)
|
||||||
|
.or_default()
|
||||||
|
.stdout_logs
|
||||||
|
.push(message);
|
||||||
|
|
||||||
if snapped {
|
if snapped {
|
||||||
self.scroll_to_bottom();
|
self.scroll_to_bottom();
|
||||||
@@ -453,7 +472,10 @@ impl Output {
|
|||||||
stderr_line: String::new(),
|
stderr_line: String::new(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let app = RunningApp { result, stdout };
|
let app = RunningApp {
|
||||||
|
result,
|
||||||
|
output: stdout,
|
||||||
|
};
|
||||||
|
|
||||||
self.running_apps.insert(platform, app);
|
self.running_apps.insert(platform, app);
|
||||||
|
|
||||||
@@ -639,7 +661,16 @@ impl Output {
|
|||||||
for line in line.lines() {
|
for line in line.lines() {
|
||||||
let text = line.into_text().unwrap_or_default();
|
let text = line.into_text().unwrap_or_default();
|
||||||
for line in text.lines {
|
for line in text.lines {
|
||||||
let mut out_line = vec![Span::from("[app] ").dark_gray()];
|
let source = format!("[{}] ", span.source);
|
||||||
|
|
||||||
|
let msg_span = Span::from(source);
|
||||||
|
let msg_span = match span.source {
|
||||||
|
MessageSource::App => msg_span.light_blue(),
|
||||||
|
MessageSource::Dev => msg_span.dark_gray(),
|
||||||
|
MessageSource::Build => msg_span.light_yellow(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out_line = vec![msg_span];
|
||||||
for span in line.spans {
|
for span in line.spans {
|
||||||
out_line.push(span);
|
out_line.push(span);
|
||||||
}
|
}
|
||||||
@@ -855,7 +886,7 @@ async fn rustc_version() -> String {
|
|||||||
|
|
||||||
pub struct RunningApp {
|
pub struct RunningApp {
|
||||||
result: BuildResult,
|
result: BuildResult,
|
||||||
stdout: Option<RunningAppOutput>,
|
output: Option<RunningAppOutput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RunningAppOutput {
|
struct RunningAppOutput {
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
use futures_channel::mpsc::{unbounded, UnboundedReceiver, UnboundedSender};
|
||||||
|
use std::{
|
||||||
|
env, io,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc, Mutex,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use tracing_subscriber::{prelude::*, EnvFilter};
|
||||||
|
|
||||||
|
const LOG_ENV: &str = "DIOXUS_LOG";
|
||||||
|
|
||||||
|
/// Build tracing infrastructure.
|
||||||
|
pub fn build_tracing() -> CLILogControl {
|
||||||
|
// If {LOG_ENV} is set, default to env, otherwise filter to cli
|
||||||
|
// and manganis warnings and errors from other crates
|
||||||
|
let mut filter = EnvFilter::new("error,dx=info,dioxus-cli=info,manganis-cli-support=info");
|
||||||
|
if env::var(LOG_ENV).is_ok() {
|
||||||
|
filter = EnvFilter::from_env(LOG_ENV);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create writer controller and custom writer.
|
||||||
|
let (tui_tx, tui_rx) = unbounded();
|
||||||
|
let tui_enabled = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let writer_control = CLILogControl {
|
||||||
|
tui_rx,
|
||||||
|
tui_enabled: tui_enabled.clone(),
|
||||||
|
};
|
||||||
|
let cli_writer = Mutex::new(CLIWriter::new(tui_enabled, tui_tx));
|
||||||
|
|
||||||
|
// Build tracing
|
||||||
|
let fmt_layer = tracing_subscriber::fmt::layer()
|
||||||
|
.with_writer(cli_writer)
|
||||||
|
.with_filter(filter);
|
||||||
|
let sub = tracing_subscriber::registry().with(fmt_layer);
|
||||||
|
|
||||||
|
#[cfg(feature = "tokio-console")]
|
||||||
|
let sub = sub.with(console_subscriber::spawn());
|
||||||
|
|
||||||
|
sub.init();
|
||||||
|
|
||||||
|
writer_control
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contains the sync primitives to control the CLIWriter.
|
||||||
|
pub struct CLILogControl {
|
||||||
|
pub tui_rx: UnboundedReceiver<String>,
|
||||||
|
pub tui_enabled: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the CLI's custom tracing writer for conditionally writing logs between outputs.
|
||||||
|
pub struct CLIWriter {
|
||||||
|
stdout: io::Stdout,
|
||||||
|
tui_tx: UnboundedSender<String>,
|
||||||
|
tui_enabled: Arc<AtomicBool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CLIWriter {
|
||||||
|
/// Create a new CLIWriter with required sync primitives for conditionally routing logs.
|
||||||
|
pub fn new(tui_enabled: Arc<AtomicBool>, tui_tx: UnboundedSender<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
stdout: io::stdout(),
|
||||||
|
tui_tx,
|
||||||
|
tui_enabled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement a conditional writer so that logs are routed to the appropriate place.
|
||||||
|
impl io::Write for CLIWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
if self.tui_enabled.load(Ordering::SeqCst) {
|
||||||
|
let len = buf.len();
|
||||||
|
|
||||||
|
let as_string = String::from_utf8(buf.to_vec())
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
self.tui_tx
|
||||||
|
.unbounded_send(as_string)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e))?;
|
||||||
|
|
||||||
|
Ok(len)
|
||||||
|
} else {
|
||||||
|
self.stdout.write(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
if !self.tui_enabled.load(Ordering::SeqCst) {
|
||||||
|
self.stdout.flush()
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user