ronantakizawa/codereview-bench huggingface dataset:
Repository: casey/just
File: src/subcommand.rs (Rust)

Code before:
  }

  fn run_inner<'src>(
    config: &Config,
    loader: &'src Loader,
    arguments: &[String],
    overrides: &BTreeMap<String, String>,
    search: &Search,
  ) -> Result<(), (Error<'src>, bool)> {
    let (_src, _ast, justfile) =
      Self::compile(config, loader, search).map_err(|err| (err, false))?;
    justfile
      .run(config, search, overrides, arguments)
      .map_err(|err| (err, justfile.settings.fallback))
  }

  fn compile<'src>(
    config: &Config,
    loader: &'src Loader,
    search: &Search,
  ) -> Result<(&'src str, Ast<'src>, Justfile<'src>), Error<'src>> {
    let src = if config.includes {
      config.require_unstable("--includes is currently an unstable option")?;
      loader.load_with_includes(&search.justfile)?
    } else {
      loader.load_without_includes(&search.justfile)?
    };

    let tokens = Lexer::lex(src)?;
    let ast = Parser::parse(&tokens)?;
    let justfile = Analyzer::analyze(ast.clone())?;

    if config.verbosity.loud() {
      for warning in &justfile.warnings {
        eprintln!("{}", warning.color_display(config.color.stderr()));
      }
    }

    Ok((src, ast, justfile))
  }

  fn changelog() {
    print!("{}", include_str!("../CHANGELOG.md"));
  }

  fn choose<'src>(
    config: &Config,
    justfile: Justfile<'src>,
    search: &Search,
    overrides: &BTreeMap<String, String>,
    chooser: Option<&str>,

Review comment (suggestion):
```suggestion
    let src = if config.unstable {
      loader.load_with_includes(&search.justfile)?
    } else {
      loader.load_without_includes(&search.justfile)?
    };
```

Code after:
  }

  fn run_inner<'src>(
    config: &Config,
    loader: &'src Loader,
    arguments: &[String],
    overrides: &BTreeMap<String, String>,
    search: &Search,
  ) -> Result<(), (Error<'src>, bool)> {
    let (_src, _ast, justfile) =
      Self::compile(config, loader, search).map_err(|err| (err, false))?;
    justfile
      .run(config, search, overrides, arguments)
      .map_err(|err| (err, justfile.settings.fallback))
  }

  fn compile<'src>(
    config: &Config,
    loader: &'src Loader,
    search: &Search,
  ) -> Result<(&'src str, Ast<'src>, Justfile<'src>), Error<'src>> {
    let src = loader.load(&search.justfile)?;

    let tokens = Lexer::lex(src)?;
    let ast = Parser::parse(&tokens)?;
    let justfile = Analyzer::analyze(ast.clone())?;

    if config.verbosity.loud() {
      for warning in &justfile.warnings {
        eprintln!("{}", warning.color_display(config.color.stderr()));
      }
    }

    Ok((src, ast, justfile))
  }

  fn changelog() {
    print!("{}", include_str!("../CHANGELOG.md"));
  }

  fn choose<'src>(
    config: &Config,
    justfile: Justfile<'src>,
    search: &Search,
    overrides: &BTreeMap<String, String>,
    chooser: Option<&str>,
  ) -> Result<(), Error<'src>> {
    let recipes = justfile
      .public_recipes(config.unsorted)
      .iter()
      .filter(|recipe| recipe.min_arguments() == 0)
Repository: casey/just
File: src/loader.rs (Rust)

Code before:
use super::*;
use std::collections::HashSet;

// This regex defines the syntax for a Justfile include statement as `!include <file-path>`
// occurring at the start of a line, and as the only contents of that line
const INCLUDE_REGEX: &str = "^!include\\s+(.+)$";

pub(crate) struct Loader {
  arena: Arena<String>,
}

impl Loader {
  pub(crate) fn new() -> Self {
    Loader {
      arena: Arena::new(),
    }
  }

