Skip to content
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
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ debug_assert_with_mut_call = "warn"
anyhow = "1.0.98"
insta = { version = "1.43.1" }
bitvec = "1.0.1"
capnp = "0.20.6"
capnp = "0.21.3"
cgmath = "0.18.0"
cool_asserts = "2.0.3"
delegate = "0.13.4"
Expand Down
6 changes: 6 additions & 0 deletions hugr-model/capnp/hugr-v0.capnp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ using LinkIndex = UInt32;

struct Package {
modules @0 :List(Module);
version @1 :Version;
}

struct Version {
major @0 :UInt32;
minor @1 :UInt32;
}

struct Module {
Expand Down
416 changes: 349 additions & 67 deletions hugr-model/src/capnp/hugr_v0_capnp.rs

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions hugr-model/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
mod capnp;

pub mod v0;
mod version;
pub use version::*;

// This is required here since the generated code assumes it's in the package root.
use capnp::hugr_v0_capnp;
27 changes: 26 additions & 1 deletion hugr-model/src/v0/binary/read.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::capnp::hugr_v0_capnp as hugr_capnp;
use crate::v0 as model;
use crate::v0::table;
use crate::{Version, v0 as model};
use bumpalo::Bump;
use bumpalo::collections::Vec as BumpVec;
use std::io::BufRead;
Expand All @@ -13,6 +13,15 @@ pub enum ReadError {
#[from(forward)]
/// An error encountered while decoding a model from a `capnproto` buffer.
DecodingError(capnp::Error),

/// The file could not be read due to a version mismatch.
#[display("Can not read file with version {actual} (tooling version {current}).")]
VersionError {
/// The current version of the hugr-model format.
current: Version,
/// The version of the hugr-model format in the file.
actual: Version,
},
}

type ReadResult<T> = Result<T, ReadError>;
Expand Down Expand Up @@ -58,6 +67,16 @@ fn read_package<'a>(
bump: &'a Bump,
reader: hugr_capnp::package::Reader,
) -> ReadResult<table::Package<'a>> {
let version = read_version(reader.get_version()?)?;
let current_version = Version::current();

if version.major != current_version.major || version.minor > current_version.minor {
return Err(ReadError::VersionError {
current: current_version,
actual: version,
});
}

let modules = reader
.get_modules()?
.iter()
Expand All @@ -67,6 +86,12 @@ fn read_package<'a>(
Ok(table::Package { modules })
}

fn read_version(reader: hugr_capnp::version::Reader) -> ReadResult<Version> {
let major = reader.get_major();
let minor = reader.get_minor();
Ok(Version { minor, major })
}

fn read_module<'a>(
bump: &'a Bump,
reader: hugr_capnp::module::Reader,
Expand Down
10 changes: 8 additions & 2 deletions hugr-model/src/v0/binary/write.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::io::Write;

use crate::Version;
use crate::capnp::hugr_v0_capnp as hugr_capnp;
use crate::v0 as model;
use crate::v0::table;
use crate::v0::{self as model, table};

/// An error encounter while serializing a model.
#[derive(Debug, derive_more::From, derive_more::Display, derive_more::Error)]
Expand Down Expand Up @@ -47,6 +47,12 @@ pub fn write_to_vec(package: &table::Package) -> Vec<u8> {

fn write_package(mut builder: hugr_capnp::package::Builder, package: &table::Package) {
write_list!(builder, init_modules, write_module, package.modules);
write_version(builder.init_version(), Version::current());
}

fn write_version(mut builder: hugr_capnp::version::Builder, version: Version) {
builder.set_major(version.major);
builder.set_minor(version.minor);
}

fn write_module(mut builder: hugr_capnp::module::Builder, module: &table::Module) {
Expand Down
74 changes: 74 additions & 0 deletions hugr-model/src/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
use derive_more::derive::Display;
use std::{str::FromStr, sync::LazyLock};
use thiserror::Error;

/// A version number.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Display)]
#[display("{major}.{minor}")]
pub struct Version {
/// The major part of the version.
pub major: u32,
/// The minor part of the version.
pub minor: u32,
}

impl Version {
/// The current version of the HUGR model format.
pub fn current() -> Self {
static VERSION: LazyLock<Version> = LazyLock::new(|| {
include_str!("../version.txt")
.trim()
.parse()
Comment on lines +20 to +21
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a const trim_ascii, but const u32::from_ascii is still in nightly u.u

.expect("`version.txt` in `hugr-model` contains version that fails to parse")
});

*VERSION
}
}

impl FromStr for Version {
type Err = VersionParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let (major, minor) = s.split_once(".").ok_or(VersionParseError)?;
let major = major.parse().map_err(|_| VersionParseError)?;
let minor = minor.parse().map_err(|_| VersionParseError)?;
Ok(Self { major, minor })
}
}

/// Error when parsing a [`Version`].
#[derive(Debug, Clone, Error)]
#[error("failed to parse version")]
pub struct VersionParseError;

#[cfg(test)]
mod test {
use super::Version;
use std::{hint::black_box, str::FromStr};

#[test]
fn test_parse() {
assert_eq!(
Version::from_str("0.1").unwrap(),
Version { major: 0, minor: 1 }
);
assert_eq!(
Version::from_str("1337.0").unwrap(),
Version {
major: 1337,
minor: 0
}
);
assert!(Version::from_str("0").is_err());
assert!(Version::from_str("0.").is_err());
assert!(Version::from_str("0.1.").is_err());
assert!(Version::from_str("0...").is_err());
assert!(Version::from_str("").is_err());
}

#[test]
fn test_current() {
black_box(Version::current());
}
}
1 change: 1 addition & 0 deletions hugr-model/version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.0
Loading