Как я могу упростить логику повторяющихся функций - PullRequest
1 голос
/ 25 февраля 2020

Вопрос

Мне интересно, есть ли способ улучшить текущую структуру некоторых функций в моей программе, поскольку я чувствую, что происходит довольно много нежелательных повторений.

Справочная информация

Я пишу крошечный регистратор, чтобы приложения CLI могли иметь более симпатичный текст в терминале. У меня есть пара функций, которые добавляют некоторые значки к тому, что собирается в стандартный вывод, например success(), он принимает сообщение и добавляет к нему зеленую галочку, также как и error(), warn() et c. Все они могут либо добавить символ новой строки в конце, либо игнорировать его в зависимости от того, вызвал ли пользователь 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.

Ответы [ 3 ]

2 голосов
/ 25 февраля 2020

Вы можете создать функцию, которая будет c обобщенной для реализаций 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 голосов
/ 25 февраля 2020

Вы можете изменить same_line на line_ending. Вместо того, чтобы хранить true, вы должны хранить \n и всегда использовать print!("... {}", ..., &self.line_ending). Я также добавил бы функцию pop_line_ending(), которая возвращает окончание сохраненной строки и очищает его.

1 голос
/ 26 февраля 2020

Мне нравится решение Питера Холла, но я думаю, что его можно упростить. Я избавился от аргумента 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"
    }
}
...