  pub(crate) fn load_without_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;
    Ok(self.arena.alloc(src))
  }

  pub(crate) fn load_with_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.perform_load(path, HashSet::new())?;
    Ok(self.arena.alloc(src))

Review comment (suggestion):
```suggestion
// This regex defines the syntax for an include statement as `!include <file-path>`
```

Code after:
use super::*;
use std::collections::HashSet;

struct LinesWithEndings<'a> {
  input: &'a str,
}

impl<'a> LinesWithEndings<'a> {
  fn new(input: &'a str) -> Self {
    Self { input }
  }
}

impl<'a> Iterator for LinesWithEndings<'a> {
  type Item = &'a str;

  fn next(&mut self) -> Option<&'a str> {
    if self.input.is_empty() {
      return None;
    }
    let split = self.input.find('\n').map_or(self.input.len(), |i| i + 1);
    let (line, rest) = self.input.split_at(split);
    self.input = rest;
    Some(line)
  }
}

pub(crate) struct Loader {
  arena: Arena<String>,
Repository: casey/just
File: src/loader.rs (Rust)

Code before:
use super::*;
use std::collections::HashSet;

// This regex defines the syntax for a Justfile include statement as `!include <file-path>`
// occurring at the start of a line, and as the only contents of that line
const INCLUDE_REGEX: &str = "^!include\\s+(.+)$";

pub(crate) struct Loader {
  arena: Arena<String>,
}

impl Loader {
  pub(crate) fn new() -> Self {
    Loader {
      arena: Arena::new(),
    }
  }

  pub(crate) fn load_without_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;
    Ok(self.arena.alloc(src))
  }

  pub(crate) fn load_with_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.perform_load(path, HashSet::new())?;
    Ok(self.arena.alloc(src))
  }

  fn perform_load(&self, path: &Path, seen_paths: HashSet<PathBuf>) -> RunResult<String> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;

    self.process_includes(src, path, seen_paths)
  }

  /// Given the original contents of a Justfile (with include directives), load all the included
  /// paths to produce one String with the contents of all the files concatenated.
  ///
  /// Note that this does not do any parsing yet (i.e. nothing stops a justfile from including a

Review comment (suggestion):
I like just calling the base case `load`:

```suggestion
  pub(crate) fn load<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
```

Code after:
use super::*;
use std::collections::HashSet;

struct LinesWithEndings<'a> {
  input: &'a str,
}

impl<'a> LinesWithEndings<'a> {
  fn new(input: &'a str) -> Self {
    Self { input }
  }
}

impl<'a> Iterator for LinesWithEndings<'a> {
  type Item = &'a str;

  fn next(&mut self) -> Option<&'a str> {
    if self.input.is_empty() {
      return None;
    }
    let split = self.input.find('\n').map_or(self.input.len(), |i| i + 1);
    let (line, rest) = self.input.split_at(split);
    self.input = rest;
    Some(line)
  }
}

pub(crate) struct Loader {
  arena: Arena<String>,
  unstable: bool,
}

impl Loader {
  pub(crate) fn new(unstable: bool) -> Self {
    Loader {
      arena: Arena::new(),
      unstable,
    }
  }

  pub(crate) fn load<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.load_recursive(path, HashSet::new())?;
    Ok(self.arena.alloc(src))
  }
Repository: casey/just
File: src/loader.rs (Rust)

Code before:
use super::*;
use std::collections::HashSet;

// This regex defines the syntax for a Justfile include statement as `!include <file-path>`
// occurring at the start of a line, and as the only contents of that line
const INCLUDE_REGEX: &str = "^!include\\s+(.+)$";

pub(crate) struct Loader {
  arena: Arena<String>,
}

impl Loader {
  pub(crate) fn new() -> Self {
    Loader {
      arena: Arena::new(),
    }
  }

