#![forbid(unsafe_code)]
#![allow(clippy::redundant_field_names)]
#![doc = include_str!("../README.md")]
mod validation;
use validation::RawVersionInfo;
use serde::{Deserialize, Serialize};
use std::str::FromStr;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[serde(try_from = "RawVersionInfo")]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct VersionInfo {
pub packages: Vec<Package>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct Package {
pub name: String,
#[cfg_attr(feature = "schema", schemars(with = "String"))]
pub version: semver::Version,
pub source: Source,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
pub kind: DependencyKind,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
pub dependencies: Vec<usize>,
#[serde(default)]
#[serde(skip_serializing_if = "is_default")]
pub root: bool,
}
#[non_exhaustive]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[serde(from = "&str")]
#[serde(into = "String")]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum Source {
CratesIo,
Git,
Local,
Registry,
Other(String),
}
impl From<&str> for Source {
fn from(s: &str) -> Self {
match s {
"crates.io" => Self::CratesIo,
"git" => Self::Git,
"local" => Self::Local,
"registry" => Self::Registry,
other_str => Self::Other(other_str.to_string()),
}
}
}
impl From<Source> for String {
fn from(s: Source) -> String {
match s {
Source::CratesIo => "crates.io".to_owned(),
Source::Git => "git".to_owned(),
Source::Local => "local".to_owned(),
Source::Registry => "registry".to_owned(),
Source::Other(string) => string,
}
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone, Default)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum DependencyKind {
#[serde(rename = "build")]
Build,
#[default]
#[serde(rename = "runtime")]
Runtime,
}
fn is_default<T: Default + PartialEq>(value: &T) -> bool {
let default_value = T::default();
value == &default_value
}
impl FromStr for VersionInfo {
type Err = serde_json::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
serde_json::from_str(s)
}
}
#[cfg(test)]
mod tests {
#![allow(unused_imports)] use super::*;
use std::fs;
use std::{
convert::TryInto,
path::{Path, PathBuf},
};
#[cfg(feature = "schema")]
fn generate_schema() -> schemars::schema::RootSchema {
let mut schema = schemars::schema_for!(VersionInfo);
let mut metadata = *schema.schema.metadata.clone().unwrap();
let title = "cargo-auditable schema".to_string();
metadata.title = Some(title);
metadata.id = Some("https://rustsec.org/schemas/cargo-auditable.json".to_string());
metadata.examples = [].to_vec();
metadata.description = Some(
"Describes the `VersionInfo` JSON data structure that cargo-auditable embeds into Rust binaries."
.to_string(),
);
schema.schema.metadata = Some(Box::new(metadata));
schema
}
#[test]
#[cfg(feature = "schema")]
fn verify_schema() {
use schemars::schema::RootSchema;
let expected = generate_schema();
println!(
"expected schema:\n{}",
serde_json::to_string_pretty(&expected).unwrap()
);
let contents = fs::read_to_string(
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("cargo-auditable.schema.json"),
)
.expect("error reading existing schema");
let actual: RootSchema =
serde_json::from_str(&contents).expect("error deserializing existing schema");
assert_eq!(expected, actual);
}
}