mirror of https://github.com/rwf2/Rocket.git
Doc, fix, and test 'cli_colors' deserialization.
This commit is contained in:
parent
f4e8987a46
commit
7cf8b1368f
|
@ -2,7 +2,7 @@ use std::collections::hash_set::HashSet;
|
||||||
|
|
||||||
use criterion::{criterion_group, Criterion};
|
use criterion::{criterion_group, Criterion};
|
||||||
|
|
||||||
use rocket::{route, config::{self, CliColors}, Request, Data, Route, Config};
|
use rocket::{route, config, Request, Data, Route, Config};
|
||||||
use rocket::http::{Method, RawStr, ContentType, Accept, Status};
|
use rocket::http::{Method, RawStr, ContentType, Accept, Status};
|
||||||
use rocket::local::blocking::{Client, LocalRequest};
|
use rocket::local::blocking::{Client, LocalRequest};
|
||||||
|
|
||||||
|
@ -81,7 +81,7 @@ fn client(routes: Vec<Route>) -> Client {
|
||||||
let config = Config {
|
let config = Config {
|
||||||
profile: Config::RELEASE_PROFILE,
|
profile: Config::RELEASE_PROFILE,
|
||||||
log_level: rocket::config::LogLevel::Off,
|
log_level: rocket::config::LogLevel::Off,
|
||||||
cli_colors: CliColors::Never,
|
cli_colors: config::CliColors::Never,
|
||||||
shutdown: config::Shutdown {
|
shutdown: config::Shutdown {
|
||||||
ctrlc: false,
|
ctrlc: false,
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
|
@ -6,7 +6,6 @@ use rocket::http::QMediaType;
|
||||||
use rocket::local::blocking::{LocalRequest, Client};
|
use rocket::local::blocking::{LocalRequest, Client};
|
||||||
use rocket::http::{Method, Accept, ContentType, MediaType, uri::Origin};
|
use rocket::http::{Method, Accept, ContentType, MediaType, uri::Origin};
|
||||||
use rocket::route::{Route, RouteUri, dummy_handler};
|
use rocket::route::{Route, RouteUri, dummy_handler};
|
||||||
use rocket::config::CliColors;
|
|
||||||
|
|
||||||
#[derive(Arbitrary)]
|
#[derive(Arbitrary)]
|
||||||
struct ArbitraryRequestData<'a> {
|
struct ArbitraryRequestData<'a> {
|
||||||
|
@ -187,7 +186,7 @@ fn fuzz((route_a, route_b, req): TestData<'_>) {
|
||||||
let rocket = rocket::custom(rocket::Config {
|
let rocket = rocket::custom(rocket::Config {
|
||||||
workers: 2,
|
workers: 2,
|
||||||
log_level: rocket::log::LogLevel::Off,
|
log_level: rocket::log::LogLevel::Off,
|
||||||
cli_colors: CliColors::Never,
|
cli_colors: rocket::config::CliColors::Never,
|
||||||
..rocket::Config::debug_default()
|
..rocket::Config::debug_default()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,26 +1,43 @@
|
||||||
use core::fmt;
|
use std::fmt;
|
||||||
use serde::{
|
|
||||||
de::{self, Unexpected::{Signed, Str}},
|
|
||||||
Deserialize, Serialize
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Configure color output for logging.
|
use serde::{de, Deserialize, Serialize};
|
||||||
#[derive(Clone, Serialize, PartialEq, Debug, Default)]
|
|
||||||
|
/// Enable or disable coloring when logging.
|
||||||
|
///
|
||||||
|
/// Valid configuration values are:
|
||||||
|
///
|
||||||
|
/// * `"always"` - [`CliColors::Always`]
|
||||||
|
/// * `"auto"`, `1`, or `true` - [`CliColors::Auto`] _(default)_
|
||||||
|
/// * `"never"`, `0`, or `false` - [`CliColors::Never`]
|
||||||
|
#[derive(Debug, Copy, Clone, Default, Serialize, PartialEq, Eq, Hash)]
|
||||||
pub enum CliColors {
|
pub enum CliColors {
|
||||||
/// Always use colors in logs.
|
/// Always enable colors, irrespective of `stdout` and `stderr`.
|
||||||
|
///
|
||||||
|
/// Case-insensitive string values of `"always"` parse as this value.
|
||||||
Always,
|
Always,
|
||||||
|
|
||||||
/// Use colors in logs if the terminal supports it.
|
/// Enable colors _only if_ `stdout` and `stderr` support coloring.
|
||||||
|
///
|
||||||
|
/// Case-insensitive string values of `"auto"`, the boolean `true`, and the
|
||||||
|
/// integer `1` all parse as this value.
|
||||||
|
///
|
||||||
|
/// Only Unix-like systems (Linux, macOS, BSD, etc.), this is equivalent to
|
||||||
|
/// checking if `stdout` and `stderr` are both TTYs. On Windows, the console
|
||||||
|
/// is queried for ANSI escape sequence based coloring support and enabled
|
||||||
|
/// if support is successfully enabled.
|
||||||
#[default]
|
#[default]
|
||||||
Auto,
|
Auto,
|
||||||
|
|
||||||
/// Never use colors in logs.
|
/// Never enable colors, even if `stdout` and `stderr` support them.
|
||||||
Never
|
///
|
||||||
|
/// Case-insensitive string values of `"never"`, the boolean `false`, and
|
||||||
|
/// the integer `0` all parse as this value.
|
||||||
|
Never,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for CliColors {
|
impl fmt::Display for CliColors {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match self {
|
||||||
CliColors::Always => write!(f, "always"),
|
CliColors::Always => write!(f, "always"),
|
||||||
CliColors::Auto => write!(f, "auto"),
|
CliColors::Auto => write!(f, "auto"),
|
||||||
CliColors::Never => write!(f, "never")
|
CliColors::Never => write!(f, "never")
|
||||||
|
@ -28,7 +45,6 @@ impl fmt::Display for CliColors {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for CliColors {
|
impl<'de> Deserialize<'de> for CliColors {
|
||||||
fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
|
fn deserialize<D: de::Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
|
||||||
struct Visitor;
|
struct Visitor;
|
||||||
|
@ -37,7 +53,7 @@ impl<'de> Deserialize<'de> for CliColors {
|
||||||
type Value = CliColors;
|
type Value = CliColors;
|
||||||
|
|
||||||
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str("0, 1, false, true, always, auto, never")
|
f.write_str("0, 1, false, true, always, auto, or never")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_str<E: de::Error>(self, val: &str) -> Result<CliColors, E> {
|
fn visit_str<E: de::Error>(self, val: &str) -> Result<CliColors, E> {
|
||||||
|
@ -46,31 +62,33 @@ impl<'de> Deserialize<'de> for CliColors {
|
||||||
"false" => Ok(CliColors::Never),
|
"false" => Ok(CliColors::Never),
|
||||||
"1" => Ok(CliColors::Auto),
|
"1" => Ok(CliColors::Auto),
|
||||||
"0" => Ok(CliColors::Never),
|
"0" => Ok(CliColors::Never),
|
||||||
|
"always" => Ok(CliColors::Always),
|
||||||
"auto" => Ok(CliColors::Auto),
|
"auto" => Ok(CliColors::Auto),
|
||||||
"never" => Ok(CliColors::Never),
|
"never" => Ok(CliColors::Never),
|
||||||
"always" => Ok(CliColors::Always),
|
_ => Err(E::invalid_value(de::Unexpected::Str(val), &self)),
|
||||||
_ => Err(E::invalid_value(
|
|
||||||
Str(val),
|
|
||||||
&"0, 1, false, true, always, auto, never",
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_bool<E: de::Error>(self, val: bool) -> Result<CliColors, E> {
|
fn visit_bool<E: de::Error>(self, val: bool) -> Result<CliColors, E> {
|
||||||
match val {
|
match val {
|
||||||
true => Ok(CliColors::Auto),
|
true => Ok(CliColors::Auto),
|
||||||
false => Ok(CliColors::Never)
|
false => Ok(CliColors::Never),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_i64<E: de::Error>(self, val: i64) -> Result<CliColors, E> {
|
fn visit_i64<E: de::Error>(self, val: i64) -> Result<CliColors, E> {
|
||||||
match val {
|
match val {
|
||||||
0 => Ok(CliColors::Never),
|
|
||||||
1 => Ok(CliColors::Auto),
|
1 => Ok(CliColors::Auto),
|
||||||
_ => Err(E::invalid_value(
|
0 => Ok(CliColors::Never),
|
||||||
Signed(val),
|
_ => Err(E::invalid_value(de::Unexpected::Signed(val), &self)),
|
||||||
&"0, 1, false, true, always, auto, never",
|
}
|
||||||
))
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E: de::Error>(self, val: u64) -> Result<CliColors, E> {
|
||||||
|
match val {
|
||||||
|
1 => Ok(CliColors::Auto),
|
||||||
|
0 => Ok(CliColors::Never),
|
||||||
|
_ => Err(E::invalid_value(de::Unexpected::Unsigned(val), &self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,7 +116,8 @@ pub struct Config {
|
||||||
pub shutdown: Shutdown,
|
pub shutdown: Shutdown,
|
||||||
/// Max level to log. **(default: _debug_ `normal` / _release_ `critical`)**
|
/// Max level to log. **(default: _debug_ `normal` / _release_ `critical`)**
|
||||||
pub log_level: LogLevel,
|
pub log_level: LogLevel,
|
||||||
/// Whether to use colors and emoji when logging. **(default: `auto`)**
|
/// Whether to use colors and emoji when logging. **(default:
|
||||||
|
/// [`CliColors::Auto`])**
|
||||||
pub cli_colors: CliColors,
|
pub cli_colors: CliColors,
|
||||||
/// PRIVATE: This structure may grow (but never change otherwise) in a
|
/// PRIVATE: This structure may grow (but never change otherwise) in a
|
||||||
/// non-breaking release. As such, constructing this structure should
|
/// non-breaking release. As such, constructing this structure should
|
||||||
|
|
|
@ -268,118 +268,103 @@ mod tests {
|
||||||
..Config::default()
|
..Config::default()
|
||||||
});
|
});
|
||||||
|
|
||||||
jail.set_env("ROCKET_CONFIG", "Other.toml");
|
Ok(())
|
||||||
jail.create_file("Other.toml", r#"
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cli_colors() {
|
||||||
|
figment::Jail::expect_with(|jail| {
|
||||||
|
jail.create_file("Rocket.toml", r#"
|
||||||
[default]
|
[default]
|
||||||
address = "1.2.3.4"
|
|
||||||
port = 1234
|
|
||||||
workers = 20
|
|
||||||
keep_alive = 10
|
|
||||||
log_level = "off"
|
|
||||||
cli_colors = "never"
|
cli_colors = "never"
|
||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
let config = Config::from(Config::figment());
|
let config = Config::from(Config::figment());
|
||||||
assert_eq!(config, Config {
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
address: Ipv4Addr::new(1, 2, 3, 4).into(),
|
|
||||||
port: 1234,
|
|
||||||
workers: 20,
|
|
||||||
keep_alive: 10,
|
|
||||||
log_level: LogLevel::Off,
|
|
||||||
cli_colors: CliColors::Never,
|
|
||||||
..Config::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
jail.set_env("ROCKET_CONFIG", "Other.toml");
|
jail.create_file("Rocket.toml", r#"
|
||||||
jail.create_file("Other.toml", r#"
|
|
||||||
[default]
|
[default]
|
||||||
address = "1.2.3.4"
|
|
||||||
port = 1234
|
|
||||||
workers = 20
|
|
||||||
keep_alive = 10
|
|
||||||
log_level = "off"
|
|
||||||
cli_colors = "auto"
|
cli_colors = "auto"
|
||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
let config = Config::from(Config::figment());
|
let config = Config::from(Config::figment());
|
||||||
assert_eq!(config, Config {
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
address: Ipv4Addr::new(1, 2, 3, 4).into(),
|
|
||||||
port: 1234,
|
|
||||||
workers: 20,
|
|
||||||
keep_alive: 10,
|
|
||||||
log_level: LogLevel::Off,
|
|
||||||
cli_colors: CliColors::Auto,
|
|
||||||
..Config::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
jail.set_env("ROCKET_CONFIG", "Other.toml");
|
jail.create_file("Rocket.toml", r#"
|
||||||
jail.create_file("Other.toml", r#"
|
|
||||||
[default]
|
[default]
|
||||||
address = "1.2.3.4"
|
|
||||||
port = 1234
|
|
||||||
workers = 20
|
|
||||||
keep_alive = 10
|
|
||||||
log_level = "off"
|
|
||||||
cli_colors = "always"
|
cli_colors = "always"
|
||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
let config = Config::from(Config::figment());
|
let config = Config::from(Config::figment());
|
||||||
assert_eq!(config, Config {
|
assert_eq!(config.cli_colors, CliColors::Always);
|
||||||
address: Ipv4Addr::new(1, 2, 3, 4).into(),
|
|
||||||
port: 1234,
|
|
||||||
workers: 20,
|
|
||||||
keep_alive: 10,
|
|
||||||
log_level: LogLevel::Off,
|
|
||||||
cli_colors: CliColors::Always,
|
|
||||||
..Config::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
jail.set_env("ROCKET_CONFIG", "Other.toml");
|
jail.create_file("Rocket.toml", r#"
|
||||||
jail.create_file("Other.toml", r#"
|
|
||||||
[default]
|
[default]
|
||||||
address = "1.2.3.4"
|
|
||||||
port = 1234
|
|
||||||
workers = 20
|
|
||||||
keep_alive = 10
|
|
||||||
log_level = "off"
|
|
||||||
cli_colors = true
|
cli_colors = true
|
||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
let config = Config::from(Config::figment());
|
let config = Config::from(Config::figment());
|
||||||
assert_eq!(config, Config {
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
address: Ipv4Addr::new(1, 2, 3, 4).into(),
|
|
||||||
port: 1234,
|
|
||||||
workers: 20,
|
|
||||||
keep_alive: 10,
|
|
||||||
log_level: LogLevel::Off,
|
|
||||||
cli_colors: CliColors::Auto,
|
|
||||||
..Config::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
jail.set_env("ROCKET_CONFIG", "Other.toml");
|
jail.create_file("Rocket.toml", r#"
|
||||||
jail.create_file("Other.toml", r#"
|
[default]
|
||||||
|
cli_colors = false
|
||||||
|
"#)?;
|
||||||
|
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
|
|
||||||
|
jail.create_file("Rocket.toml", r#"[default]"#)?;
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
|
|
||||||
|
jail.create_file("Rocket.toml", r#"
|
||||||
[default]
|
[default]
|
||||||
address = "1.2.3.4"
|
|
||||||
port = 1234
|
|
||||||
workers = 20
|
|
||||||
keep_alive = 10
|
|
||||||
log_level = "off"
|
|
||||||
cli_colors = 1
|
cli_colors = 1
|
||||||
"#)?;
|
"#)?;
|
||||||
|
|
||||||
let config = Config::from(Config::figment());
|
let config = Config::from(Config::figment());
|
||||||
assert_eq!(config, Config {
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
address: Ipv4Addr::new(1, 2, 3, 4).into(),
|
|
||||||
port: 1234,
|
jail.create_file("Rocket.toml", r#"
|
||||||
workers: 20,
|
[default]
|
||||||
keep_alive: 10,
|
cli_colors = 0
|
||||||
log_level: LogLevel::Off,
|
"#)?;
|
||||||
cli_colors: CliColors::Auto,
|
|
||||||
..Config::default()
|
let config = Config::from(Config::figment());
|
||||||
});
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", 1);
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", 0);
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", true);
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", false);
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", "always");
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Always);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", "NEveR");
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Never);
|
||||||
|
|
||||||
|
jail.set_env("ROCKET_CLI_COLORS", "auTO");
|
||||||
|
let config = Config::from(Config::figment());
|
||||||
|
assert_eq!(config.cli_colors, CliColors::Auto);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -7,8 +7,6 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
|
use serde::{de, Serialize, Serializer, Deserialize, Deserializer};
|
||||||
use yansi::{Paint, Painted, Condition};
|
use yansi::{Paint, Painted, Condition};
|
||||||
|
|
||||||
use crate::config::CliColors;
|
|
||||||
|
|
||||||
/// Reexport the `log` crate as `private`.
|
/// Reexport the `log` crate as `private`.
|
||||||
pub use log as private;
|
pub use log as private;
|
||||||
|
|
||||||
|
@ -170,12 +168,12 @@ pub(crate) fn init(config: &crate::Config) {
|
||||||
|
|
||||||
// Always disable colors if requested or if the stdout/err aren't TTYs.
|
// Always disable colors if requested or if the stdout/err aren't TTYs.
|
||||||
let should_color = match config.cli_colors {
|
let should_color = match config.cli_colors {
|
||||||
CliColors::Always => true,
|
crate::config::CliColors::Always => Condition::ALWAYS,
|
||||||
CliColors::Auto => Condition::stdouterr_are_tty(),
|
crate::config::CliColors::Auto => Condition::DEFAULT,
|
||||||
CliColors::Never => false
|
crate::config::CliColors::Never => Condition::NEVER,
|
||||||
};
|
};
|
||||||
|
|
||||||
yansi::whenever(Condition::cached(should_color));
|
yansi::whenever(should_color);
|
||||||
|
|
||||||
// Set Rocket-logger specific settings only if Rocket's logger is set.
|
// Set Rocket-logger specific settings only if Rocket's logger is set.
|
||||||
if ROCKET_LOGGER_SET.load(Ordering::Acquire) {
|
if ROCKET_LOGGER_SET.load(Ordering::Acquire) {
|
||||||
|
|
|
@ -27,7 +27,7 @@ values:
|
||||||
| `ip_header` | `string`, `false` | IP header to inspect to get [client's real IP]. | `"X-Real-IP"` |
|
| `ip_header` | `string`, `false` | IP header to inspect to get [client's real IP]. | `"X-Real-IP"` |
|
||||||
| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` |
|
| `keep_alive` | `u32` | Keep-alive timeout seconds; disabled when `0`. | `5` |
|
||||||
| `log_level` | [`LogLevel`] | Max level to log. (off/normal/debug/critical) | `normal`/`critical` |
|
| `log_level` | [`LogLevel`] | Max level to log. (off/normal/debug/critical) | `normal`/`critical` |
|
||||||
| `cli_colors` | `bool` | Whether to use colors and emoji when logging. | `true` |
|
| `cli_colors` | [`CliColors`] | Whether to use colors and emoji when logging. | `"auto"` |
|
||||||
| `secret_key` | [`SecretKey`] | Secret key for signing and encrypting values. | `None` |
|
| `secret_key` | [`SecretKey`] | Secret key for signing and encrypting values. | `None` |
|
||||||
| `tls` | [`TlsConfig`] | TLS configuration, if any. | `None` |
|
| `tls` | [`TlsConfig`] | TLS configuration, if any. | `None` |
|
||||||
| `limits` | [`Limits`] | Streaming read size limits. | [`Limits::default()`] |
|
| `limits` | [`Limits`] | Streaming read size limits. | [`Limits::default()`] |
|
||||||
|
@ -68,6 +68,7 @@ profile supplant any values with the same name in any profile.
|
||||||
[`Limits`]: @api/rocket/data/struct.Limits.html
|
[`Limits`]: @api/rocket/data/struct.Limits.html
|
||||||
[`Limits::default()`]: @api/rocket/data/struct.Limits.html#impl-Default
|
[`Limits::default()`]: @api/rocket/data/struct.Limits.html#impl-Default
|
||||||
[`SecretKey`]: @api/rocket/config/struct.SecretKey.html
|
[`SecretKey`]: @api/rocket/config/struct.SecretKey.html
|
||||||
|
[`CliColors`]: @api/rocket/config/enum.CliColors.html
|
||||||
[`TlsConfig`]: @api/rocket/config/struct.TlsConfig.html
|
[`TlsConfig`]: @api/rocket/config/struct.TlsConfig.html
|
||||||
[`Shutdown`]: @api/rocket/config/struct.Shutdown.html
|
[`Shutdown`]: @api/rocket/config/struct.Shutdown.html
|
||||||
[`Shutdown::default()`]: @api/rocket/config/struct.Shutdown.html#fields
|
[`Shutdown::default()`]: @api/rocket/config/struct.Shutdown.html#fields
|
||||||
|
|
Loading…
Reference in New Issue