  pub(crate) fn load_without_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;
    Ok(self.arena.alloc(src))
  }

  pub(crate) fn load_with_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.perform_load(path, HashSet::new())?;
    Ok(self.arena.alloc(src))
  }

  fn perform_load(&self, path: &Path, seen_paths: HashSet<PathBuf>) -> RunResult<String> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;

    self.process_includes(src, path, seen_paths)
  }

  /// Given the original contents of a Justfile (with include directives), load all the included
  /// paths to produce one String with the contents of all the files concatenated.
  ///
  /// Note that this does not do any parsing yet (i.e. nothing stops a justfile from including a
  /// file that is not a valid justfile), and that (currently) line numbers in error messages will
  /// reference lines in this concatenated String rather than probably-more-useful information
  /// about the original file an error came from.
  ///

Review comment (refactor):
Let's refactor this into a shared function, `Loader::load_file`  that returns a String.

Code after:
use super::*;
use std::collections::HashSet;

struct LinesWithEndings<'a> {
  input: &'a str,
}

impl<'a> LinesWithEndings<'a> {
  fn new(input: &'a str) -> Self {
    Self { input }
  }
}

impl<'a> Iterator for LinesWithEndings<'a> {
  type Item = &'a str;

  fn next(&mut self) -> Option<&'a str> {
    if self.input.is_empty() {
      return None;
    }
    let split = self.input.find('\n').map_or(self.input.len(), |i| i + 1);
    let (line, rest) = self.input.split_at(split);
    self.input = rest;
    Some(line)
  }
}

pub(crate) struct Loader {
  arena: Arena<String>,
  unstable: bool,
}

impl Loader {
  pub(crate) fn new(unstable: bool) -> Self {
    Loader {
      arena: Arena::new(),
      unstable,
    }
  }

  pub(crate) fn load<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.load_recursive(path, HashSet::new())?;
    Ok(self.arena.alloc(src))
  }

  fn load_file<'a>(path: &Path) -> RunResult<'a, String> {
    fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
Repository: casey/just
File: src/loader.rs (Rust)

Code before:
use super::*;
use std::collections::HashSet;

// This regex defines the syntax for a Justfile include statement as `!include <file-path>`
// occurring at the start of a line, and as the only contents of that line
const INCLUDE_REGEX: &str = "^!include\\s+(.+)$";

pub(crate) struct Loader {
  arena: Arena<String>,
}

impl Loader {
  pub(crate) fn new() -> Self {
    Loader {
      arena: Arena::new(),
    }
  }

  pub(crate) fn load_without_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = fs::read_to_string(path).map_err(|io_error| Error::Load {
      path: path.to_owned(),
      io_error,
    })?;
    Ok(self.arena.alloc(src))
  }

  pub(crate) fn load_with_includes<'src>(&'src self, path: &Path) -> RunResult<&'src str> {
    let src = self.perform_load(path, HashSet::new())?;
    Ok(self.arena.alloc(src))
  }


Review comment (suggestion):
I think we should match `!include` on its own:

```suggestion
const INCLUDE_REGEX: &str = "^!include(\\s(.+))?$";
```

Which would be an error.

Code after:
use super::*;
use std::collections::HashSet;

struct LinesWithEndings<'a> {
  input: &'a str,
}

impl<'a> LinesWithEndings<'a> {
  fn new(input: &'a str) -> Self {
    Self { input }
  }
}

impl<'a> Iterator for LinesWithEndings<'a> {
  type Item = &'a str;

  fn next(&mut self) -> Option<&'a str> {
    if self.input.is_empty() {
      return None;
    }
    let split = self.input.find('\n').map_or(self.input.len(), |i| i + 1);
    let (line, rest) = self.input.split_at(split);
    self.input = rest;
    Some(line)
  }
}

pub(crate) struct Loader {
  arena: Arena<String>,
  unstable: bool,
}
Repository: casey/just
File: src/config_error.rs (Rust)

