Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
8a0c75b
update to use latest scale-decode (from branch)
jsdw Feb 7, 2023
05c472b
dep from git branch, not local
jsdw Feb 7, 2023
1fbc5a5
zero-length composites always unnamed
jsdw Feb 15, 2023
7872209
reqrite Value encoding to lean on EncodeAsType for encoding parity wi…
jsdw Feb 16, 2023
297a91d
Value to impl IntoVisitor too (to get DecodeAsType), and remove TypeI…
jsdw Feb 21, 2023
8e31bd3
use scale-encode 0.0.17
jsdw Feb 23, 2023
0b7f1c8
fix path change in scale-encode
jsdw Feb 23, 2023
97e2daa
EncodeAsFields and DecodeAsFields impls
jsdw Feb 24, 2023
3017d8b
consume any leftover bytes just incase
jsdw Feb 24, 2023
4bf80f6
consume bytes fully when decode_as_fields
jsdw Feb 24, 2023
40d8b34
scale-decode only on Composite, not Value
jsdw Feb 27, 2023
108573c
Custom Composite encoding; don't rely on scale_encode::Composite so much
jsdw Feb 28, 2023
e2414d3
bump scale-encode to 0.0.18
jsdw Feb 28, 2023
910ab14
bump scale-encode to 0.1
jsdw Feb 28, 2023
0bccbc9
Have a pass over the docs
jsdw Mar 2, 2023
0fbc780
tabs to spaces to make doc comment writing less annoying/for consiste…
jsdw Mar 2, 2023
91edfc1
actually run doc tests in CI
jsdw Mar 2, 2023
c9439ff
tweak release notes to ensure doc tests are run
jsdw Mar 2, 2023
77ea82f
add codeowners file
jsdw Mar 2, 2023
a7185e3
fix failing doc test
jsdw Mar 3, 2023
18d30b3
be a little more defensive and skip bytes even if our visitor impl er…
jsdw Mar 3, 2023
cbdc8b3
fix doc typo
jsdw Mar 3, 2023
3fbfc07
use released version of scale-decode
jsdw Mar 13, 2023
2c23eb5
nits and fixes, and remove stray tabs that cargo fmt doesn't seem to …
jsdw Mar 13, 2023
cba75f2
Actually, use matches and avoid expects
jsdw Mar 13, 2023
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
6 changes: 6 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ jobs:
command: test
args: --all-targets --workspace

- name: Cargo test docs
uses: actions-rs/[email protected]
with:
command: test
args: --doc --workspace

clippy:
name: Cargo clippy
runs-on: ubuntu-latest
Expand Down
3 changes: 2 additions & 1 deletion .rustfmt.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
hard_tabs = true
hard_tabs = false
Copy link
Contributor

@niklasad1 niklasad1 Mar 9, 2023

Choose a reason for hiding this comment

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

👀

are you tired of tabs? :)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's mainly just that subxt, scale-decode and scale-encode all use spaces :)

But also, a really annoying thing I run into with tabs is making doc examples; I can't remember the exact issue but something like the IDE tries to make things tabs but cargo complains about tabs in doc comments; either way it all "just works" with spaces I guess (and spaces also seem to be the default anyway)

tab_spaces = 4
max_width = 100
use_small_heuristics = "Max"
edition = "2021"
5 changes: 5 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# main codeowner @paritytech/tools-team
* @paritytech/tools-team

# CI
/.github/ @paritytech/ci @paritytech/tools-team
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ serde = { version = "1.0.124", features = ["derive"], optional = true }
frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] }
thiserror = "1.0.24"
scale-info = { version = "2.0.0", default-features = false, features = ["std"] }
scale-decode = "0.4.0"
scale-decode = { version = "0.5.0", default-features = false }
scale-encode = { version = "0.1.0", default-features = false, features = ["bits"] }
scale-bits = "0.3.0"
either = "1.6.1"
yap = { version = "0.7.2", optional = true }
Expand Down
7 changes: 5 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ These steps assume that you've checked out the `scale-value` repository and are

Checkout `main`, ensuring we're looking at that latest merge (`git pull`).

