-
-
Notifications
You must be signed in to change notification settings - Fork 145
feat: no-std support #455
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
base: main
Are you sure you want to change the base?
feat: no-std support #455
Conversation
This commit finishes full no_std support to miette.
…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
| } | ||
| } | ||
|
|
||
| #[cfg(not(feature = "std"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curiosity: why not a different struct, instead of using one in two configs.
If the answer is "won't compile", then this is infectious / not additive, no?
src/eyreish/context.rs
Outdated
| } | ||
|
|
||
| impl Diag for Report { | ||
| #[cfg_attr(track_caller, track_caller)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is track_caller needed for the no_std implementation, or is that a separate change?
src/eyreish/into_diagnostic.rs
Outdated
| use std::{error::Error, fmt::Display}; | ||
| extern crate alloc; | ||
|
|
||
| use crate::StdError as Error; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why re-name StdError to Error here, but not elsewehre?
src/eyreish/kind.rs
Outdated
| // let error = $msg; | ||
| // (&error).miette_kind().new(error) | ||
|
|
||
| #[cfg(not(feature = "std"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can all these repeated declarations live in a single imported prelude instead, or nah?
| let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref(); | ||
| pub(crate) fn capture_handler_with_location( | ||
| error: &(dyn Diagnostic + 'static), | ||
| ) -> Box<dyn ReportHandler> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the Once? can't it just be OnceLock?
I'm having a hard time following what this change is achieving, which means more comments might be needed here. I don't see why two different once's are needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnceLock isn't available in no_std, hence spin::Once
| fn source(&self) -> Option<&(dyn StdError + 'static)> { | ||
| self.0.source() | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking change, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| boxed::Box, | ||
| string::{String, ToString}, | ||
| }; | ||
| #[cfg(feature = "fancy")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't many of these still be alloc:: qualified types instead, in case someone wants to make fancy no-std too in future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point for future-proofing. Right now the fancy features all require std (terminal detection, env vars, etc.) so these std imports are fine. If someone wanted no_std_fancy later they'd need to rework the syscall detection anyway. Left it as-is to keep the diff smaller but happy to change if you feel strongly.
| Self(Arc::new(SyntectHighlighter::default())) | ||
| #[cfg(all(feature = "syntect-highlighter", not(feature = "fancy-no-syscall")))] | ||
| { | ||
| use std::io::IsTerminal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like there's some deduplication that should be done vs src/handlers/theme.rs
It's already pretty complicated and presumably the two should be kept in sync.
Maybe a helper function? Ideally that memo-izes it to avoid many redundant syscalls if necessary
Note: the double or triple negation in the old version was making my head hurt, so I made it exhaustive instead
enum StyleDetection
{
NoColor,
Color
}
fn detect_color_support() ->
{
#[cfg(any(feature = "fancy-no-syscall", not(feature = "std")))]
{
StyleDetection::NoColor
}
#[cfg(all(not(feature = "fancy-no-syscall"), feature = "std"))]
{
match std::env::var("NO_COLOR") {
if !std::io::stdout().is_terminal() || !std::io::stderr().is_terminal() => {
// TODO: address todo by making this StyleDetection::NoColor?
//TODO: should use ANSI styling instead of 24-bit truecolor here
StyleDetection::Color
}
Ok(string) =>
{
if string == "0"
{
// NO_COLOR = "0" means enable color
StyleDetection::Color
}
else
{
// any other value means disable color.
StyleDetection::NoColor
}
}
// This could be NotPresent (color since disablement env var not set) or NotUnicode (hopefully never happens, but we'llt reat it the same)
Err(_) => StyleDetection::Color
}
}
}There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought about this. The two defaults have different return types (GraphicalTheme vs MietteHighlighter) so a shared helper would need callbacks or an enum. The duplicated logic is small (just the NO_COLOR check) and both places already have cfg guards. A helper could work but might add more complexity than it saves. Open to ideas if you see a clean way to do it.
| @@ -1,6 +1,8 @@ | |||
| #![no_std] | |||
| #![deny(missing_docs, missing_debug_implementations, nonstandard_style)] | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is going to be merged, I think at least some of these should probably be enabled?
![deny(clippy::alloc_instead_of_core,clippy::std_instead_of_core,clippy::std_instead_of_alloc)]|
|
||
| let error = MyError { | ||
| source: io::Error::new(io::ErrorKind::Other, "oh no!!!!"), | ||
| source: io::Error::other("oh no!!!!"), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be worth pulling all the io::Error changes out into another PR to keep the diff down, but idk.
Cargo.toml
Outdated
| unicode-width = "0.2.0" | ||
| unicode-width = { version = "0.2.0", default-features = false } | ||
| cfg-if = "1.0.0" | ||
| spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex", "lazy"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't seem like it should be a dependency for std environments. I see the simplicity benefit, but adding a dependency for consumers for whom OnceLock is an option, doesn't seem like it makes sense.
Also, do you need all those features?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spin is optional = true and only activated when not(feature = "std") (see the cfg guards in src/eyreish/mod.rs). Std users don't pull in spin at all. The minimal features (once only) are intentional to keep the dependency small for no_std users who need it.
|
To be clear, my comments are just my opinion - I'm neither the author nor am I a maintainer of this library, I'm just a person who has submitted a few contributions previously and was curious. Hope the feedback is helpful. |
- Fixed compilation errors in no-std mode (TestError Debug derive, ToString imports) - Addressed consistency issues: Option qualification, StdError naming, Borrow impl - Fixed feature flag logic for supports_color function with proper priority ordering - Added conditional compilation for std-dependent features in panic.rs, handler.rs, theme.rs - Made syntect-highlighter feature explicitly require std - Consolidated duplicate Borrow implementations using core::borrow::Borrow
- Add #[allow(unused_assignments)] to test structs/enums with unused fields - Fix Miri test failures caused by derive macro feature interactions - Miri tests now pass: 52 passed, 0 failed, 6 ignored - Issue occurs when fancy feature changes how derive macros process fields - Suppression is appropriate as fields are only unused under specific feature combinations
| #[cfg(feature = "std")] | ||
| use std::boxed::Box; | ||
| #[cfg(feature = "std")] | ||
| use std::{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe make the whole module conditional? I don't think a fake no-op set_panic_hook should be provided when std is not set, it's correct to not have it there and will not break any program that depends on miette with std support feature
- Remove rustc_version build dependency that was no longer needed - Remove nightly detection since nightly cfg flag wasn't used anywhere - Simplify build.rs to only set track_caller cfg flag
- Use core::error::Error directly (MSRV 1.82 supports it) - Remove build.rs (track_caller is always available with MSRV 1.82) - Simplify feature detection in handler.rs syscall module - Add clippy lints to catch std/core/alloc misuse - Remove custom StdError trait, re-export core::error::Error - Add __alloc module for derive macro compatibility - Clean up verbose comments - Remove extern crate alloc from tests (not needed with std) - Fix derive macros to use miette::__alloc paths
- spin is now optional, only needed for no_std (provides Once) - std builds use OnceLock, avoiding unnecessary dependency - Restore unicode-width default features (keeps CJK support)
| Self(Arc::new(SyntectHighlighter::default())) | ||
| #[cfg(all(feature = "syntect-highlighter", not(feature = "fancy-no-syscall")))] | ||
| { | ||
| use std::io::IsTerminal; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thought about this. The two defaults have different return types (GraphicalTheme vs MietteHighlighter) so a shared helper would need callbacks or an enum. The duplicated logic is small (just the NO_COLOR check) and both places already have cfg guards. A helper could work but might add more complexity than it saves. Open to ideas if you see a clean way to do it.
| boxed::Box, | ||
| string::{String, ToString}, | ||
| }; | ||
| #[cfg(feature = "fancy")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair point for future-proofing. Right now the fancy features all require std (terminal detection, env vars, etc.) so these std imports are fine. If someone wanted no_std_fancy later they'd need to rework the syscall detection anyway. Left it as-is to keep the diff smaller but happy to change if you feel strongly.
| let hook = HOOK.get_or_init(|| Box::new(get_default_printer)).as_ref(); | ||
| pub(crate) fn capture_handler_with_location( | ||
| error: &(dyn Diagnostic + 'static), | ||
| ) -> Box<dyn ReportHandler> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OnceLock isn't available in no_std, hence spin::Once
| #[cfg(feature = "syntect-highlighter")] | ||
| { | ||
| use std::io::IsTerminal; | ||
| match std::env::var("NO_COLOR") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The two defaults have different return types (GraphicalTheme vs MietteHighlighter) so a shared helper would need callbacks or an enum. The duplicated logic is small (just the NO_COLOR check) and both places already have cfg guards. A helper could work but might add more complexity than it saves. Open to ideas if you see a clean way to do it.
Cargo.toml
Outdated
| unicode-width = "0.2.0" | ||
| unicode-width = { version = "0.2.0", default-features = false } | ||
| cfg-if = "1.0.0" | ||
| spin = { version = "0.9", default-features = false, features = ["mutex", "spin_mutex", "lazy"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
spin is optional = true and only activated when not(feature = "std") (see the cfg guards in src/eyreish/mod.rs). Std users don't pull in spin at all. The minimal features (once only) are intentional to keep the dependency small for no_std users who need it.
- Make spin a required dependency for no_std hook support (OnceLock unavailable) - Simplify cfg guards: not(std) instead of not(std) && spin - Change clippy alloc/std lints from warn to deny - Remove no_std stub for from_current_location (compile-time vs runtime error) - Fix is_graphical cfg logic for fancy-base without fancy-no-backtrace - Add cargo-hack feature powerset CI job to verify all 56 combinations
Adds support for no-std.
This is a slight modernization of the miette fork by @bitwalker mentioned in #443, found at bitwalker/miette@main...no-std
All the credit for the contribution goes to @bitwalker, I'm just doing janitorial work around the edges.