1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//! The cli entrypoint for the `query` subcommand

use std::fs;
use std::path::PathBuf;

use anyhow::Result;
use clap::Parser;

use crate::config::Config;
use crate::context::Context;
use crate::lockfile::Digest;
use crate::metadata::Cargo;
use crate::splicing::SplicingManifest;

/// Command line options for the `query` subcommand
#[derive(Parser, Debug)]
#[clap(about = "Command line options for the `query` subcommand", version)]
pub struct QueryOptions {
    /// The lockfile path for reproducible Cargo->Bazel renderings
    #[clap(long)]
    pub lockfile: PathBuf,

    /// The config file with information about the Bazel and Cargo workspace
    #[clap(long)]
    pub config: PathBuf,

    /// A generated manifest of splicing inputs
    #[clap(long)]
    pub splicing_manifest: PathBuf,

    /// The path to a Cargo binary to use for gathering metadata
    #[clap(long, env = "CARGO")]
    pub cargo: PathBuf,

    /// The path to a rustc binary for use with Cargo
    #[clap(long, env = "RUSTC")]
    pub rustc: PathBuf,
}

/// Determine if the current lockfile needs to be re-pinned
pub fn query(opt: QueryOptions) -> Result<()> {
    // Read the lockfile
    let content = match fs::read_to_string(&opt.lockfile) {
        Ok(c) => c,
        Err(_) => return announce_repin("Unable to read lockfile"),
    };

    // Deserialize it so we can easily compare it with
    let lockfile: Context = match serde_json::from_str(&content) {
        Ok(ctx) => ctx,
        Err(_) => return announce_repin("Could not load lockfile"),
    };

    // Check to see if a digest has been set
    let digest = match &lockfile.checksum {
        Some(d) => d.clone(),
        None => return announce_repin("No digest provided in lockfile"),
    };

    // Load the config file
    let config = Config::try_from_path(&opt.config)?;

    let splicing_manifest = SplicingManifest::try_from_path(&opt.splicing_manifest)?;

    // Generate a new digest so we can compare it with the one in the lockfile
    let expected = Digest::new(
        &lockfile,
        &config,
        &splicing_manifest,
        &Cargo::new(opt.cargo),
        &opt.rustc,
    )?;
    if digest != expected {
        return announce_repin(&format!("Digests do not match: {digest:?} != {expected:?}",));
    }

    // There is no need to repin
    Ok(())
}

fn announce_repin(reason: &str) -> Result<()> {
    eprintln!("{reason}");
    println!("repin");
    Ok(())
}