-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Stable ordering & fix copy partials (#7)
- Loading branch information
1 parent
16377a8
commit 1b06662
Showing
4 changed files
with
144 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
//! Utilities to help with parsing configuration attributes. | ||
use darling::{Error, FromMeta}; | ||
use syn::{Ident, NestedMeta}; | ||
|
||
/// Parse a list of nested meta items. | ||
/// | ||
/// Useful for passing through attributes intended for other macros. | ||
#[derive(Debug)] | ||
pub struct NestedMetaList { | ||
pub metas: Vec<NestedMeta>, | ||
} | ||
|
||
impl FromMeta for NestedMetaList { | ||
fn from_list(items: &[NestedMeta]) -> Result<Self, Error> { | ||
Ok(Self { | ||
metas: items.iter().cloned().collect(), | ||
}) | ||
} | ||
} | ||
|
||
/// List of identifiers implementing `FromMeta`. | ||
/// | ||
/// Useful for imposing ordering, unlike the `HashMap` options provided by `darling`. | ||
#[derive(Debug)] | ||
pub struct IdentList { | ||
pub idents: Vec<Ident>, | ||
} | ||
|
||
impl FromMeta for IdentList { | ||
fn from_list(items: &[NestedMeta]) -> Result<Self, Error> { | ||
let idents = items | ||
.iter() | ||
.map(|nested_meta| { | ||
let meta = match nested_meta { | ||
NestedMeta::Meta(m) => m, | ||
NestedMeta::Lit(l) => { | ||
return Err(Error::custom(format!( | ||
"expected ident, got literal: {:?}", | ||
l | ||
))) | ||
} | ||
}; | ||
let path = meta.path(); | ||
path.get_ident() | ||
.cloned() | ||
.ok_or(Error::custom(format!("can't parse as ident: {:?}", path))) | ||
}) | ||
.collect::<Result<_, _>>()?; | ||
Ok(Self { idents }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,93 @@ | ||
use serde::Deserialize; | ||
use superstruct::superstruct; | ||
|
||
#[superstruct( | ||
variants(Base, Ext), | ||
variant_attributes(derive(Debug, PartialEq)), | ||
cast_error(ty = "BlockError", expr = "BlockError::WrongVariant") | ||
)] | ||
#[derive(Debug, PartialEq)] | ||
pub struct Block { | ||
#[superstruct(getter(copy))] | ||
slot: u64, | ||
data: Vec<u8>, | ||
#[superstruct(only(Ext))] | ||
description: &'static str, | ||
} | ||
|
||
pub enum BlockError { | ||
WrongVariant, | ||
} | ||
|
||
#[test] | ||
fn basic() { | ||
#[superstruct( | ||
variants(Base, Ext), | ||
variant_attributes(derive(Debug, PartialEq, Clone)), | ||
cast_error(ty = "BlockError", expr = "BlockError::WrongVariant"), | ||
partial_getter_error(ty = "BlockError", expr = "BlockError::WrongVariant") | ||
)] | ||
#[derive(Debug, PartialEq, Clone)] | ||
pub struct Block { | ||
#[superstruct(getter(copy))] | ||
slot: u64, | ||
data: Vec<u8>, | ||
#[superstruct(only(Ext), partial_getter(copy))] | ||
description: &'static str, | ||
} | ||
|
||
#[derive(Debug, PartialEq)] | ||
pub enum BlockError { | ||
WrongVariant, | ||
} | ||
|
||
let base = BlockBase { | ||
slot: 10, | ||
data: vec![], | ||
}; | ||
let lmao = BlockExt { | ||
let ext = BlockExt { | ||
slot: 11, | ||
data: vec![10], | ||
description: "oooeee look at this", | ||
}; | ||
|
||
let mut block1 = Block::Base(base); | ||
let block2 = Block::Ext(lmao); | ||
let mut block1 = Block::Base(base.clone()); | ||
let mut block2 = Block::Ext(ext.clone()); | ||
|
||
// Test basic getters. | ||
assert_eq!(block1.slot(), 10); | ||
assert_eq!(block2.slot(), 11); | ||
|
||
println!("{:?}", block1); | ||
println!("{:?}", block2); | ||
println!("{}", block1.slot()); | ||
// Check ref getters. | ||
let block1_ref = block1.to_ref(); | ||
assert_eq!(block1_ref.slot(), 10); | ||
|
||
let block_ref = block1.to_ref(); | ||
println!("{:?}", block_ref.slot()); | ||
// Check casting | ||
assert_eq!(block1.as_base(), Ok(&base)); | ||
assert_eq!(block1.as_ext(), Err(BlockError::WrongVariant)); | ||
assert_eq!(block2.as_ext(), Ok(&ext)); | ||
assert_eq!(block2.as_base(), Err(BlockError::WrongVariant)); | ||
|
||
// Check mutable reference mutators. | ||
let mut block_mut_ref = block1.to_mut(); | ||
println!("{:?}", block_mut_ref.slot_mut()); | ||
*block_mut_ref.slot_mut() = 1000; | ||
assert_eq!(block1.slot(), 1000); | ||
*block1.slot_mut() = 1001; | ||
assert_eq!(block1.slot(), 1001); | ||
|
||
// Check partial getters. | ||
assert_eq!(block1.description(), Err(BlockError::WrongVariant)); | ||
assert_eq!(block2.description().unwrap(), ext.description); | ||
*block2.description_mut().unwrap() = "updated"; | ||
assert_eq!(block2.description().unwrap(), "updated"); | ||
} | ||
|
||
// Test that superstruct's enum ordering is based on the ordering in `variants(...)`. | ||
// This test fails with variant order (A, B) because A is a subset of B and we're not | ||
// using `serde(deny_unknown_fields)`. | ||
#[test] | ||
fn serde_deserialise_order() { | ||
#[superstruct( | ||
variants(B, A), | ||
variant_attributes(derive(Debug, Deserialize, PartialEq)) | ||
)] | ||
#[serde(untagged)] | ||
#[derive(Debug, Deserialize, PartialEq)] | ||
struct Message { | ||
common: String, | ||
#[superstruct(only(B))] | ||
exclusive: String, | ||
} | ||
|
||
let message_str = r#"{"common": "hello", "exclusive": "world"}"#; | ||
let message: Message = serde_json::from_str(&message_str).unwrap(); | ||
|
||
let expected = Message::B(MessageB { | ||
common: "hello".into(), | ||
exclusive: "world".into(), | ||
}); | ||
|
||
assert_eq!(message, expected); | ||
} |