candid_parser/
lib.rs

1//! # Candid Parser
2//!
3//! Everything from `candid` crate is re-exported here. Users don't have to add `candid` as their dependency in `Cargo.toml`.
4//!
5//! Provides parser for Candid type and value.
6//!  * `str.parse::<IDLProg>()` parses the Candid signature file to Candid AST.
7//!  * `parse_idl_args()` parses the Candid value in text format to a struct `IDLArg` that can be used for serialization and deserialization between Candid and an enum type `IDLValue` in Rust.
8
9//! ## Parse [`candid::IDLArgs`]
10//!
11//! We provide a parser to parse Candid values in text format.
12//!
13//! ```
14//! # fn f() -> anyhow::Result<()> {
15//! use candid::{IDLArgs, TypeEnv};
16//! use candid_parser::parse_idl_args;
17//! // Candid values represented in text format
18//! let text_value = r#"
19//!      (42, opt true, vec {1;2;3},
20//!       opt record {label="text"; 42="haha"})
21//! "#;
22//!
23//! // Parse text format into IDLArgs for serialization
24//! let args: IDLArgs = parse_idl_args(text_value)?;
25//! let encoded: Vec<u8> = args.to_bytes()?;
26//!
27//! // Deserialize into IDLArgs
28//! let decoded: IDLArgs = IDLArgs::from_bytes(&encoded)?;
29//! assert_eq!(encoded, decoded.to_bytes()?);
30//!
31//! // Convert IDLArgs to text format
32//! let output: String = decoded.to_string();
33//! let parsed_args: IDLArgs = parse_idl_args(&output)?;
34//! let annotated_args = args.annotate_types(true, &TypeEnv::new(), &parsed_args.get_types())?;
35//! assert_eq!(annotated_args, parsed_args);
36//! # Ok(())
37//! # }
38//! ```
39//! Note that when parsing Candid values, we assume the number literals are always of type `Int`.
40//! This can be changed by providing the type of the method arguments, which can usually be obtained
41//! by parsing a Candid file in the following section.
42//!
43//! ## Operating on Candid AST
44//!
45//! We provide a parser and type checker for Candid files specifying the service interface.
46//!
47//! ```
48//! # fn f() -> anyhow::Result<()> {
49//! use candid::{TypeEnv, types::{Type, TypeInner}};
50//! use candid_parser::{IDLProg, check_prog};
51//! let did_file = r#"
52//!     type List = opt record { head: int; tail: List };
53//!     type byte = nat8;
54//!     service : {
55//!       f : (byte, int, nat, int8) -> (List);
56//!       g : (List) -> (int) query;
57//!     }
58//! "#;
59//!
60//! // Parse did file into an AST
61//! let ast: IDLProg = did_file.parse()?;
62//!
63//! // Type checking a given .did file
64//! // let (env, opt_actor) = check_file("a.did")?;
65//! // Or alternatively, use check_prog to check in-memory did file
66//! // Note that file import is ignored by check_prog.
67//! let mut env = TypeEnv::new();
68//! let actor: Type = check_prog(&mut env, &ast)?.unwrap();
69//!
70//! let method = env.get_method(&actor, "g").unwrap();
71//! assert_eq!(method.is_query(), true);
72//! assert_eq!(method.args, vec![TypeInner::Var("List".to_string()).into()]);
73//! # Ok(())
74//! # }
75//! ```
76//!
77//! ## Serializing untyped Candid values with type annotations.
78//!
79//! With type signatures from the Candid file, [`candid::IDLArgs`](parser/value/struct.IDLArgs.html)
80//! uses `to_bytes_with_types` function to serialize arguments directed by the Candid types.
81//! This is useful when serializing different number types and recursive types.
82//! There is no need to use types for deserialization as the types are available in the Candid message.
83//!
84//! ```
85//! # fn f() -> anyhow::Result<()> {
86//! use candid::{IDLArgs, types::value::IDLValue};
87//! use candid_parser::parse_idl_args;
88//! # use candid::TypeEnv;
89//! # use candid_parser::{IDLProg, check_prog};
90//! # let did_file = r#"
91//! #    type List = opt record { head: int; tail: List };
92//! #    type byte = nat8;
93//! #    service : {
94//! #      f : (byte, int, nat, int8) -> (List);
95//! #      g : (List) -> (int) query;
96//! #    }
97//! # "#;
98//! # let ast = did_file.parse::<IDLProg>()?;
99//! # let mut env = TypeEnv::new();
100//! # let actor = check_prog(&mut env, &ast)?.unwrap();
101//! // Get method type f : (byte, int, nat, int8) -> (List)
102//! let method = env.get_method(&actor, "f").unwrap();
103//! let args = parse_idl_args("(42, 42, 42, 42)")?;
104//! // Serialize arguments with candid types
105//! let encoded = args.to_bytes_with_types(&env, &method.args)?;
106//! let decoded = IDLArgs::from_bytes(&encoded)?;
107//! assert_eq!(decoded.args,
108//!        vec![IDLValue::Nat8(42),
109//!             IDLValue::Int(42.into()),
110//!             IDLValue::Nat(42u8.into()),
111//!             IDLValue::Int8(42)
112//!            ]);
113//! # Ok(())
114//! # }
115//! ```
116
117// only enables the `doc_cfg` feature when
118// the `docsrs` configuration attribute is defined
119#![cfg_attr(docsrs, feature(doc_cfg))]
120
121pub mod error;
122pub use error::{pretty_parse, pretty_wrap, Error, Result};
123
124pub mod bindings;
125pub mod grammar;
126pub mod syntax;
127pub mod token;
128pub mod typing;
129pub mod utils;
130pub use syntax::IDLProg;
131pub use typing::{check_file, check_prog, pretty_check_file};
132
133pub use candid;
134pub use candid::*;
135
136#[cfg_attr(docsrs, doc(cfg(feature = "assist")))]
137#[cfg(feature = "assist")]
138pub mod assist;
139pub mod configs;
140#[cfg_attr(docsrs, doc(cfg(feature = "random")))]
141#[cfg(feature = "random")]
142pub mod random;
143pub mod test;
144
145pub fn parse_idl_args(s: &str) -> crate::Result<candid::IDLArgs> {
146    let lexer = token::Tokenizer::new(s);
147    Ok(grammar::ArgsParser::new().parse(None, lexer)?)
148}
149
150pub fn parse_idl_value(s: &str) -> crate::Result<candid::IDLValue> {
151    let lexer = token::Tokenizer::new(s);
152    Ok(grammar::ArgParser::new().parse(None, lexer)?)
153}