Next, do a dry run to make sure that things seem sane:
Next, do a final sanity check to make sure there are no new issues:
```
cargo publish --dry-run
cargo fmt
cargo clippy --all-targets
cargo test --all-targets
cargo test --doc
```

If we're happy with everything, proceed with the release:
Expand Down
267 changes: 135 additions & 132 deletions src/at.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,61 +56,61 @@ use super::{Composite, Value, ValueDef, Variant};
/// assert_eq!(val.at("wibble").at("wobble").at("nope"), None);
/// ```
pub trait At<Ctx>: private::Sealed {
/// Index into a value, returning a reference to the value if one
/// exists, or [`None`] if not.
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>>;
/// Index into a value, returning a reference to the value if one
/// exists, or [`None`] if not.
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>>;
}

// Prevent users from implementing the At trait.
mod private {
use super::*;
pub trait Sealed {}
impl<Ctx> Sealed for Value<Ctx> {}
impl<Ctx> Sealed for Composite<Ctx> {}
impl<Ctx> Sealed for Variant<Ctx> {}
impl<T: Sealed> Sealed for Option<&T> {}
use super::*;
pub trait Sealed {}
impl<Ctx> Sealed for Value<Ctx> {}
impl<Ctx> Sealed for Composite<Ctx> {}
impl<Ctx> Sealed for Variant<Ctx> {}
impl<T: Sealed> Sealed for Option<&T> {}
}

impl<Ctx> At<Ctx> for Composite<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match loc.as_location().inner {
LocationInner::Str(s) => match self {
Composite::Named(vals) => {
vals.iter().find_map(|(n, v)| if s == n { Some(v) } else { None })
}
_ => None,
},
LocationInner::Usize(n) => match self {
Composite::Named(vals) => {
let val = vals.get(n);
val.map(|v| &v.1)
}
Composite::Unnamed(vals) => vals.get(n),
},
}
}
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match loc.as_location().inner {
LocationInner::Str(s) => match self {
Composite::Named(vals) => {
vals.iter().find_map(|(n, v)| if s == n { Some(v) } else { None })
}
_ => None,
},
LocationInner::Usize(n) => match self {
Composite::Named(vals) => {
let val = vals.get(n);
val.map(|v| &v.1)
}
Composite::Unnamed(vals) => vals.get(n),
},
}
}
}

impl<Ctx> At<Ctx> for Variant<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.values.at(loc)
}
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.values.at(loc)
}
}

impl<Ctx> At<Ctx> for Value<Ctx> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match &self.value {
ValueDef::Composite(c) => c.at(loc),
ValueDef::Variant(v) => v.at(loc),
_ => None,
}
}
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
match &self.value {
ValueDef::Composite(c) => c.at(loc),
ValueDef::Variant(v) => v.at(loc),
_ => None,
}
}
}

impl<Ctx, T: At<Ctx>> At<Ctx> for Option<&T> {
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.as_ref().and_then(|v| v.at(loc))
}
fn at<L: AsLocation>(&self, loc: L) -> Option<&Value<Ctx>> {
self.as_ref().and_then(|v| v.at(loc))
}
}

/// Types which can be used as a lookup location with [`At::at`]
Expand All @@ -119,122 +119,125 @@ impl<Ctx, T: At<Ctx>> At<Ctx> for Option<&T> {
/// Users cannot implement this as the [`Location`] type internals
/// are opaque and subject to change.
pub trait AsLocation {
fn as_location(&self) -> Location<'_>;
fn as_location(&self) -> Location<'_>;
}

impl AsLocation for usize {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Usize(*self) }
}
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Usize(*self) }
}
}

impl AsLocation for &str {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(self) }
}
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(self) }
}
}

impl AsLocation for String {
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(&**self) }
}
fn as_location(&self) -> Location<'_> {
Location { inner: LocationInner::Str(self) }
}
}

impl<T: AsLocation> AsLocation for &T {
fn as_location(&self) -> Location<'_> {
(*self).as_location()
}
fn as_location(&self) -> Location<'_> {
(*self).as_location()
}
}

