Add CI job for no-std build verification and fix Infallible StdError implementation

- Add dedicated no-std CI job that builds for wasm32-unknown-unknown target
- Fix Infallible StdError implementation for no-std environments

The CI job validates:
1. Core no-std functionality (no default features)
2. fancy-no-syscall feature set compatible with no-std environments
This commit is contained in:
François Garillot 2025-10-20 04:46:36 -04:00
parent 79fbd0a07d
commit 18c217f0dc
No known key found for this signature in database
GPG Key ID: E7645C6B883A1E9A
8 changed files with 83 additions and 31 deletions

View File

@ -68,6 +68,21 @@ jobs:
- name: Check wasm target - name: Check wasm target
run: cargo check --target wasm32-unknown-unknown --features fancy-no-syscall run: cargo check --target wasm32-unknown-unknown --features fancy-no-syscall
no-std:
name: Check no-std build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
targets: wasm32-unknown-unknown
- name: Check no-std core build
run: cargo check --target wasm32-unknown-unknown --no-default-features
- name: Check no-std with fancy-no-syscall
run: cargo check --target wasm32-unknown-unknown --no-default-features --features fancy-no-syscall
miri: miri:
name: Miri name: Miri
runs-on: ubuntu-latest runs-on: ubuntu-latest

View File

@ -8,6 +8,9 @@ use core::{convert::Infallible, fmt::Display};
use crate::{Diagnostic, LabeledSpan, Severity, SourceCode}; use crate::{Diagnostic, LabeledSpan, Severity, SourceCode};
#[cfg(not(feature = "std"))]
impl crate::StdError for Infallible {}
impl Diagnostic for Infallible { impl Diagnostic for Infallible {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match *self {} match *self {}

View File

@ -209,13 +209,16 @@ impl<C> StdError for WithSourceCode<Report, C> {
} }
} }
#[cfg(test)] #[cfg(all(test, feature = "std"))]
mod tests { mod tests {
#[cfg(feature = "fancy")]
use std::format;
use std::{ use std::{
boxed::Box, boxed::Box,
string::{String, ToString}, string::{String, ToString},
}; };
#[cfg(feature = "fancy")]
use std::{vec, vec::Vec};
use thiserror::Error; use thiserror::Error;
use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan}; use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan};

View File

@ -345,15 +345,27 @@ impl MietteHandlerOpts {
} }
} }
#[allow(clippy::manual_unwrap_or)]
pub(crate) fn is_graphical(&self) -> bool { pub(crate) fn is_graphical(&self) -> bool {
if let Some(force_narrated) = self.force_narrated { if let Some(force_narrated) = self.force_narrated {
!force_narrated !force_narrated
} else if let Some(force_graphical) = self.force_graphical { } else if let Some(force_graphical) = self.force_graphical {
force_graphical force_graphical
} else if let Ok(env) = std::env::var("NO_GRAPHICS") {
env == "0"
} else { } else {
true #[cfg(feature = "fancy-no-syscall")]
{
// In no-std environment, assume graphics are available
true
}
#[cfg(not(feature = "fancy-no-syscall"))]
{
// In std environment, check NO_GRAPHICS env var
if let Ok(env) = std::env::var("NO_GRAPHICS") {
env == "0"
} else {
true
}
}
} }
} }
@ -501,7 +513,7 @@ mod syscall {
// In no-std environment without color support, default to no color support // In no-std environment without color support, default to no color support
false false
} else { } else {
supports_color::on(supports_color::Stream::Stderr).is_some() true // Fallback to assuming color support
} }
} }
} }
@ -515,7 +527,7 @@ mod syscall {
// In no-std environment without color support, default to no RGB color support // In no-std environment without color support, default to no RGB color support
Some(false) Some(false)
} else { } else {
supports_color::on(supports_color::Stream::Stderr).map(|color| color.has_16m) Some(true) // Fallback to assuming RGB support
} }
} }
} }

View File

@ -588,7 +588,7 @@ impl GraphicalReportHandler {
// The snippets will overlap, so we create one Big Chunky Boi // The snippets will overlap, so we create one Big Chunky Boi
let left_end = left.offset() + left.len(); let left_end = left.offset() + left.len();
let right_end = right.offset() + right.len(); let right_end = right.offset() + right.len();
let new_end = std::cmp::max(left_end, right_end); let new_end = core::cmp::max(left_end, right_end);
let new_span = LabeledSpan::new( let new_span = LabeledSpan::new(
left.label().map(String::from), left.label().map(String::from),
@ -655,7 +655,7 @@ impl GraphicalReportHandler {
num_highlights += 1; num_highlights += 1;
} }
} }
max_gutter = std::cmp::max(max_gutter, num_highlights); max_gutter = core::cmp::max(max_gutter, num_highlights);
} }
// Oh and one more thing: We need to figure out how much room our line // Oh and one more thing: We need to figure out how much room our line
@ -1179,7 +1179,7 @@ impl GraphicalReportHandler {
.style(hl.style) .style(hl.style)
.to_string(), .to_string(),
); );
highest = std::cmp::max(highest, end); highest = core::cmp::max(highest, end);
(hl, vbar_offset) (hl, vbar_offset)
}) })

