Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/znippy

plugins/native/znippy-plugin-maven/src/native.rs

use std::collections::HashMap;
use znippy_common::arrow::datatypes::{DataType, Field};
use znippy_common::plugin::{ArchiveTypePlugin, ExtensionRow, ExtensionValue, Claim, HandlerSpec};
use crate::pom::parse_pom_project;

pub struct NativeMavenPlugin;

/// The maven handler, stated once. Its columns come from `row::MAVEN_SCHEMA`
/// (the list the wasm `plugin_schema` export serializes too), so `fields` stays
/// empty here and `schema_fields` reads that one list.
pub static MAVEN_SPEC: HandlerSpec = HandlerSpec {
    name: "maven",
    aliases: &["java"],
    type_id: 3,
    ecosystem: "Java / Maven Central",
    extensions: crate::row::MAVEN_EXTENSIONS,
    claim: Claim::Extensions,
    description: "Java Maven artifacts — GAV coordinates from embedded pom.xml or a .pom",
    fields: &[],
    commands: &[],
};

impl ArchiveTypePlugin for NativeMavenPlugin {
    fn spec(&self) -> &HandlerSpec {
        &MAVEN_SPEC
    }

    /// Columns this handler contributes — the maven coords the read-side
    /// [`MavenView`](znippy_common::views::MavenView) resolves on. `classifier`
    /// (e.g. `sources`/`javadoc`) is derived from the request path when a coord
    /// has more than the GAV.
    fn schema_fields(&self) -> Vec<Field> {
        // Built from the SAME `MAVEN_SCHEMA` the wasm `plugin_schema` export
        // serializes, so neither surface can grow a column the other lacks.
        crate::row::MAVEN_SCHEMA
            .iter()
            .map(|(name, tag)| {
                let dt = match *tag {
                    "u32" => DataType::UInt32,
                    "i8" => DataType::Int8,
                    _ => DataType::Utf8,
                };
                Field::new(*name, dt, true)
            })
            .collect()
    }

    fn extract_metadata(&self, path: &str, data: &[u8]) -> Option<ExtensionRow> {
        let coord = if path.ends_with(".pom") {
            parse_pom_project(data)?
        } else {
            let entries = ljar::decompress_jar_filter(data, "pom.xml").ok()?;
            let entry = entries
                .iter()
                .find(|e| e.name.starts_with("META-INF/maven/") && e.name.ends_with("/pom.xml"))?;
            parse_pom_project(&entry.data)?
        };

        Some(make_row(crate::row::maven_fields(coord, path)))
    }
}

/// Arrow-side serialization of the shared field list from [`crate::row`].
///
/// The LAST column, `classifier`, is the only optional one and lands as
/// `OptStr` so a primary artifact writes a null cell rather than an empty
/// string. Everything else is a plain `Str`; a `None` in a non-optional column
/// would mean the field writer changed, so it serializes to `OptStr(None)` too
/// rather than being silently dropped.
fn make_row(fields: Vec<(&'static str, Option<String>)>) -> ExtensionRow {
    let mut out = HashMap::new();
    for (name, value) in fields {
        let v = match value {
            Some(s) => ExtensionValue::Str(s),
            None => ExtensionValue::OptStr(None),
        };
        out.insert(name.to_string(), v);
    }
    ExtensionRow { fields: out }
}