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

Code before:
        json::from_str(&contents).ok()?
    };

    let raw_version = json_content.get("version")?.as_str()?;
    format_version(raw_version, config.version_format)
}

fn get_poetry_version(pyproject: &toml::Table) -> Option<String> {
    pyproject
        .get("tool")?
        .get("poetry")?
        .get("version")?
        .as_str()
        .map(|s| s.to_owned())
}

fn parse_file_version(context: &Context, path: &str) -> Option<String> {
    let file_contents = read_file(context.current_dir.join(path)).ok()?;
    let re = Regex::new(r#"(__version__|VERSION)\s*=\s*["']([^"']+)["']"#).ok()?;
    Some(
        re.captures(&file_contents)
            .and_then(|cap| cap.get(2))?
            .as_str()
            .to_owned(),
    )
}

fn parse_hatchling_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    let version_path = pyproject
        .get("tool")?
        .get("hatch")?
        .get("version")?
        .get("path")?
        .as_str()?;
    // TODO: custom regex pattern
    parse_file_version(context, version_path)
}

fn parse_pep621_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    // TODO: Flit, PDM, Setuptools
    parse_hatchling_dynamic_version(context, pyproject)
}

fn get_pep621_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    pyproject
        .get("project")?
        .get("dynamic")?
        .as_array()?
        .iter()
        .any(|v| v.as_str() == Some("version"))
        .then(|| parse_pep621_dynamic_version(context, pyproject))?

Review comment (suggestion):
based on my limited Rust knowledge, it seems that we need to return a `String` rather than an `&str` here otherwise there would be a dangling reference to the file contents which is local to this function. The other nearby functions are modified to return `Option<String>` rather than `Option<&str>` accordingly.

Code after:
        jsonc_parser::parse_to_serde_value(&contents, &ParseOptions::default()).ok()??
    } else {
        json::from_str(&contents).ok()?
    };

    let raw_version = json_content.get("version")?.as_str()?;
    format_version(raw_version, config.version_format)
}

fn get_poetry_version(pyproject: &toml::Table) -> Option<String> {
    pyproject
        .get("tool")?
        .get("poetry")?
        .get("version")?
        .as_str()
        .map(|s| s.to_owned())
}

fn parse_file_version_for_hatchling(context: &Context, path: &str) -> Option<String> {
    let file_contents = read_file(context.current_dir.join(path)).ok()?;
    // https://hatch.pypa.io/latest/version/
    let re = Regex::new(r#"(__version__|VERSION)\s*=\s*["']([^"']+)["']"#).ok()?;
    Some(
        re.captures(&file_contents)
            .and_then(|cap| cap.get(2))?
            .as_str()
            .to_owned(),
    )
}

fn parse_hatchling_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    let version_path = pyproject
        .get("tool")?
        .get("hatch")?
        .get("version")?
        .get("path")?
        .as_str()?;

    parse_file_version_for_hatchling(context, version_path)
        .filter(|s| Version::new(s.as_str()).is_some())
}

fn parse_pep621_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    // TODO: Flit, PDM, Setuptools
    // https://packaging.python.org/en/latest/discussions/single-source-version#build-system-version-handling
    parse_hatchling_dynamic_version(context, pyproject)
}

fn get_pep621_dynamic_version(context: &Context, pyproject: &toml::Table) -> Option<String> {
    pyproject
        .get("project")?