use super::*;
impl<N: Network> Parser for Record<N, Plaintext<N>> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
#[allow(clippy::type_complexity)]
fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Entry<N, Plaintext<N>>)> {
let (string, _) = Sanitizer::parse(string)?;
let (string, identifier) = Identifier::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag(":")(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, entry) = Entry::parse(string)?;
Ok((string, (identifier, entry)))
}
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("{")(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("owner")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag(":")(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, owner) = alt((
map(pair(Address::parse, tag(".public")), |(owner, _)| Owner::Public(owner)),
map(pair(Address::parse, tag(".private")), |(owner, _)| {
Owner::Private(Plaintext::from(Literal::Address(owner)))
}),
))(string)?;
let (string, _) = tag(",")(string)?;
let (string, entries) = map_res(separated_list0(tag(","), parse_pair), |entries: Vec<_>| {
let reserved = [Identifier::from_str("owner").map_err(|e| error(e.to_string()))?];
if has_duplicates(entries.iter().map(|(identifier, _)| identifier).chain(reserved.iter())) {
return Err(error("Duplicate entry type found in record"));
}
match entries.len() <= N::MAX_DATA_ENTRIES {
true => Ok(entries),
false => Err(error(format!("Found a record that exceeds size ({})", entries.len()))),
}
})(string)?;
let string = match !entries.is_empty() {
true => tag(",")(string)?.0,
false => string,
};
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("_nonce")(string)?;
let (string, _) = tag(":")(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, (nonce, _)) = pair(Group::parse, tag(".public"))(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("}")(string)?;
Ok((string, Record { owner, data: IndexMap::from_iter(entries.into_iter()), nonce }))
}
}
impl<N: Network> FromStr for Record<N, Plaintext<N>> {
type Err = Error;
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network> Debug for Record<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Record<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.fmt_internal(f, 0)
}
}
impl<N: Network> Record<N, Plaintext<N>> {
fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
const INDENT: usize = 2;
write!(f, "{{")?;
write!(f, "\n{:indent$}owner: {},", "", self.owner, indent = (depth + 1) * INDENT)?;
for (identifier, entry) in self.data.iter() {
write!(f, "\n{:indent$}{identifier}: ", "", indent = (depth + 1) * INDENT)?;
match entry {
Entry::Constant(Plaintext::Literal(..))
| Entry::Public(Plaintext::Literal(..))
| Entry::Private(Plaintext::Literal(..)) => write!(f, "{entry}")?,
Entry::Constant(Plaintext::Struct(..))
| Entry::Public(Plaintext::Struct(..))
| Entry::Private(Plaintext::Struct(..)) => entry.fmt_internal(f, depth + 1)?,
}
write!(f, ",")?;
}
write!(f, "\n{:indent$}_nonce: {}.public", "", self.nonce, indent = (depth + 1) * INDENT)?;
write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::Testnet3;
type CurrentNetwork = Testnet3;
#[test]
fn test_parse_without_data_entries() -> Result<()> {
let expected = r"{
owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private,
_nonce: 0group.public
}";
let given =
"{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private, _nonce: 0group.public }";
let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
#[test]
fn test_parse_with_literal_entry() -> Result<()> {
let expected = r"{
owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public,
foo: 5u8.constant,
_nonce: 0group.public
}";
let given = "{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public, foo: 5u8.constant, _nonce: 0group.public }";
let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(given)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
#[test]
fn test_parse_with_struct_entry() -> Result<()> {
let expected = r"{
owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public,
foo: 5u8.public,
bar: {
baz: 6u8.constant,
qux: 7u8.constant
},
quux: 8u8.private,
corge: {
grault: 9u8.constant,
garply: {
waldo: 10u8.constant,
fred: 11u8.constant
}
},
xyzzy: {
thud: 12u8.public
},
_nonce: 2293253577170800572742339369209137467208538700597121244293392265726446806023group.public
}";
let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
let expected = r"{
owner: aleo1lhcpfumagern97esytsgdva2ytme043zydlzyprhejsd0gw5vypqqz0zkw.private,
foo: {
bar: 0u128.private
},
baz: {
quine: {
flop: 0u64.private
},
slice: 0u16.private,
flag: true.private,
square: {
first: 0u128.private,
second: 1u128.private,
third: 2u128.private,
fourth: 3u128.private
}
},
_nonce: 0group.public
}";
let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
let expected = r"{
owner: aleo18ttcegpydcs95yw4je0u400j3u7r26yqr9h8evqps3qa9slrvyrsqjwt9l.private,
c: {
c: {
a: 0u8.private,
b: 1u8.private
},
d: {
a: 0u8.private,
b: 1u8.private
}
},
d: {
c: {
a: 0u8.private,
b: 1u8.private
},
d: {
a: 0u8.private,
b: 1u8.private
}
},
_nonce: 8102307625287186026775464343238779600702564007094834161216556016558567413871group.public
}";
let (remainder, candidate) = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(expected)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
#[test]
fn test_parse_fails() -> Result<()> {
let expected = "{ foo: 5u8.private, _nonce: 0group.public }";
assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
let expected =
"{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.public, foo: 5u8.private }";
assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
let expected = r"{
owner: aleo14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.private,
a: true.private,
b: 123456789field.private,
c: 0group.private,
d: {
e: true.private,
f: 123456789field.public,
g: 0group.private
},
_nonce: 0group.public
}";
assert!(Plaintext::<CurrentNetwork>::parse(expected).is_err());
Ok(())
}
}