如何简化重复功能逻辑

时间:2020-02-25 07:52:29

标签: rust refactoring program-structure

问题

我想知道是否有一种方法可以改善程序中某些函数的当前结构,因为我觉得有很多不必要的重复发生。

背景

我正在编写一个小的记录器,因此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);   
} 

所有其他功能都一样,它们输出到stderrstdout

3 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分: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;
}

答案 2 :(得分:1)

我喜欢Peter Hall的解决方案,但是我认为它可以简化。我摆脱了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"
    }
}