1pub mod dump;
13pub mod error;
14pub mod types;
15mod validate;
16
17pub use error::HirError;
18pub use types::{
19 Annotation, AnnotationArg, Declaration, DictEntry, DirectiveRecord, DirectiveValue, Event,
20 Expr, Generator, OptimizationState, Position, PreprocessingSnapshot, PreprocessingState,
21 Program, Protocol, Rule, RuleEntry, Settings, SettingsListElement, SettingsNode, SourceFile,
22 Span, Stmt, SwitchArm, TranslationState, default_var_index,
23};
24
25use serde_json::Value;
26
27pub fn parse_str(input: &str) -> Result<Program, HirError> {
33 let value: Value = serde_json::from_str(input)?;
34 parse_value(value)
35}
36
37pub fn parse_value(value: Value) -> Result<Program, HirError> {
39 validate::check_envelope(&value)?;
40 validate::check_unknown_kinds(&value)?;
41 let program: Program = serde_json::from_value(value)?;
42 program.validate()?;
43 Ok(program)
44}
45
46impl Program {
47 pub fn validate(&self) -> Result<(), HirError> {
51 validate::validate_program(self)
52 }
53
54 pub fn dump(&self) -> String {
57 dump::dump(self)
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use serde_json::json;
64
65 use super::validate::check_envelope;
66
67 #[test]
68 fn v2_envelope_is_accepted_and_v1_is_rejected_before_body_inspection() {
69 let v2 = json!({
70 "protocol": { "name": "wright/opy-hir", "version": "2.0.0" }
71 });
72 assert!(check_envelope(&v2).is_ok());
73
74 let v1 = json!({
75 "protocol": { "name": "wright/opy-hir", "version": "1.1.0" },
76 "rules": [{ "kind": "malformed-v1-body" }]
77 });
78 let error = check_envelope(&v1).expect_err("v1 payload must not enter the v2 parser");
79 assert_eq!(error.code(), "incompatible-protocol");
80 }
81}