Add logger trait (#93)
Introduced a new `Logger` trait which can be used to access all log events emitted during compilation. Currently these only include messages emitted by the `@debug` and `@warn` statements. Changes were implemented in a backwards-compatible manner, but the current `Options::quiet` method has been marked as deprecated, as its behavior can be achieved using the `NullLogger` structure. The default logger used is `StdLogger` which writes all log events to standard error. This reflect the default behavior prior to introduction of `Logger`. With these new changes, it is also now possible to properly test the `@debug` and `@warn` statements.
This commit is contained in:
parent
8d3258dcd4
commit
a1ca700bff
@ -1042,14 +1042,10 @@ impl<'a> Visitor<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let message = self.visit_expr(debug_rule.value)?;
|
let message = self.visit_expr(debug_rule.value)?;
|
||||||
|
let message = message.inspect(debug_rule.span)?;
|
||||||
|
|
||||||
let loc = self.map.look_up_span(debug_rule.span);
|
let loc = self.map.look_up_span(debug_rule.span);
|
||||||
eprintln!(
|
self.options.logger.debug(loc, message.as_str());
|
||||||
"{}:{} DEBUG: {}",
|
|
||||||
loc.file.name(),
|
|
||||||
loc.begin.line + 1,
|
|
||||||
message.inspect(debug_rule.span)?
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
@ -1588,13 +1584,7 @@ impl<'a> Visitor<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let loc = self.map.look_up_span(span);
|
let loc = self.map.look_up_span(span);
|
||||||
eprintln!(
|
self.options.logger.warning(loc, message);
|
||||||
"Warning: {}\n ./{}:{}:{}",
|
|
||||||
message,
|
|
||||||
loc.file.name(),
|
|
||||||
loc.begin.line + 1,
|
|
||||||
loc.begin.column + 1
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_warn_rule(&mut self, warn_rule: AstWarn) -> SassResult<()> {
|
fn visit_warn_rule(&mut self, warn_rule: AstWarn) -> SassResult<()> {
|
||||||
|
@ -80,6 +80,7 @@ pub use crate::error::{
|
|||||||
PublicSassErrorKind as ErrorKind, SassError as Error, SassResult as Result,
|
PublicSassErrorKind as ErrorKind, SassError as Error, SassResult as Result,
|
||||||
};
|
};
|
||||||
pub use crate::fs::{Fs, NullFs, StdFs};
|
pub use crate::fs::{Fs, NullFs, StdFs};
|
||||||
|
pub use crate::logger::{Logger, NullLogger, StdLogger};
|
||||||
pub use crate::options::{InputSyntax, Options, OutputStyle};
|
pub use crate::options::{InputSyntax, Options, OutputStyle};
|
||||||
pub use crate::{builtin::Builtin, evaluate::Visitor};
|
pub use crate::{builtin::Builtin, evaluate::Visitor};
|
||||||
pub(crate) use crate::{context_flags::ContextFlags, lexer::Token};
|
pub(crate) use crate::{context_flags::ContextFlags, lexer::Token};
|
||||||
@ -114,6 +115,7 @@ mod evaluate;
|
|||||||
mod fs;
|
mod fs;
|
||||||
mod interner;
|
mod interner;
|
||||||
mod lexer;
|
mod lexer;
|
||||||
|
mod logger;
|
||||||
mod options;
|
mod options;
|
||||||
mod parse;
|
mod parse;
|
||||||
mod selector;
|
mod selector;
|
||||||
|
50
crates/compiler/src/logger.rs
Normal file
50
crates/compiler/src/logger.rs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
use codemap::SpanLoc;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
/// Sink for log messages
|
||||||
|
pub trait Logger: Debug {
|
||||||
|
/// Logs message from a `@debug` statement
|
||||||
|
fn debug(&self, location: SpanLoc, message: &str);
|
||||||
|
|
||||||
|
/// Logs message from a `@warn` statement
|
||||||
|
fn warning(&self, location: SpanLoc, message: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Logs events to standard error
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StdLogger;
|
||||||
|
|
||||||
|
impl Logger for StdLogger {
|
||||||
|
#[inline]
|
||||||
|
fn debug(&self, location: SpanLoc, message: &str) {
|
||||||
|
eprintln!(
|
||||||
|
"{}:{} DEBUG: {}",
|
||||||
|
location.file.name(),
|
||||||
|
location.begin.line + 1,
|
||||||
|
message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn warning(&self, location: SpanLoc, message: &str) {
|
||||||
|
eprintln!(
|
||||||
|
"Warning: {}\n ./{}:{}:{}",
|
||||||
|
message,
|
||||||
|
location.file.name(),
|
||||||
|
location.begin.line + 1,
|
||||||
|
location.begin.column + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discards all log events
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NullLogger;
|
||||||
|
|
||||||
|
impl Logger for NullLogger {
|
||||||
|
#[inline]
|
||||||
|
fn debug(&self, _location: SpanLoc, _message: &str) {}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn warning(&self, _location: SpanLoc, _message: &str) {}
|
||||||
|
}
|
@ -3,7 +3,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{builtin::Builtin, Fs, StdFs};
|
use crate::{builtin::Builtin, Fs, Logger, StdFs, StdLogger};
|
||||||
|
|
||||||
/// Configuration for Sass compilation
|
/// Configuration for Sass compilation
|
||||||
///
|
///
|
||||||
@ -12,10 +12,12 @@ use crate::{builtin::Builtin, Fs, StdFs};
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Options<'a> {
|
pub struct Options<'a> {
|
||||||
pub(crate) fs: &'a dyn Fs,
|
pub(crate) fs: &'a dyn Fs,
|
||||||
|
pub(crate) logger: &'a dyn Logger,
|
||||||
pub(crate) style: OutputStyle,
|
pub(crate) style: OutputStyle,
|
||||||
pub(crate) load_paths: Vec<PathBuf>,
|
pub(crate) load_paths: Vec<PathBuf>,
|
||||||
pub(crate) allows_charset: bool,
|
pub(crate) allows_charset: bool,
|
||||||
pub(crate) unicode_error_messages: bool,
|
pub(crate) unicode_error_messages: bool,
|
||||||
|
// TODO: remove in favor of NullLogger
|
||||||
pub(crate) quiet: bool,
|
pub(crate) quiet: bool,
|
||||||
pub(crate) input_syntax: Option<InputSyntax>,
|
pub(crate) input_syntax: Option<InputSyntax>,
|
||||||
pub(crate) custom_fns: HashMap<String, Builtin>,
|
pub(crate) custom_fns: HashMap<String, Builtin>,
|
||||||
@ -26,6 +28,7 @@ impl Default for Options<'_> {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
fs: &StdFs,
|
fs: &StdFs,
|
||||||
|
logger: &StdLogger,
|
||||||
style: OutputStyle::Expanded,
|
style: OutputStyle::Expanded,
|
||||||
load_paths: Vec::new(),
|
load_paths: Vec::new(),
|
||||||
allows_charset: true,
|
allows_charset: true,
|
||||||
@ -49,6 +52,16 @@ impl<'a> Options<'a> {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This option allows you to define how log events should be handled
|
||||||
|
///
|
||||||
|
/// Be default, [`StdLogger`] is used, which writes all events to standard output.
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub fn logger(mut self, logger: &'a dyn Logger) -> Self {
|
||||||
|
self.logger = logger;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// `grass` currently offers 2 different output styles
|
/// `grass` currently offers 2 different output styles
|
||||||
///
|
///
|
||||||
/// - [`OutputStyle::Expanded`] writes each selector and declaration on its own line.
|
/// - [`OutputStyle::Expanded`] writes each selector and declaration on its own line.
|
||||||
@ -67,10 +80,12 @@ impl<'a> Options<'a> {
|
|||||||
/// when compiling. By default, Sass emits warnings
|
/// when compiling. By default, Sass emits warnings
|
||||||
/// when deprecated features are used or when the
|
/// when deprecated features are used or when the
|
||||||
/// `@warn` rule is encountered. It also silences the
|
/// `@warn` rule is encountered. It also silences the
|
||||||
/// `@debug` rule.
|
/// `@debug` rule. Setting this option to `true` will
|
||||||
|
/// stop all events from reaching the assigned [`logger`].
|
||||||
///
|
///
|
||||||
/// By default, this value is `false` and warnings are emitted.
|
/// By default, this value is `false` and warnings are emitted.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
|
#[deprecated = "use `logger(&NullLogger)` instead"]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn quiet(mut self, quiet: bool) -> Self {
|
pub const fn quiet(mut self, quiet: bool) -> Self {
|
||||||
self.quiet = quiet;
|
self.quiet = quiet;
|
||||||
|
@ -66,8 +66,8 @@ grass input.scss
|
|||||||
)]
|
)]
|
||||||
|
|
||||||
pub use grass_compiler::{
|
pub use grass_compiler::{
|
||||||
from_path, from_string, Error, ErrorKind, Fs, InputSyntax, NullFs, Options, OutputStyle,
|
from_path, from_string, Error, ErrorKind, Fs, InputSyntax, Logger, NullFs, NullLogger, Options,
|
||||||
Result, StdFs,
|
OutputStyle, Result, StdFs, StdLogger,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Include CSS in your binary at compile time from a Sass source file
|
/// Include CSS in your binary at compile time from a Sass source file
|
||||||
|
@ -1,12 +1,37 @@
|
|||||||
|
use macros::TestLogger;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
test!(simple_debug, "@debug 2", "");
|
#[test]
|
||||||
test!(simple_debug_with_semicolon, "@debug 2;", "");
|
fn simple_debug() {
|
||||||
test!(
|
let input = "@debug 2";
|
||||||
// todo: test stdout
|
let logger = TestLogger::default();
|
||||||
debug_while_quiet,
|
let options = grass::Options::default().logger(&logger);
|
||||||
"@debug 2;",
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
"",
|
assert_eq!(&output, "");
|
||||||
grass::Options::default().quiet(true)
|
assert_eq!(&[String::from("2")], logger.debug_messages().as_slice());
|
||||||
);
|
assert_eq!(&[] as &[String], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_debug_with_semicolon() {
|
||||||
|
let input = "@debug 2;";
|
||||||
|
let logger = TestLogger::default();
|
||||||
|
let options = grass::Options::default().logger(&logger);
|
||||||
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
|
assert_eq!(&output, "");
|
||||||
|
assert_eq!(&[String::from("2")], logger.debug_messages().as_slice());
|
||||||
|
assert_eq!(&[] as &[String], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn debug_while_quiet() {
|
||||||
|
let input = "@debug 2;";
|
||||||
|
let logger = TestLogger::default();
|
||||||
|
let options = grass::Options::default().logger(&logger).quiet(true);
|
||||||
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
|
assert_eq!(&output, "");
|
||||||
|
assert_eq!(&[] as &[String], logger.debug_messages().as_slice());
|
||||||
|
assert_eq!(&[] as &[String], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
use std::{
|
use std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
cell::RefCell,
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use grass::Fs;
|
use grass::{Fs, Logger};
|
||||||
|
use grass_compiler::codemap::SpanLoc;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! test {
|
macro_rules! test {
|
||||||
@ -162,3 +164,32 @@ impl Fs for TestFs {
|
|||||||
Ok(self.files.get(path).unwrap().as_bytes().to_vec())
|
Ok(self.files.get(path).unwrap().as_bytes().to_vec())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct TestLoggerState {
|
||||||
|
debug_messages: Vec<String>,
|
||||||
|
warning_messages: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct TestLogger(RefCell<TestLoggerState>);
|
||||||
|
|
||||||
|
impl TestLogger {
|
||||||
|
pub fn debug_messages(&self) -> Vec<String> {
|
||||||
|
self.0.borrow().debug_messages.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn warning_messages(&self) -> Vec<String> {
|
||||||
|
self.0.borrow().warning_messages.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Logger for TestLogger {
|
||||||
|
fn debug(&self, _location: SpanLoc, message: &str) {
|
||||||
|
self.0.borrow_mut().debug_messages.push(message.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn warning(&self, _location: SpanLoc, message: &str) {
|
||||||
|
self.0.borrow_mut().warning_messages.push(message.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,11 +1,37 @@
|
|||||||
|
use macros::TestLogger;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod macros;
|
mod macros;
|
||||||
|
|
||||||
test!(simple_warn, "@warn 2", "");
|
#[test]
|
||||||
test!(
|
fn warn_debug() {
|
||||||
// todo: test stdout
|
let input = "@warn 2";
|
||||||
warn_while_quiet,
|
let logger = TestLogger::default();
|
||||||
"@warn 2;",
|
let options = grass::Options::default().logger(&logger);
|
||||||
"",
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
grass::Options::default().quiet(true)
|
assert_eq!(&output, "");
|
||||||
);
|
assert_eq!(&[] as &[String], logger.debug_messages().as_slice());
|
||||||
|
assert_eq!(&[String::from("2")], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_warn_with_semicolon() {
|
||||||
|
let input = "@warn 2;";
|
||||||
|
let logger = TestLogger::default();
|
||||||
|
let options = grass::Options::default().logger(&logger);
|
||||||
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
|
assert_eq!(&output, "");
|
||||||
|
assert_eq!(&[] as &[String], logger.debug_messages().as_slice());
|
||||||
|
assert_eq!(&[String::from("2")], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn warn_while_quiet() {
|
||||||
|
let input = "@warn 2;";
|
||||||
|
let logger = TestLogger::default();
|
||||||
|
let options = grass::Options::default().logger(&logger).quiet(true);
|
||||||
|
let output = grass::from_string(input.to_string(), &options).expect(input);
|
||||||
|
assert_eq!(&output, "");
|
||||||
|
assert_eq!(&[] as &[String], logger.debug_messages().as_slice());
|
||||||
|
assert_eq!(&[] as &[String], logger.warning_messages().as_slice());
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user