/// A struct representing a location to access in a [`Value`].
#[derive(Copy, Clone)]
pub struct Location<'a> {
inner: LocationInner<'a>,
inner: LocationInner<'a>,
}

#[derive(Copy, Clone)]
enum LocationInner<'a> {
Usize(usize),
Str(&'a str),
Usize(usize),
Str(&'a str),
}

#[cfg(test)]
mod test {
use super::*;

// This is basically the doc example with a little extra.
#[test]
fn nested_accessing() {
let val = Value::named_composite([(
"hello",
Value::unnamed_composite([
Value::u128(1),
Value::bool(true),
Value::named_composite([
("wibble", Value::bool(false)),
("foo", Value::named_composite([("bar", Value::u128(123))])),
]),
]),
)]);

assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
assert_eq!(val.at("hello").at(1), Some(&Value::bool(true)));
assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false)));
assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123)));

assert_eq!(val.at("wibble").at(3), None);
assert_eq!(val.at("wibble").at("wobble").at("nope"), None);

// Strings can be used:
assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
// References to valid locations are fine too:
assert_eq!(val.at(&&"hello").at(&&&0), Some(&Value::u128(1)));
}

#[test]
fn accessing_variants() {
let val = Value::named_variant(
"TheVariant",
[("foo", Value::u128(12345)), ("bar", Value::char('c'))],
);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Value::unnamed_variant("TheVariant", [Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');

// We can use `at()` on the variant directly, too:

let val = Variant::named_fields(
"TheVariant",
[("foo", Value::u128(12345)), ("bar", Value::char('c'))],
);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Variant::unnamed_fields("TheVariant", [Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}

#[test]
fn accessing_composites() {
// We already test accessing composite Values. This also checks that `at` works on
// the Composite type, too..

let val = Composite::named([("foo", Value::u128(12345)), ("bar", Value::char('c'))]);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Composite::unnamed([Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}
use super::*;

// This is basically the doc example with a little extra.
#[test]
fn nested_accessing() {
let val = Value::named_composite([(
"hello",
Value::unnamed_composite([
Value::u128(1),
Value::bool(true),
Value::named_composite([
("wibble", Value::bool(false)),
("foo", Value::named_composite([("bar", Value::u128(123))])),
]),
]),
)]);

assert_eq!(val.at("hello").at(0), Some(&Value::u128(1)));
assert_eq!(val.at("hello").at(1), Some(&Value::bool(true)));
assert_eq!(val.at("hello").at(2).at("wibble"), Some(&Value::bool(false)));
assert_eq!(val.at("hello").at(2).at("foo").at("bar"), Some(&Value::u128(123)));

assert_eq!(val.at("wibble").at(3), None);
assert_eq!(val.at("wibble").at("wobble").at("nope"), None);

// Strings can be used:
assert_eq!(val.at("hello".to_string()).at(0), Some(&Value::u128(1)));
// References to valid locations are fine too:
#[allow(clippy::needless_borrow)]
{
assert_eq!(val.at(&"hello").at(&0), Some(&Value::u128(1)));
}
}

#[test]
fn accessing_variants() {
let val = Value::named_variant(
"TheVariant",
[("foo", Value::u128(12345)), ("bar", Value::char('c'))],
);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Value::unnamed_variant("TheVariant", [Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');

// We can use `at()` on the variant directly, too:

let val = Variant::named_fields(
"TheVariant",
[("foo", Value::u128(12345)), ("bar", Value::char('c'))],
);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Variant::unnamed_fields("TheVariant", [Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}

#[test]
fn accessing_composites() {
// We already test accessing composite Values. This also checks that `at` works on
// the Composite type, too..

let val = Composite::named([("foo", Value::u128(12345)), ("bar", Value::char('c'))]);

assert_eq!(val.at("foo").unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at("bar").unwrap().as_char().unwrap(), 'c');

let val = Composite::unnamed([Value::u128(12345), Value::char('c')]);

assert_eq!(val.at(0).unwrap().as_u128().unwrap(), 12345);
assert_eq!(val.at(1).unwrap().as_char().unwrap(), 'c');
}
}
Loading