Repository: starship/starship
File: src/modules/git_status.rs (Rust)

Code before:
    fn add(&mut self, s: &str) {
        match s.chars().next() {
            Some('1') => self.parse_normal_status(&s[2..4]),
            Some('2') => {
                self.renamed += 1;
                self.parse_normal_status(&s[2..4])
            }
            Some('u') => self.conflicted += 1,
            Some('?') => self.untracked += 1,
            Some('!') => (),
            Some(_) => log::error!("Unknown line type in git status output"),
            None => log::error!("Missing line type in git status output"),
        }
    }

    fn set_ahead_behind(&mut self, s: &str) {
        let re = Regex::new(r"branch\.ab \+([0-9]+) \-([0-9]+)").unwrap();

        if let Some(caps) = re.captures(s) {
            self.ahead = caps.get(1).unwrap().as_str().parse::<usize>().ok();
            self.behind = caps.get(2).unwrap().as_str().parse::<usize>().ok();
        }
    }

    fn set_ahead_behind_for_each_ref(&mut self, mut s: &str) {
        let re = Regex::new(r"^([^ ]+) (\[(.*)\])?$").unwrap();
        if let Some(caps) = re.captures(s) {
            let ahead_behind_match = caps.get(3);
            if let Some(ahead_behind_match) = ahead_behind_match {
                s = ahead_behind_match.as_str();

                if s == "gone" {
                    self.ahead = None;
                    self.behind = None;
                    return;
                }

                for pair in s.split(',') {
                    let mut tokens = pair.trim().splitn(2, ' ');
                    if let (Some(name), Some(number)) = (tokens.next(), tokens.next()) {
                        let storage = match name {
                            "ahead" => &mut self.ahead,
                            "behind" => &mut self.behind,
                            _ => return,
                        };
                        *storage = number.parse().ok();
                    }
                }
            }
            for field in [&mut self.ahead, &mut self.behind] {
                if field.is_none() {

Review comment (performance):
This regex should not be recreated for every invocation, please try to avoid regexes or if that is not possible cache the regex.

Code after:
    fn add(&mut self, s: &str) {
        match s.chars().next() {
            Some('1') => self.parse_normal_status(&s[2..4]),
            Some('2') => {
                self.renamed += 1;
                self.parse_normal_status(&s[2..4])
            }
            Some('u') => self.conflicted += 1,
            Some('?') => self.untracked += 1,
            Some('!') => (),
            Some(_) => log::error!("Unknown line type in git status output"),
            None => log::error!("Missing line type in git status output"),
        }
    }

    fn set_ahead_behind(&mut self, s: &str) {
        let re = Regex::new(r"branch\.ab \+([0-9]+) \-([0-9]+)").unwrap();

        if let Some(caps) = re.captures(s) {
            self.ahead = caps.get(1).unwrap().as_str().parse::<usize>().ok();
            self.behind = caps.get(2).unwrap().as_str().parse::<usize>().ok();
        }
    }

    fn set_ahead_behind_for_each_ref(&mut self, mut s: &str) {
        if s == " " || s.ends_with(" [gone]") {
            self.ahead = None;
            self.behind = None;
            return;
        }

        s = s
            .split_once(' ')
            .unwrap()
            .1
            .trim_matches(|c| c == '[' || c == ']');

        for pair in s.split(',') {
            let mut tokens = pair.trim().splitn(2, ' ');
            if let (Some(name), Some(number)) = (tokens.next(), tokens.next()) {
                let storage = match name {
                    "ahead" => &mut self.ahead,
                    "behind" => &mut self.behind,
                    _ => return,
                };
                *storage = number.parse().ok();
            }
        }
        for field in [&mut self.ahead, &mut self.behind] {
            if field.is_none() {
                *field = Some(0);