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

Code before:
        formatter
            .map_meta(|variable, _| match variable {
                "symbol" => Some(config.symbol),
                _ => None,
            })
            .map_style(|variable| match variable {
                "style" => Some(Ok(config.style)),
                _ => None,
            })
            .map(|variable| match variable {
                "version" => {
                    let mojo_version_output = context.exec_cmd("mojo", &["--version"])?.stdout;
                    let version_items = mojo_version_output.split(' ').collect::<Vec<&str>>();

                    if version_items.len() <= 1 {
                        return None;
                    }

                    if version_items.len() == 2 || !config.show_commit {
                        return Some(Ok(version_items[1].trim().to_string()));
                    }

                    let mojo_version = version_items[1].trim();
                    let mojo_hash = version_items[2].trim();

                    Some(Ok(format!("{} {}", mojo_version, mojo_hash)))
                }
                _ => None,
            })
            .parse(None, Some(context))
    });

    module.set_segments(match parsed {
        Ok(segments) => segments,
        Err(error) => {
            log::warn!("Error in module `mojo`:\n{}", error);
            return None;
        }
    });

    Some(module)
}

#[cfg(test)]
mod tests {
    use crate::configs::mojo::MOJO_DEFAULT_COLOR;
    use crate::test::ModuleRenderer;
    use crate::utils::CommandOutput;
    use std::fs::File;
    use std::io;


Review comment (performance):
I think it might be better to have separate `$version` and `$hash` variables. To cache the output/parsed version you can use `once_cell::sync::Lazy`.

Code after:
    }

    let version_hash = Lazy::new(|| get_mojo_version(context));

    let parsed = StringFormatter::new(config.format).and_then(|formatter| {
        formatter
            .map_meta(|variable, _| match variable {
                "symbol" => Some(config.symbol),
                _ => None,
            })
            .map_style(|variable| match variable {
                "style" => Some(Ok(config.style)),
                _ => None,
            })
            .map(|variable| match variable {
                "version" => match version_hash.deref() {
                    Some((version, _)) => Some(Ok(version)),
                    _ => None,
                },
                "hash" => match version_hash.deref() {
                    Some((_, Some(hash))) => Some(Ok(hash)),
                    _ => None,
                },
                _ => None,
            })
            .parse(None, Some(context))
    });

    module.set_segments(match parsed {
        Ok(segments) => segments,
        Err(error) => {
            log::warn!("Error in module `mojo`:\n{}", error);
            return None;
        }
    });

    Some(module)
}

fn get_mojo_version(context: &Context) -> Option<(String, Option<String>)> {
    let mojo_version_output = context.exec_cmd("mojo", &["--version"])?.stdout;

    let version_items = mojo_version_output
        .split_ascii_whitespace()
        .collect::<Vec<&str>>();

    let (version, hash) = match version_items[..] {
        [_, version] => (version.trim().to_string(), None),
        [_, version, hash, ..] => (version.trim().to_string(), Some(hash.trim().to_string())),
        _ => {
            log::debug!("Unexpected `mojo --version` output: {mojo_version_output}");