Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rename VersionedAbiEntry #2

Merged
merged 3 commits into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ schemars = "0.8.8"
serde_json = "1"

[features]
versioned-entries = []
chunked-entries = []
129 changes: 10 additions & 119 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::collections::HashMap;

#[cfg(feature = "chunked-entries")]
mod private;
#[cfg(feature = "chunked-entries")]
pub use private::{AbiCombineError, AbiCombineErrorKind, ChunkedAbiEntry};

use borsh::schema::{BorshSchemaContainer, Declaration, Definition, Fields, VariantName};
use schemars::schema::{RootSchema, Schema};
use serde::{Deserialize, Serialize};

/// Current version of the ABI schema format.
const ABI_SCHEMA_SEMVER: &str = env!("CARGO_PKG_VERSION");
pub const SCHEMA_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Contract ABI.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
Expand All @@ -20,8 +24,8 @@ pub struct AbiRoot {
}

impl AbiRoot {
#[cfg(feature = "versioned-entries")]
pub fn new(metadata: AbiMetadata, versioned_abi: VersionedAbiEntry) -> AbiRoot {
#[cfg(feature = "chunked-entries")]
pub fn new(metadata: AbiMetadata, versioned_abi: ChunkedAbiEntry) -> AbiRoot {
AbiRoot {
abi_schema_version: versioned_abi.abi_schema_version,
metadata,
Expand Down Expand Up @@ -59,119 +63,6 @@ pub struct AbiEntry {
pub root_schema: RootSchema,
}

/// Core ABI information, with schema version.
#[cfg(feature = "versioned-entries")]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct VersionedAbiEntry {
/// Semver of the ABI schema format.
abi_schema_version: String,
#[serde(flatten)]
pub abi: AbiEntry,
}

#[cfg(feature = "versioned-entries")]
impl VersionedAbiEntry {
pub fn new(functions: Vec<AbiFunction>, root_schema: RootSchema) -> VersionedAbiEntry {
Self {
abi_schema_version: ABI_SCHEMA_SEMVER.to_string(),
abi: AbiEntry {
functions,
root_schema,
},
}
}

pub fn combine<I: IntoIterator<Item = VersionedAbiEntry>>(
entries: I,
) -> Result<VersionedAbiEntry, AbiCombineError> {
let mut abi_schema_version = None;
let mut functions = Vec::<AbiFunction>::new();
let mut gen = schemars::gen::SchemaGenerator::default();
let definitions = gen.definitions_mut();

let mut unexpected_versions = HashSet::new();

for entry in entries {
if let Some(ref abi_schema_version) = abi_schema_version {
// should probably only disallow major version mismatch
if abi_schema_version != &entry.abi_schema_version {
unexpected_versions.insert(entry.abi_schema_version.clone());
continue;
}
} else {
abi_schema_version = Some(entry.abi_schema_version);
}

// Update resulting JSON Schema
definitions.extend(entry.abi.root_schema.definitions.to_owned());

// Update resulting function list
functions.extend(entry.abi.functions);
}

if !unexpected_versions.is_empty() {
return Err(AbiCombineError {
kind: AbiCombineErrorKind::SchemaVersionConflict {
expected: abi_schema_version.unwrap(),
found: unexpected_versions.into_iter().collect(),
},
});
}

// Sort the function list for readability
functions.sort_by(|x, y| x.name.cmp(&y.name));

Ok(VersionedAbiEntry {
abi_schema_version: abi_schema_version.unwrap(),
abi: AbiEntry {
functions,
root_schema: gen.into_root_schema_for::<String>(),
},
})
}
}

#[derive(Eq, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AbiCombineError {
#[serde(flatten)]
kind: AbiCombineErrorKind,
}

impl AbiCombineError {
pub fn kind(&self) -> &AbiCombineErrorKind {
&self.kind
}
}

impl std::error::Error for AbiCombineError {}
impl fmt::Display for AbiCombineError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.kind.fmt(f)
}
}

