use super::*;
impl<N: Network> Parser for Plaintext<N> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
Self::parse_internal(string, 0)
}
}
impl<N: Network> Plaintext<N> {
fn parse_pair(string: &str, depth: usize) -> ParserResult<(Identifier<N>, Self)> {
let (string, _) = Sanitizer::parse(string)?;
let (string, identifier) = Identifier::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag(":")(string)?;
let (string, plaintext) = Self::parse_internal(string, depth + 1)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
Ok((string, (identifier, plaintext)))
}
fn parse_struct(string: &str, depth: usize) -> ParserResult<Self> {
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("{")(string)?;
let (string, members) =
map_res(separated_list1(tag(","), |input| Self::parse_pair(input, depth)), |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, Self::Struct(IndexMap::from_iter(members), Default::default())))
}
fn parse_array(string: &str, depth: usize) -> ParserResult<Self> {
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("[")(string)?;
let (string, members) = separated_list1(tag(","), |input| Self::parse_internal(input, depth + 1))(string)?;
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("]")(string)?;
Ok((string, Self::Array(members, Default::default())))
}
fn parse_internal(string: &str, depth: usize) -> ParserResult<Self> {
if depth > N::MAX_DATA_DEPTH {
return map_res(take(0usize), |_| {
Err(error(format!("Found a plaintext that exceeds maximum data depth ({})", N::MAX_DATA_DEPTH)))
})(string);
}
let (string, _) = Sanitizer::parse(string)?;
alt((
map(Literal::parse, |literal| Self::Literal(literal, Default::default())),
|input| Self::parse_struct(input, depth),
|input| Self::parse_array(input, depth),
))(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(..) | Self::Array(..) => {
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, ","),
}
}
}
})
}
Self::Array(array, ..) => {
write!(f, "[")?;
array.iter().enumerate().try_for_each(|(i, plaintext)| {
match plaintext {
Self::Literal(literal, ..) => match i == array.len() - 1 {
true => {
write!(f, "\n{:indent$}{literal}", "", indent = (depth + 1) * INDENT)?;
write!(f, "\n{:indent$}]", "", indent = depth * INDENT)
}
false => write!(f, "\n{:indent$}{literal},", "", indent = (depth + 1) * INDENT),
},
Self::Struct(..) | Self::Array(..) => {
write!(f, "\n{:indent$}", "", indent = (depth + 1) * INDENT)?;
plaintext.fmt_internal(f, depth + 1)?;
match i == array.len() - 1 {
true => write!(f, "\n{:indent$}]", "", indent = depth * INDENT),
false => write!(f, ","),
}
}
}
})
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::MainnetV0;
type CurrentNetwork = MainnetV0;
#[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);
}
#[test]
fn test_array() {
let expected = r"[
1u8,
2u8,
3u8
]";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
let expected = r"[
{
foo: 1u8,
bar: 2u8
},
{
foo: 3u8,
bar: 4u8
},
{
foo: 5u8,
bar: 6u8
}
]";
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_struct_with_arrays() {
let expected = r"{
foo: [
1u8,
2u8,
3u8
],
bar: [
4u8,
5u8,
6u8
]
}";
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_struct_with_array_of_structs() {
let expected = r"{
foo: [
{
foo: 1u8,
bar: 2u8
},
{
foo: 3u8,
bar: 4u8
},
{
foo: 5u8,
bar: 6u8
}
],
bar: [
{
foo: [
1u8,
2u8,
3u8
],
bar: [
4u8,
5u8,
6u8
]
},
{
foo: [
7u8,
8u8,
9u8
],
bar: [
10u8,
11u8,
12u8
]
},
{
foo: [
13u8,
14u8,
15u8
],
bar: [
16u8,
17u8,
18u8
]
}
]
}";
let (remainder, candidate) = Plaintext::<CurrentNetwork>::parse(expected).unwrap();
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
}
fn get_depth(plaintext: &Plaintext<CurrentNetwork>) -> usize {
match plaintext {
Plaintext::Literal(_, _) => 0,
Plaintext::Struct(members, _) => members.values().map(get_depth).max().unwrap_or(0) + 1,
Plaintext::Array(elements, _) => elements.iter().map(get_depth).max().unwrap_or(0) + 1,
}
}
#[test]
fn test_deeply_nested_plaintext() {
fn create_nested_array(depth: usize, root: impl Display) -> String {
let prefix = if depth == 0 { "".to_string() } else { "[".repeat(depth) };
let suffix = if depth == 0 { "".to_string() } else { "]".repeat(depth) };
format!("{prefix}{root}{suffix}")
}
fn create_nested_struct(depth: usize, root: impl Display) -> String {
let prefix = if depth == 0 { "".to_string() } else { "{inner:".repeat(depth) };
let suffix = if depth == 0 { "".to_string() } else { "}".repeat(depth) };
format!("{prefix}{root}{suffix}")
}
fn create_alternated_nested(depth: usize, root: impl Display) -> String {
let prefix = (0..depth).map(|i| if i % 2 == 0 { "[" } else { "{inner:" }).collect::<String>();
let suffix = (0..depth).map(|i| if i % 2 == 0 { "]" } else { "}" }).rev().collect::<String>();
format!("{prefix}{root}{suffix}")
}
fn run_test(expected_depth: usize, input: String, expected_error: bool) {
let result = Plaintext::<CurrentNetwork>::parse(&input);
match expected_error {
true => {
assert!(result.is_err());
return;
}
false => assert!(result.is_ok()),
};
let (remainder, candidate) = result.unwrap();
assert!(remainder.is_empty());
assert_eq!(input, candidate.to_string().replace("\n", "").replace(" ", ""));
assert_eq!(get_depth(&candidate), expected_depth);
}
let mut depths = (0usize..100).collect_vec();
depths.extend((100..1000).step_by(100));
depths.extend((1000..10000).step_by(1000));
depths.extend((10000..100000).step_by(10000));
for i in depths.iter().copied() {
run_test(i, create_nested_array(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_array(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_array(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_array(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
}
for i in depths.iter().copied() {
run_test(i, create_nested_struct(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_struct(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_struct(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_struct(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
}
for i in depths.iter().copied() {
run_test(i, create_alternated_nested(i, "false"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_alternated_nested(i, "1u8"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_alternated_nested(i, "0u128"), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_alternated_nested(i, "10field"), i > CurrentNetwork::MAX_DATA_DEPTH);
}
}
}