1

问题

我想知道是否有办法改进我程序中某些函数的当前结构,因为我觉得发生了大量不需要的重复。

背景

我正在编写一个小型记录器,以便 CLI 应用程序可以在终端中拥有更漂亮的文本。我有几个函数可以向标准输出添加一些图标,例如success(),它需要一条消息并向其添加一个绿色复选标记图标,与 等相同error()warn()它们都可以在末尾添加换行符或忽略它取决于用户是否same()在它之前调用过。

目前他们使用下面定义的三个函数来决定是否添加换行符,以及是否添加时间戳。

代码

/// Outputs to stdout with an icon
fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();

    if self.same_line {
        print!("{} {}{}", icon, timestamp, message);
    } else {
        println!("{} {}{}", icon, timestamp, message);
    }

    self.same_line = false;
}

/// Outputs to stderr with an icon
fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();

    if self.same_line {
        eprint!("{} {}{}", icon, timestamp, message);
    } else {
        eprintln!("{} {}{}", icon, timestamp, message);
    }

    self.same_line = false;
}

/// Outputs to stdout normally
fn output_normal<T: Display>(&mut self, message: T) {
    let timestamp = self.timestamp();

    if self.same_line {
        print!("{}{}", timestamp, message);
    } else {
        println!("{}{}", timestamp, message);
    }

    self.same_line = false;
}

这是该success函数目前如何使用输出函数的方式:

pub fn success<T: Display>(&mut self, message: T) {
    self.output(message, LogIcon::CheckMark);   
} 

这同样适用于所有其他功能,它们要么输出到stderr要么stdout

4

3 回答 3

2

你可以same_line换成line_ending. 与其存储 true,不如存储\n并始终使用print!("... {}", ..., &self.line_ending). 我还将添加一个函数,该函数pop_line_ending()返回存储的行尾并将其清除。

于 2020-02-25T08:26:58.240 回答
2

您可以创建一个对 的实现通用的函数std::io::Writer,并且还接受std::fmt::Arguments,这是在编译时以可以方便地传递的方式应用于输入字符串的格式化参数。

use std::{fmt, fmt::Display, io};

fn write_output<W: io::Write>(&mut self, args: fmt::Arguments, newline: bool, mut writer: W) {
    write!(writer, "{}", args).unwrap();
    if newline {
        write!(writer, "\n").unwrap();
    }
    writer.flush().unwrap();
}

然后在每个调用中传递stdout()stderr()根据需要传递其他参数:

fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{} {}{}", icon, timestamp, message),
        !self.same_line,
        io::stdout(),
    );
    self.same_line = false;
}

fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{} {}{}", icon, timestamp, message),
        !self.same_line,
        io::stderr(),
    );
    self.same_line = false;
}

fn output_normal<T: Display>(&mut self, message: T) {
    let timestamp = self.timestamp();
    self.write_output(
        format_args!("{}{}", timestamp, message),
        !self.same_line,
        io::stdout(),
    );
    self.same_line = false;
}
于 2020-02-25T18:07:12.647 回答
1

我喜欢彼得霍尔的解决方案,但我认为它可以简化。我摆脱了fmt::Arguments参数并传递了消息和可选图标。也去掉了newline参数,直接使用了成员变量。

use std::{fmt, fmt::Display, io};

struct LogIcon {}

impl Display for LogIcon {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "[ICON]")
    }
}

struct Logger {
    same_line: bool,
}

impl Logger {
    fn write_output<T: Display, W: io::Write>(
        &mut self,
        message: T,
        icon: Option<LogIcon>,
        mut writer: W,
    ) {
        if let Some(icon) = icon {
            write!(writer, "{} ", icon);
        }
        write!(writer, "{}{}", self.timestamp(), message);

        if self.same_line {
            write!(writer, "\n").unwrap();
        }
        writer.flush().unwrap();
        self.same_line = false;
    }

    fn output<T: Display>(&mut self, message: T, icon: LogIcon) {
        self.write_output(message, Some(icon), io::stdout());
    }

    fn output_error<T: Display>(&mut self, message: T, icon: LogIcon) {
        self.write_output(message, Some(icon), io::stderr());
    }

    fn output_normal<T: Display>(&mut self, message: T) {
        self.write_output(message, None, io::stdout())
    }

    fn timestamp(&self) -> &'static str {
        "A TIMESTAMP"
    }
}
于 2020-02-26T04:24:20.300 回答