Code before:
use super::*;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)), context(suffix(Context)))]
pub(crate) enum ConfigError {
  #[snafu(display("Failed to get current directory: {}", source))]
  CurrentDir { source: io::Error },
  #[snafu(display(
    "Internal config error, this may indicate a bug in just: {} \
     consider filing an issue: https://github.com/casey/just/issues/new",
    message
  ))]
  Internal { message: String },
  #[snafu(display("Conflicting path-prefixed recipes: `{}` and `{}`", seen, conflicting))]
  MultipleSearchDir { seen: String, conflicting: String },
  #[snafu(display(
    "Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
  ))]
  SearchDirConflict,
  #[snafu(display(
    "`--{}` used with unexpected {}: {}",
    subcommand.to_lowercase(),
    Count("argument", arguments.len()),
    List::and_ticked(arguments)
  ))]
  SubcommandArguments {
    subcommand: &'static str,
    arguments: Vec<String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}",
      subcommand.to_lowercase(),
      List::and_ticked(overrides.iter().map(|(key, value)| format!("{key}={value}"))),
  ))]
  SubcommandOverrides {
    subcommand: &'static str,
    overrides: BTreeMap<String, String>,
  },
  #[snafu(display(

Review comment (suggestion):
```suggestion
  #[snafu(display("Conflicting path arguments: `{}` and `{}`", seen, conflicting))]
```

Code after:
use super::*;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)), context(suffix(Context)))]
pub(crate) enum ConfigError {
  #[snafu(display("Failed to get current directory: {}", source))]
  CurrentDir { source: io::Error },
  #[snafu(display(
    "Internal config error, this may indicate a bug in just: {} \
     consider filing an issue: https://github.com/casey/just/issues/new",
    message
  ))]
  Internal { message: String },
  #[snafu(display("Conflicting path arguments: `{}` and `{}`", seen, conflicting))]
  ConflictingSearchDirArgs { seen: String, conflicting: String },
  #[snafu(display(
    "Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
  ))]
  SearchDirConflict,
  #[snafu(display(
    "`--{}` used with unexpected {}: {}",
    subcommand.to_lowercase(),
    Count("argument", arguments.len()),
    List::and_ticked(arguments)
  ))]
  SubcommandArguments {
    subcommand: &'static str,
    arguments: Vec<String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}",
      subcommand.to_lowercase(),
      List::and_ticked(overrides.iter().map(|(key, value)| format!("{key}={value}"))),
  ))]
  SubcommandOverrides {
    subcommand: &'static str,
    overrides: BTreeMap<String, String>,
  },
  #[snafu(display(
Repository: casey/just
File: src/config_error.rs (Rust)

Code before:
use super::*;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)), context(suffix(Context)))]
pub(crate) enum ConfigError {
  #[snafu(display("Failed to get current directory: {}", source))]
  CurrentDir { source: io::Error },
  #[snafu(display(
    "Internal config error, this may indicate a bug in just: {} \
     consider filing an issue: https://github.com/casey/just/issues/new",
    message
  ))]
  Internal { message: String },
  #[snafu(display("Conflicting path-prefixed recipes: `{}` and `{}`", seen, conflicting))]
  MultipleSearchDir { seen: String, conflicting: String },
  #[snafu(display(
    "Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
  ))]
  SearchDirConflict,
  #[snafu(display(
    "`--{}` used with unexpected {}: {}",
    subcommand.to_lowercase(),
    Count("argument", arguments.len()),
    List::and_ticked(arguments)
  ))]
  SubcommandArguments {
    subcommand: &'static str,
    arguments: Vec<String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}",
      subcommand.to_lowercase(),
      List::and_ticked(overrides.iter().map(|(key, value)| format!("{key}={value}"))),
  ))]
  SubcommandOverrides {
    subcommand: &'static str,
    overrides: BTreeMap<String, String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}; and arguments: {}",

Review comment (suggestion):
```suggestion
  ConflictingSearchDirArgs { seen: String, conflicting: String },
```

Code after:
use super::*;