#[derive(Eq, Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AbiCombineErrorKind {
SchemaVersionConflict {
expected: String,
found: Vec<String>,
},
}

impl fmt::Display for AbiCombineErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AbiCombineErrorKind::SchemaVersionConflict { expected, found } => format!(
"ABI schema version conflict: expected {}, found {}",
expected,
found.join(", ")
)
.fmt(f),
}
}
}

/// ABI of a single function.
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
pub struct AbiFunction {
Expand Down Expand Up @@ -276,7 +167,7 @@ mod borsh_clone {
pub fn clone_definition(definition: &Definition) -> Definition {
match definition {
Definition::Array { length, elements } => Definition::Array {
length: length.clone(),
length: *length,
elements: elements.clone(),
},
Definition::Sequence { elements } => Definition::Sequence {
Expand Down
117 changes: 117 additions & 0 deletions src/private.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use std::fmt;

use serde::{Deserialize, Serialize};

use super::{AbiEntry, AbiFunction, RootSchema, SCHEMA_VERSION};

/// Core ABI information, with schema version and identity hash.
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct ChunkedAbiEntry {
/// Semver of the ABI schema format.
pub(crate) abi_schema_version: String,
#[serde(flatten)]
pub abi: AbiEntry,
}

impl ChunkedAbiEntry {
pub fn new(functions: Vec<AbiFunction>, root_schema: RootSchema) -> ChunkedAbiEntry {
Self {
abi_schema_version: SCHEMA_VERSION.to_string(),
abi: AbiEntry {
functions,
root_schema,
},
}
}

pub fn combine<I: IntoIterator<Item = ChunkedAbiEntry>>(
entries: I,
) -> Result<ChunkedAbiEntry, AbiCombineError> {
let mut abi_schema_version = None;
let mut functions = Vec::<AbiFunction>::new();

let mut gen = schemars::gen::SchemaGenerator::default();
let definitions = gen.definitions_mut();

let mut unexpected_versions = std::collections::HashSet::new();

for entry in entries {
if let Some(ref abi_schema_version) = abi_schema_version {
// should probably only disallow major version mismatch
if abi_schema_version != &entry.abi_schema_version {
unexpected_versions.insert(entry.abi_schema_version.clone());
continue;
}
} else {
abi_schema_version = Some(entry.abi_schema_version);
}

// Update resulting JSON Schema
definitions.extend(entry.abi.root_schema.definitions.to_owned());

// Update resulting function list
functions.extend(entry.abi.functions);
}

if !unexpected_versions.is_empty() {
return Err(AbiCombineError {
kind: AbiCombineErrorKind::SchemaVersionConflict {
expected: abi_schema_version.unwrap(),
found: unexpected_versions.into_iter().collect(),
},
});
}

// Sort the function list for readability
functions.sort_by(|x, y| x.name.cmp(&y.name));

Ok(ChunkedAbiEntry {
abi_schema_version: abi_schema_version.unwrap(),
abi: AbiEntry {
functions,
root_schema: gen.into_root_schema_for::<String>(),
},
})
}
}

#[derive(Eq, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct AbiCombineError {
#[serde(flatten)]
kind: AbiCombineErrorKind,
}

impl AbiCombineError {
pub fn kind(&self) -> &AbiCombineErrorKind {
&self.kind
}
}

impl std::error::Error for AbiCombineError {}
impl fmt::Display for AbiCombineError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.kind.fmt(f)
}
}

#[derive(Eq, Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum AbiCombineErrorKind {
SchemaVersionConflict {
expected: String,
found: Vec<String>,
},
}

impl fmt::Display for AbiCombineErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::SchemaVersionConflict { expected, found } => format!(
"ABI schema version conflict: expected {}, found {}",
expected,
found.join(", ")
)
.fmt(f),
}
}
}