use super::*;
impl<N: Network> Parser for Plaintext<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<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, plaintext) = Plaintext::parse(string)?;
Ok((string, (identifier, plaintext)))
}
fn parse_struct<N: Network>(string: &str) -> ParserResult<Plaintext<N>> {
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("{")(string)?;
let (string, members) = map_res(separated_list1(tag(","), parse_pair), |members: Vec<_>| {
if has_duplicates(members.iter().map(|(name, ..)| name)) {
return Err(error("Duplicate member in struct"));
}
match members.len() <= N::MAX_STRUCT_ENTRIES {
true => Ok(members),
false => Err(error(format!("Found a plaintext that exceeds size ({})", members.len()))),
}
})(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("}")(string)?;
Ok((string, Plaintext::Struct(IndexMap::from_iter(members.into_iter()), Default::default())))
}
let (string, _) = Sanitizer::parse_whitespaces(string)?;
alt((
map(Literal::parse, |literal| Self::Literal(literal, Default::default())),
parse_struct,
))(string)
}
}
impl<N: Network> FromStr for 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 Plaintext<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Plaintext<N> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.fmt_internal(f, 0)
}
}
impl<N: Network> Plaintext<N> {
fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
const INDENT: usize = 2;
match self {
Self::Literal(literal, ..) => write!(f, "{:indent$}{literal}", "", indent = depth * INDENT),
Self::Struct(struct_, ..) => {
write!(f, "{{")?;
struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
match plaintext {
Self::Literal(literal, ..) => match i == struct_.len() - 1 {
true => {
write!(f, "\n{:indent$}{name}: {literal}", "", indent = (depth + 1) * INDENT)?;
write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
}
false => write!(f, "\n{:indent$}{name}: {literal},", "", indent = (depth + 1) * INDENT),
},
Self::Struct(..) => {
write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
plaintext.fmt_internal(f, depth + 1)?;
match i == struct_.len() - 1 {
true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
false => write!(f, ","),
}
}
}
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::Testnet3;
type CurrentNetwork = Testnet3;
#[test]
fn test_parse_literal() -> Result<()> {
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("5u8")?;
assert_eq!("5u8", candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
#[test]
fn test_parse_struct() -> Result<()> {
let expected = r"{
foo: 5u8
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse("{ foo: 5u8 }")?;
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
let expected = r"{
foo: 5u8,
bar: {
baz: 10field,
qux: {
quux: {
corge: {
grault: {
garply: {
waldo: {
fred: {
plugh: {
xyzzy: {
thud: true
}
}
}
}
}
}
}
}
}
}
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(
"{ foo: 5u8, bar: { baz: 10field, qux: {quux:{corge :{grault: {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true}} }}} }}}}}}",
)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
#[test]
fn test_parse_fails() {
assert!(Plaintext::<CurrentNetwork>::parse("").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("{}").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("_").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("__").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("___").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("-").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("--").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("---").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("*").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("**").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("***").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("1").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("2").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("3").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("1foo").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("12").is_err());
assert!(Plaintext::<CurrentNetwork>::parse("111").is_err());
let plaintext =
Plaintext::<CurrentNetwork>::parse("foo_bar_baz_qux_quux_quuz_corge_grault_garply_waldo_fred_plugh_xyzzy");
assert!(plaintext.is_err());
}
#[test]
fn test_nested_structs1() {
let expected = r"{
r1: {
c1: 1u8,
c2: 2u8,
c3: 1u8
},
r2: {
c1: 2u8,
c2: 2u8,
c3: 1u8
},
r3: {
c1: 1u8,
c2: 2u8,
c3: 1u8
}
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
}
#[test]
fn test_nested_structs2() {
let expected = r"{
foo: {
bar: {
baz: 1u8
},
qux: {
quux: 2u8
}
}
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
}
#[test]
fn test_nested_structs3() {
let expected = r"{
c: {
a: 0u8,
b: 1u8
},
d: {
a: 0u8,
b: 1u8
}
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
}
}