Repository: casey/just
File: src/recipe.rs (Rust)

Code before:
  pub(crate) fn min_arguments(&self) -> usize {
    self
      .parameters
      .iter()
      .filter(|p| p.default.is_none() && p.kind != ParameterKind::Star)
      .count()
  }

  pub(crate) fn max_arguments(&self) -> usize {
    if self.parameters.iter().any(|p| p.kind.is_variadic()) {
      usize::MAX - 1
    } else {
      self.parameters.len()
    }
  }

  pub(crate) fn name(&self) -> &'src str {
    self.name.lexeme()
  }

  pub(crate) fn line_number(&self) -> usize {
    self.name.line
  }

  pub(crate) fn confirm(&self) -> RunResult<'src, bool> {
    if let Some(Attribute::Confirm(prompt_text)) = self
      .attributes
      .iter()
      .find(|attr| matches!(attr, Attribute::Confirm(_)))
    {
      let optional_prompt_text: Box<str> = prompt_text
        .as_ref()
        .map_or(String::new().into_boxed_str(), |s| format!("{s} - ").into());
      eprint!("{optional_prompt_text}Run recipe `{}`? ", self.name,);
      let mut line = String::new();
      std::io::stdin()
        .read_line(&mut line)
        .map_err(|io_error| Error::GetConfirmation { io_error })?;
      let line = line.trim().to_lowercase();
      Ok(line == "y" || line == "yes")
    } else {
      Ok(true)
    }
  }

  pub(crate) fn check_can_be_default_recipe(&self) -> RunResult<'src, ()> {
    let min_arguments = self.min_arguments();
    if min_arguments > 0 {
      return Err(Error::DefaultRecipeRequiresArguments {
        recipe: self.name.lexeme(),
        min_arguments,

Review comment (suggestion):
I think we should probably completely replace the prompt, so that users can customize it however they want. I.e., we either print out the default prompt if there's none given, or we print the user-specified prompt, and nothing else. This allows users fully customize the prompt.

Code after:
  }

  pub(crate) fn min_arguments(&self) -> usize {
    self
      .parameters
      .iter()
      .filter(|p| p.default.is_none() && p.kind != ParameterKind::Star)
      .count()
  }

  pub(crate) fn max_arguments(&self) -> usize {
    if self.parameters.iter().any(|p| p.kind.is_variadic()) {
      usize::MAX - 1
    } else {
      self.parameters.len()
    }
  }

  pub(crate) fn name(&self) -> &'src str {
    self.name.lexeme()
  }

  pub(crate) fn line_number(&self) -> usize {
    self.name.line
  }

  pub(crate) fn confirm(&self) -> RunResult<'src, bool> {
    for attribute in &self.attributes {
      if let Attribute::Confirm(prompt) = attribute {
        if let Some(prompt) = prompt {
          eprint!("{} ", prompt.cooked);
        } else {
          eprint!("Run recipe `{}`? ", self.name);
        }
        let mut line = String::new();
        std::io::stdin()
          .read_line(&mut line)
          .map_err(|io_error| Error::GetConfirmation { io_error })?;
        let line = line.trim().to_lowercase();
        return Ok(line == "y" || line == "yes");
      }
    }
    Ok(true)
  }

  pub(crate) fn check_can_be_default_recipe(&self) -> RunResult<'src, ()> {
    let min_arguments = self.min_arguments();
    if min_arguments > 0 {
      return Err(Error::DefaultRecipeRequiresArguments {
        recipe: self.name.lexeme(),
        min_arguments,