Skip to content
Open
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
15 changes: 10 additions & 5 deletions apps/framework-cli/src/cli/display/infrastructure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,15 @@ use crate::framework::core::{
},
plan::InfraPlan,
};
use crate::utilities::constants::NO_ANSI;
use crossterm::{execute, style::Print};
use std::sync::atomic::Ordering;
use tracing::info;

/// Create the detail indentation string at compile time
/// Computed from ACTION_WIDTH (15) + 3 spaces:
/// - ACTION_WIDTH spaces for the action column
/// - 1 space after the action symbol (e.g., "+", "-", "~")
/// - 1 space after the action symbol (e.g., "+", "-", "~")
/// - 2 spaces for additional indentation of detail lines
/// Total: 18 spaces for proper alignment
const DETAIL_INDENT: &str = {
Expand Down Expand Up @@ -267,7 +269,8 @@ fn format_table_display(
/// ```
pub fn infra_added(message: &str) {
let styled_text = StyledText::from_str("+ ").green();
write_styled_line(&styled_text, message).expect("failed to write message to terminal");
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
write_styled_line(&styled_text, message, no_ansi).expect("failed to write message to terminal");
info!("+ {}", message.trim());
}

Expand Down Expand Up @@ -305,7 +308,8 @@ pub fn infra_added_detailed(title: &str, details: &[String]) {
/// ```
pub fn infra_removed(message: &str) {
let styled_text = StyledText::from_str("- ").red();
write_styled_line(&styled_text, message).expect("failed to write message to terminal");
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
write_styled_line(&styled_text, message, no_ansi).expect("failed to write message to terminal");
info!("- {}", message.trim());
}

Expand Down Expand Up @@ -343,7 +347,8 @@ pub fn infra_removed_detailed(title: &str, details: &[String]) {
/// ```
pub fn infra_updated(message: &str) {
let styled_text = StyledText::from_str("~ ").yellow();
write_styled_line(&styled_text, message).expect("failed to write message to terminal");
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
write_styled_line(&styled_text, message, no_ansi).expect("failed to write message to terminal");
info!("~ {}", message.trim());
}

Expand Down Expand Up @@ -388,7 +393,7 @@ pub fn infra_updated_detailed(title: &str, details: &[String]) {
/// # Change Types Handled
///
/// - **Table Changes**: Added, removed, or updated database tables
/// - **View Changes**: Added, removed, or updated database views
/// - **View Changes**: Added, removed, or updated database views
/// - **SQL Resource Changes**: Added, removed, or updated SQL resources
///
/// # Examples
Expand Down
47 changes: 35 additions & 12 deletions apps/framework-cli/src/cli/display/message_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
message::{Message, MessageType},
terminal::{write_styled_line, StyledText},
};
use crate::utilities::constants::NO_ANSI;
use std::sync::atomic::Ordering;
use tracing::info;

/// Displays a message about a batch database insertion.
Expand All @@ -31,7 +33,8 @@
action: "[DB]".to_string(),
details: format!("{count} row(s) successfully written to DB table ({table_name})"),
};
let _ = show_message_impl(MessageType::Info, message, true);
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
let _ = show_message_impl(MessageType::Info, message, true, no_ansi);
}

/// Wrapper function for the show_message macro.
Expand All @@ -56,7 +59,8 @@
/// );
/// ```
pub fn show_message_wrapper(message_type: MessageType, message: Message) {
let _ = show_message_impl(message_type, message, false);
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
let _ = show_message_impl(message_type, message, false, no_ansi);
}

/// Internal implementation for the show_message macro.
Expand All @@ -69,6 +73,7 @@
/// * `message_type` - The type of message determining visual style
/// * `message` - The message to display
/// * `should_log` - Whether to log the message
/// * `no_ansi` - If true, disable ANSI color codes and formatting
///
/// # Returns
///
Expand All @@ -77,6 +82,7 @@
message_type: MessageType,
message: Message,
should_log: bool,
no_ansi: bool,
) -> std::io::Result<()> {
let action = message.action.clone();
let details = message.details.clone();
Expand All @@ -90,7 +96,7 @@
};

// Write styled prefix and details in one line
write_styled_line(&styled_prefix, &details)?;
write_styled_line(&styled_prefix, &details, no_ansi)?;

if should_log {
let log_action = action.replace('\n', " ");
Expand All @@ -105,7 +111,8 @@
///
/// This macro provides a unified interface for displaying messages with consistent
/// formatting and optional logging. It handles the styling based on message type
/// and ensures proper terminal output.
/// and ensures proper terminal output. ANSI color codes are automatically disabled
/// when the `no_ansi` setting is enabled in logger configuration.
///
/// # Syntax
///
Expand Down Expand Up @@ -137,15 +144,31 @@
/// ```
#[macro_export]
macro_rules! show_message {
($message_type:expr, $message:expr) => {
$crate::cli::display::message_display::show_message_impl($message_type, $message, true)
.expect("failed to write message to terminal");
};
($message_type:expr, $message:expr) => {{
use std::sync::atomic::Ordering;
use $crate::utilities::constants::NO_ANSI;
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
$crate::cli::display::message_display::show_message_impl(
$message_type,
$message,
true,
no_ansi,
)
.expect("failed to write message to terminal");
}};

($message_type:expr, $message:expr, $no_log:expr) => {
$crate::cli::display::message_display::show_message_impl($message_type, $message, false)
.expect("failed to write message to terminal");
};
($message_type:expr, $message:expr, $no_log:expr) => {{
use std::sync::atomic::Ordering;
use $crate::utilities::constants::NO_ANSI;
let no_ansi = NO_ANSI.load(Ordering::Relaxed);
$crate::cli::display::message_display::show_message_impl(
$message_type,
$message,
false,
no_ansi,
)
.expect("failed to write message to terminal");
}};
}

#[cfg(test)]
Expand All @@ -155,35 +178,35 @@
#[test]
fn test_show_message_impl_info() {
let message = Message::new("Test".to_string(), "Test message".to_string());
let result = show_message_impl(MessageType::Info, message, false);

Check failure on line 181 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 181 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 181 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

#[test]
fn test_show_message_impl_success() {
let message = Message::new("Success".to_string(), "Operation completed".to_string());
let result = show_message_impl(MessageType::Success, message, false);

Check failure on line 188 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 188 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 188 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

#[test]
fn test_show_message_impl_error() {
let message = Message::new("Error".to_string(), "Something went wrong".to_string());
let result = show_message_impl(MessageType::Error, message, false);

Check failure on line 195 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 195 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 195 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

#[test]
fn test_show_message_impl_highlight() {
let message = Message::new("Important".to_string(), "Pay attention to this".to_string());
let result = show_message_impl(MessageType::Highlight, message, false);

Check failure on line 202 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 202 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 202 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

#[test]
fn test_show_message_impl_with_logging() {
let message = Message::new("Log".to_string(), "This should be logged".to_string());
let result = show_message_impl(MessageType::Info, message, true);

Check failure on line 209 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 209 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 209 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

Expand All @@ -193,7 +216,7 @@
"Multi\nLine".to_string(),
"Details\nwith\nnewlines".to_string(),
);
let result = show_message_impl(MessageType::Info, message, false);

Check failure on line 219 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 219 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 219 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

Expand All @@ -203,14 +226,14 @@
"🚀 Deploy".to_string(),
"Successfully deployed 🎉".to_string(),
);
let result = show_message_impl(MessageType::Success, message, false);

Check failure on line 229 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 229 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 229 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

#[test]
fn test_show_message_impl_empty() {
let message = Message::new("".to_string(), "".to_string());
let result = show_message_impl(MessageType::Info, message, false);

Check failure on line 236 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (macos-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 236 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Test CLI (ubuntu-latest)

this function takes 4 arguments but 3 arguments were supplied

Check failure on line 236 in apps/framework-cli/src/cli/display/message_display.rs

View workflow job for this annotation

GitHub Actions / Lints

this function takes 4 arguments but 3 arguments were supplied
assert!(result.is_ok());
}

Expand Down
42 changes: 24 additions & 18 deletions apps/framework-cli/src/cli/display/terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ impl StyledText {
///
/// * `styled_text` - The styled text configuration for the action portion
/// * `message` - The main message content to display
/// * `no_ansi` - If true, disable ANSI color codes and formatting
///
/// # Returns
///
Expand All @@ -202,10 +203,10 @@ impl StyledText {
/// ```rust
/// # use crate::cli::display::terminal::{StyledText, write_styled_line};
/// let styled = StyledText::new("Success".to_string()).green().bold();
/// write_styled_line(&styled, "Operation completed successfully")?;
/// write_styled_line(&styled, "Operation completed successfully", false)?;
/// # Ok::<(), std::io::Error>(())
/// ```
pub fn write_styled_line(styled_text: &StyledText, message: &str) -> IoResult<()> {
pub fn write_styled_line(styled_text: &StyledText, message: &str, no_ansi: bool) -> IoResult<()> {
let mut stdout = stdout();

// Ensure action is exactly ACTION_WIDTH characters, right-aligned
Expand All @@ -221,28 +222,33 @@ pub fn write_styled_line(styled_text: &StyledText, message: &str) -> IoResult<()
};
let padded_action = format!("{truncated_action:>ACTION_WIDTH$}");

// Apply foreground color
if let Some(color) = styled_text.foreground {
execute!(stdout, SetForegroundColor(color))?;
}
// Only apply ANSI styling if not disabled
if !no_ansi {
// Apply foreground color
if let Some(color) = styled_text.foreground {
execute!(stdout, SetForegroundColor(color))?;
}

// Apply background color
if let Some(color) = styled_text.background {
execute!(stdout, SetBackgroundColor(color))?;
}
// Apply background color
if let Some(color) = styled_text.background {
execute!(stdout, SetBackgroundColor(color))?;
}

// Apply bold
if styled_text.bold {
execute!(stdout, SetAttribute(Attribute::Bold))?;
// Apply bold
if styled_text.bold {
execute!(stdout, SetAttribute(Attribute::Bold))?;
}
}

// Write the styled, right-aligned action text
execute!(stdout, Print(&padded_action))?;

// Reset styling before writing the message
execute!(stdout, ResetColor)?;
if styled_text.bold {
execute!(stdout, SetAttribute(Attribute::Reset))?;
// Reset styling before writing the message (only if ANSI was applied)
if !no_ansi {
execute!(stdout, ResetColor)?;
if styled_text.bold {
execute!(stdout, SetAttribute(Attribute::Reset))?;
}
}

// Write separator and message
Expand Down Expand Up @@ -331,6 +337,6 @@ mod tests {
let styled = StyledText::from_str("Test").green().bold();
// This test mainly ensures the function signature is correct
// and doesn't panic during compilation
let _ = write_styled_line(&styled, "test message");
let _ = write_styled_line(&styled, "test message", false);
}
}
24 changes: 21 additions & 3 deletions apps/framework-cli/src/cli/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, Layer};

use crate::utilities::constants::{CONTEXT, CTX_SESSION_ID};
use crate::utilities::constants::{CONTEXT, CTX_SESSION_ID, NO_ANSI};
use std::sync::atomic::Ordering;

use super::settings::user_directory;

Expand Down Expand Up @@ -144,6 +145,9 @@ pub struct LoggerSettings {

#[serde(default = "default_use_tracing_format")]
pub use_tracing_format: bool,

#[serde(default = "default_no_ansi")]
pub no_ansi: bool,
}

fn default_log_file() -> String {
Expand Down Expand Up @@ -173,6 +177,10 @@ fn default_use_tracing_format() -> bool {
.unwrap_or(false)
}

fn default_no_ansi() -> bool {
false // ANSI colors enabled by default
}

impl Default for LoggerSettings {
fn default() -> Self {
LoggerSettings {
Expand All @@ -182,6 +190,7 @@ impl Default for LoggerSettings {
format: default_log_format(),
include_session_id: default_include_session_id(),
use_tracing_format: default_use_tracing_format(),
no_ansi: default_no_ansi(),
}
}
}
Expand Down Expand Up @@ -453,6 +462,9 @@ fn create_rolling_file_appender(date_format: &str) -> DateBasedWriter {
pub fn setup_logging(settings: &LoggerSettings) {
clean_old_logs();

// Set global NO_ANSI flag for terminal display functions
NO_ANSI.store(settings.no_ansi, Ordering::Relaxed);

let session_id = CONTEXT.get(CTX_SESSION_ID).unwrap();

// Setup logging based on format type
Expand All @@ -469,11 +481,16 @@ fn setup_modern_format(settings: &LoggerSettings) {
let env_filter = EnvFilter::try_from_default_env()
.unwrap_or_else(|_| EnvFilter::new(settings.level.to_tracing_level().to_string()));

// When no_ansi is false, ANSI is enabled (true)
// When no_ansi is true, ANSI is disabled (false)
let ansi_enabled = !settings.no_ansi;

if settings.stdout {
let format_layer = tracing_subscriber::fmt::layer()
.with_writer(std::io::stdout)
.with_target(true)
.with_level(true);
.with_level(true)
.with_ansi(ansi_enabled);

if settings.format == LogFormat::Json {
tracing_subscriber::registry()
Expand All @@ -491,7 +508,8 @@ fn setup_modern_format(settings: &LoggerSettings) {
let format_layer = tracing_subscriber::fmt::layer()
.with_writer(file_appender)
.with_target(true)
.with_level(true);
.with_level(true)
.with_ansi(ansi_enabled);

if settings.format == LogFormat::Json {
tracing_subscriber::registry()
Expand Down
8 changes: 7 additions & 1 deletion apps/framework-cli/src/utilities/constants.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use lazy_static::lazy_static;
use std::collections::HashMap;
use std::sync::atomic::AtomicBool;
use uuid::Uuid;

pub const CLI_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -82,8 +83,13 @@ lazy_static! {
};
}

/// Global flag to disable ANSI colors in terminal output
/// When true, ANSI escape codes are disabled in terminal display functions
/// This is set once at startup based on logger configuration
pub static NO_ANSI: AtomicBool = AtomicBool::new(false);

pub const README_PREFIX: &str = r#"
This is a [MooseJs](https://www.moosejs.com/) project bootstrapped with the
This is a [MooseJs](https://www.moosejs.com/) project bootstrapped with the
[`Moose CLI`](https://github.com/514-labs/moose/tree/main/apps/framework-cli).

"#;
Expand Down
Loading