#[derive(Debug, Snafu)]
#[snafu(visibility(pub(crate)), context(suffix(Context)))]
pub(crate) enum ConfigError {
  #[snafu(display("Failed to get current directory: {}", source))]
  CurrentDir { source: io::Error },
  #[snafu(display(
    "Internal config error, this may indicate a bug in just: {} \
     consider filing an issue: https://github.com/casey/just/issues/new",
    message
  ))]
  Internal { message: String },
  #[snafu(display("Conflicting path arguments: `{}` and `{}`", seen, conflicting))]
  ConflictingSearchDirArgs { seen: String, conflicting: String },
  #[snafu(display(
    "Path-prefixed recipes may not be used with `--working-directory` or `--justfile`."
  ))]
  SearchDirConflict,
  #[snafu(display(
    "`--{}` used with unexpected {}: {}",
    subcommand.to_lowercase(),
    Count("argument", arguments.len()),
    List::and_ticked(arguments)
  ))]
  SubcommandArguments {
    subcommand: &'static str,
    arguments: Vec<String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}",
      subcommand.to_lowercase(),
      List::and_ticked(overrides.iter().map(|(key, value)| format!("{key}={value}"))),
  ))]
  SubcommandOverrides {
    subcommand: &'static str,
    overrides: BTreeMap<String, String>,
  },
  #[snafu(display(
      "`--{}` used with unexpected overrides: {}; and arguments: {}",
Repository: casey/just
File: src/positional.rs (Rust)

Code before:
  }

  test! {
    name: override_after_search_directory,
    values: ["..", "a=b"],
    overrides: [],
    search_directory: Some(".."),
    arguments: ["a=b"],
  }

  test! {
    name: override_after_argument,
    values: ["a", "a=b"],
    overrides: [],
    search_directory: None,
    arguments: ["a", "a=b"],
  }

  test! {
    name: search_dir_and_recipe_only,
    values: ["some/path/recipe_a"],
    overrides: [],
    search_directory: Some("some/path/"),
    arguments: ["recipe_a"],
  }

  test! {
    name: multiple_same_valued_search_directories,
    values: ["some/path/recipe_a", "some/path/recipe_b"],
    overrides: [],
    search_directory: Some("some/path/"),
    arguments: ["recipe_a", "recipe_b"],
  }

  #[test]
  fn invalid_multiple_search_paths() {
    let err = Positional::from_values(Some(
      [
        "some/path/recipe_a",
        "some/path/recipe_b",
        "other/path/recipe_c",
      ]
      .iter()
      .copied(),
    ))
    .unwrap_err();
    assert_matches!(err, ConfigError::MultipleSearchDir { seen, conflicting } if seen == "some/path/" && conflicting == "other/path/");
  }
}


Review comment (suggestion):
I think these would be better as integration tests. I'd also add one that tries other args, like `.`, `./foo`, `../foo`

Code after:

  test! {
    name: search_directory_after_argument,
    values: ["foo=bar", "bar=foo", "baz", "bzzd", "bar/"],
    overrides: [("foo", "bar"), ("bar", "foo")],
    search_directory: None,
    arguments: ["baz", "bzzd", "bar/"],
  }

  test! {
    name: override_after_search_directory,
    values: ["..", "a=b"],
    overrides: [],
    search_directory: Some(".."),
    arguments: ["a=b"],
  }

  test! {
    name: override_after_argument,
    values: ["a", "a=b"],
    overrides: [],
    search_directory: None,
    arguments: ["a", "a=b"],
  }

  test! {
    name: search_dir_and_recipe_only,
    values: ["some/path/recipe_a"],
    overrides: [],
    search_directory: Some("some/path/"),
    arguments: ["recipe_a"],
  }

  test! {
    name: multiple_same_valued_search_directories,
    values: ["some/path/recipe_a", "some/path/recipe_b"],
    overrides: [],
    search_directory: Some("some/path/"),
    arguments: ["recipe_a", "recipe_b"],
  }

  #[test]
  fn invalid_multiple_search_paths() {
    let err = Positional::from_values(Some(
      [
        "some/path/recipe_a",
        "some/path/recipe_b",
        "other/path/recipe_c",
      ]
      .iter()
      .copied(),
Repository: casey/just
File: src/lexer.rs (Rust)

Code before:
      self.recipe_body_pending = true;
    }

    Ok(())
  }

  /// Lex an token starting with '\' escape
  fn lex_escape(&mut self) -> CompileResult<'src, ()> {
    self.presume('\\')?;

    // Treat newline escaped with \ as whitespace
    if self.accepted('\n')? {
      while self.next_is_whitespace() {
        self.advance()?;
      }
      self.token(Whitespace);
    } else if self.accepted('\r')? {
      if !self.accepted('\n')? {
        return Err(self.error(UnpairedCarriageReturn));
      }
      while self.next_is_whitespace() {
        self.advance()?;
      }
      self.token(Whitespace);
    } else if let Some(next) = self.next {
      return Err(self.error(CompileErrorKind::InvalidEscapeSequence { character: next }));
    }

    Ok(())
  }

  /// Lex a carriage return and line feed
  fn lex_eol(&mut self) -> CompileResult<'src, ()> {
    if self.accepted('\r')? {
      if !self.accepted('\n')? {
        return Err(self.error(UnpairedCarriageReturn));
      }
    } else {
      self.presume('\n')?;
    }

    // Emit an eol if there are no open delimiters, otherwise emit a whitespace
    // token.
    if self.open_delimiters() {
      self.token(Whitespace);
    } else {
      self.token(Eol);
    }

    Ok(())
  }