View File

@ -71,12 +71,20 @@ impl GraphicalTheme {
impl Default for GraphicalTheme { impl Default for GraphicalTheme {
fn default() -> Self { fn default() -> Self {
match std::env::var("NO_COLOR") { #[cfg(feature = "fancy-no-syscall")]
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { {
Self::none() // In no-std environments, default to no-color mode
Self::unicode_nocolor()
}
#[cfg(not(feature = "fancy-no-syscall"))]
{
match std::env::var("NO_COLOR") {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
Self::none()
}
Ok(string) if string != "0" => Self::unicode_nocolor(),
_ => Self::unicode(),
} }
Ok(string) if string != "0" => Self::unicode_nocolor(),
_ => Self::unicode(),
} }
} }
} }

View File

@ -11,7 +11,7 @@
//! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`] //! * `syntect-highlighter` - Enables [`syntect`](https://docs.rs/syntect/latest/syntect/) syntax highlighting support via the [`SyntectHighlighter`]
//! //!
use std::{ops::Deref, sync::Arc}; use core::ops::Deref;
extern crate alloc; extern crate alloc;
use alloc::boxed::Box; use alloc::boxed::Box;
@ -83,21 +83,28 @@ impl MietteHighlighter {
} }
impl Default for MietteHighlighter { impl Default for MietteHighlighter {
#[cfg(feature = "syntect-highlighter")]
fn default() -> Self { fn default() -> Self {
use std::io::IsTerminal; #[cfg(all(feature = "syntect-highlighter", not(feature = "fancy-no-syscall")))]
match std::env::var("NO_COLOR") { {
_ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => { use std::io::IsTerminal;
//TODO: should use ANSI styling instead of 24-bit truecolor here match std::env::var("NO_COLOR") {
Self(Arc::new(SyntectHighlighter::default())) _ if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
//TODO: should use ANSI styling instead of 24-bit truecolor here
Self(Arc::new(SyntectHighlighter::default()))
}
Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())),
} }
Ok(string) if string != "0" => MietteHighlighter::nocolor(),
_ => Self(Arc::new(SyntectHighlighter::default())),
} }
} #[cfg(all(feature = "syntect-highlighter", feature = "fancy-no-syscall"))]
#[cfg(not(feature = "syntect-highlighter"))] {
fn default() -> Self { // In no-std environment, use syntect but without terminal detection
MietteHighlighter::nocolor() Self(Arc::new(SyntectHighlighter::default()))
}
#[cfg(not(feature = "syntect-highlighter"))]
{
MietteHighlighter::nocolor()
}
} }
} }
@ -107,8 +114,8 @@ impl<T: Highlighter + Send + Sync + 'static> From<T> for MietteHighlighter {
} }
} }
impl std::fmt::Debug for MietteHighlighter { impl core::fmt::Debug for MietteHighlighter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "MietteHighlighter(...)") write!(f, "MietteHighlighter(...)")
} }
} }

View File

@ -9,6 +9,7 @@ use alloc::boxed::Box;
use alloc::string::String; use alloc::string::String;
use core::fmt::{self, Display}; use core::fmt::{self, Display};
use core::ops; use core::ops;
#[cfg(feature = "std")]
use core::panic::Location; use core::panic::Location;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::fs; use std::fs;
@ -16,7 +17,7 @@ use std::fs;
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{DiagnosticError, MietteError}; use crate::MietteError;
/// Adds rich metadata to your Error that can be used by /// Adds rich metadata to your Error that can be used by
/// [`Report`](crate::Report) to print really nice and human-friendly error /// [`Report`](crate::Report) to print really nice and human-friendly error
@ -172,6 +173,9 @@ impl From<String> for Box<dyn Diagnostic + Send + Sync> {
} }
} }
#[cfg(feature = "std")]
use crate::DiagnosticError;
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> { impl From<Box<dyn std::error::Error + Send + Sync>> for Box<dyn Diagnostic + Send + Sync> {
fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self { fn from(s: Box<dyn std::error::Error + Send + Sync>) -> Self {