#!/usr/bin/perl
#
# Checks that the current package's Vendored-Sources-Rust field is up-to-date

use strict;
use warnings;

use Dpkg::Control::Info;
use JSON::PP;

unless (defined $ENV{CARGO_VENDOR_DIR}) {
    print STDERR "CARGO_VENDOR_DIR environment variable not defined, aborting\n";
    exit 1;
}

my $vendor_dir = $ENV{CARGO_VENDOR_DIR};

sub cargo_info {
    my $src = shift;
    open(F, "cargo metadata --manifest-path $src --no-deps --format-version 1 |");
    local $/;
    my $json = JSON::PP->new;
    my $manifest = $json->decode(<F>);
    close(F);
    my $crate = %{@{%{$manifest}{'packages'}}[0]}{'name'};
    my $version = %{@{%{$manifest}{'packages'}}[0]}{'version'};
    return "$crate\@$version";
}

# Check if the crate has custom entrypoints (non-standard source paths)
sub check_custom_entrypoints {
    my $crate_path = shift;
    return undef unless (-f "$crate_path/Cargo.toml");
    my $src = "$crate_path/Cargo.toml";
    open(F, "cargo metadata --manifest-path $src --no-deps --format-version 1 |");
    my $json = JSON::PP->new;
    my $manifest = $json->decode(<F>);
    close(F);
    foreach my $entry (@{%{@{%{$manifest}{'packages'}}[0]}{'targets'}}) {
        my $target = %{$entry}{'src_path'};
        return 1 if (-f "$target" and -s "$target");
    }
    return 0;
}

# Check if we have one non-empty source code file in the crate
sub has_code {
    my $crate_path = shift;
    my $sourcedh = undef;

    return 1 if (-f "$crate_path/lib.rs" and -s "$crate_path/lib.rs");
    my $has_code = 0;
    if (-d "$crate_path/src") {
        opendir $sourcedh, "$crate_path/src" or die "Cannot open directory '$crate_path/src': $!";
        while (my $entry = readdir($sourcedh)) {
            if (-f "$crate_path/src/$entry" and -s "$crate_path/src/$entry") {
                $has_code = 1;
                last;
            }
        }
        closedir $sourcedh;
    } else {
        $has_code = check_custom_entrypoints("$crate_path");
    }
    return $has_code;
}

my @unsorted_expected = ();

# We operate under the assumption of a flat vendor directory with one crate per subdir,
# as is generated by `cargo vendor`.
opendir my $dh, $vendor_dir or die "Cannot open directory '$vendor_dir': $!";
my @crates = readdir $dh;
foreach my $crate (@crates) {
    next if $crate eq ".";
    next if $crate eq "..";
    next unless -d "$vendor_dir/$crate";
    next unless -e "$vendor_dir/$crate/Cargo.toml";
    next unless has_code("$vendor_dir/$crate");
    push(@unsorted_expected, cargo_info("$vendor_dir/$crate/Cargo.toml"));
}
closedir $dh;

my @expected = sort @unsorted_expected;

my $control = Dpkg::Control::Info->new();
my $source = $control->get_source();

my $actual_raw = $source->get_custom_field('Vendored-Sources-Rust');
my @actual = sort(split(/\s*,\s*/, $actual_raw // ""));

my $expected_formatted = join(', ', @expected);
my $actual_formatted = join(', ', @actual);

if ($expected_formatted ne $actual_formatted) {
    print STDERR "Mismatched XS-Vendored-Sources-Rust field. Expected:\n";
    print STDERR "XS-Vendored-Sources-Rust: $expected_formatted\n";
    exit 2;
}