Review comment (security):
```suggestion
    } else if let Some(character) = self.next {
      return Err(self.error(CompileErrorKind::InvalidEscapeSequence { character }));
```

Code after:
      self.recipe_body_pending = true;
    }

    Ok(())
  }

  /// Lex an token starting with '\' escape
  fn lex_escape(&mut self) -> CompileResult<'src, ()> {
    self.presume('\\')?;

    // Treat newline escaped with \ as whitespace
    if self.accepted('\n')? {
      while self.next_is_whitespace() {
        self.advance()?;
      }
      self.token(Whitespace);
    } else if self.accepted('\r')? {
      if !self.accepted('\n')? {
        return Err(self.error(UnpairedCarriageReturn));
      }
      while self.next_is_whitespace() {
        self.advance()?;
      }
      self.token(Whitespace);
    } else if let Some(character) = self.next {
      return Err(self.error(CompileErrorKind::InvalidEscapeSequence { character }));
    }

    Ok(())
  }

  /// Lex a carriage return and line feed
  fn lex_eol(&mut self) -> CompileResult<'src, ()> {
    if self.accepted('\r')? {
      if !self.accepted('\n')? {
        return Err(self.error(UnpairedCarriageReturn));
      }
    } else {
      self.presume('\n')?;
    }

    // Emit an eol if there are no open delimiters, otherwise emit a whitespace
    // token.
    if self.open_delimiters() {
      self.token(Whitespace);
    } else {
      self.token(Eol);
    }

    Ok(())
  }
Repository: casey/just
File: src/color.rs (Rust)

Code before:
use ansi_term::Colour;
use {
  super::*,
  ansi_term::{ANSIGenericString, Color::*, Prefix, Style, Suffix},
  atty::Stream,
};

#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) struct Color {
  use_color: UseColor,
  atty: bool,
  style: Style,
}

impl Color {
  fn restyle(self, style: Style) -> Self {
    Self { style, ..self }
  }

  fn redirect(self, stream: Stream) -> Self {
    Self {
      atty: atty::is(stream),
      ..self
    }
  }


Review comment (suggestion):
There's already a `Color` struct, so to avoid ambiguity, we shouldn't import this, and just refer to it as `ansi_term::Color`. (With the American spelling.)

```suggestion
```

Code after:
use {
  super::*,
  ansi_term::{ANSIGenericString, Color::*, Prefix, Style, Suffix},
  atty::Stream,
};

#[derive(Copy, Clone, Debug, PartialEq)]
pub(crate) struct Color {
  use_color: UseColor,
  atty: bool,
  style: Style,
}

impl Color {
  fn restyle(self, style: Style) -> Self {
    Self { style, ..self }
  }

  fn redirect(self, stream: Stream) -> Self {
    Self {
      atty: atty::is(stream),
      ..self
    }
  }

  fn effective_style(&self) -> Style {