use super::*;
impl<N: Network> Parser for Entry<N, Plaintext<N>> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
enum Mode {
Constant,
Public,
Private,
}
fn parse_literal<N: Network>(string: &str) -> ParserResult<(Plaintext<N>, Mode)> {
alt((
map(pair(Literal::parse, tag(".constant")), |(literal, _)| (Plaintext::from(literal), Mode::Constant)),
map(pair(Literal::parse, tag(".public")), |(literal, _)| (Plaintext::from(literal), Mode::Public)),
map(pair(Literal::parse, tag(".private")), |(literal, _)| (Plaintext::from(literal), Mode::Private)),
))(string)
}
fn parse_pair<N: Network>(string: &str) -> ParserResult<(Identifier<N>, Plaintext<N>, Mode)> {
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_whitespaces(string)?;
let (string, (plaintext, mode)) = alt((
parse_literal,
parse_struct,
))(string)?;
Ok((string, (identifier, plaintext, mode)))
}
fn parse_struct<N: Network>(string: &str) -> ParserResult<(Plaintext<N>, Mode)> {
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag("{")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, (members, mode)) = map_res(separated_list1(tag(","), parse_pair), |members: Vec<_>| {
if has_duplicates(members.iter().map(|(name, ..)| name)) {
return Err(error("Duplicate member in struct"));
}
let mode = members.iter().map(|(_, _, mode)| mode).dedup().collect::<Vec<_>>();
let mode = match mode.len() == 1 {
true => *mode[0],
false => return Err(error("Members of struct in entry have different visibilities")),
};
match members.len() <= N::MAX_DATA_ENTRIES {
true => Ok((members.into_iter().map(|(i, p, _)| (i, p)).collect::<Vec<_>>(), mode)),
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()), mode)))
}
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, (plaintext, mode)) = alt((
parse_literal,
parse_struct,
))(string)?;
match mode {
Mode::Constant => Ok((string, Entry::Constant(plaintext))),
Mode::Public => Ok((string, Entry::Public(plaintext))),
Mode::Private => Ok((string, Entry::Private(plaintext))),
}
}
}
impl<N: Network> FromStr for Entry<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 Entry<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network> Display for Entry<N, Plaintext<N>> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
self.fmt_internal(f, 0)
}
}
impl<N: Network> Entry<N, Plaintext<N>> {
pub(in crate::data::record) fn fmt_internal(&self, f: &mut Formatter, depth: usize) -> fmt::Result {
const INDENT: usize = 2;
let (plaintext, visibility) = match self {
Self::Constant(constant) => (constant, "constant"),
Self::Public(public) => (public, "public"),
Self::Private(private) => (private, "private"),
};
match plaintext {
Plaintext::Literal(literal, ..) => {
write!(f, "{:indent$}{literal}.{visibility}", "", indent = depth * INDENT)
}
Plaintext::Struct(struct_, ..) => {
write!(f, "{{")?;
struct_.iter().enumerate().try_for_each(|(i, (name, plaintext))| {
match plaintext {
#[rustfmt::skip]
Plaintext::Literal(literal, ..) => match i == struct_.len() - 1 {
true => {
write!(f, "\n{:indent$}{name}: {literal}.{visibility}", "", indent = (depth + 1) * INDENT)?;
write!(f, "\n{:indent$}}}", "", indent = depth * INDENT)
}
false => write!(f, "\n{:indent$}{name}: {literal}.{visibility},", "", indent = (depth + 1) * INDENT),
},
Plaintext::Struct(..) => {
write!(f, "\n{:indent$}{name}: ", "", indent = (depth + 1) * INDENT)?;
match self {
Self::Constant(..) => Self::Constant(plaintext.clone()).fmt_internal(f, depth + 1)?,
Self::Public(..) => Self::Public(plaintext.clone()).fmt_internal(f, depth + 1)?,
Self::Private(..) => Self::Private(plaintext.clone()).fmt_internal(f, depth + 1)?,
}
match i == struct_.len() - 1 {
true => write!(f, "\n{:indent$}}}", "", indent = depth * INDENT),
false => 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() -> Result<()> {
let expected = r"{
foo: 5u8.private
}";
let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse("{ foo: 5u8.private }")?;
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
let expected = r"{
foo: 5u8.public,
bar: {
baz: 10field.public,
qux: {
quux: {
corge: {
grault: {
garply: {
waldo: {
fred: {
plugh: {
xyzzy: {
thud: true.public
}
}
}
}
}
}
}
}
}
}
}";
let (remainder, candidate) = Entry::<CurrentNetwork, Plaintext<CurrentNetwork>>::parse(
"{ foo: 5u8.public, bar: { baz: 10field.public, qux: {quux:{corge :{grault: {garply:{waldo:{fred:{plugh:{xyzzy: { thud: true.public}} }}} }}}}}}",
)?;
println!("\nExpected: {expected}\n\nFound: {candidate}\n");
assert_eq!(expected, candidate.to_string());
assert_eq!("", remainder);
Ok(())
}
}