1use schemars::JsonSchema;
10use serde::Deserializer;
11use serde::ser::SerializeMap;
12use serde::{Deserialize, Serialize};
13
14use crate::format_version::{
15 CanonicalSpelling, NormalizedFormatVersion, ScalarValue, SupportTable,
16};
17
18pub mod access;
20pub mod annotation;
21pub mod attributes;
22pub mod distribution;
23pub mod legacy;
24pub mod linked_metadata;
25mod linked_metadata_project;
26pub(crate) mod linked_metadata_scan;
27pub mod literal;
28pub mod module;
29pub mod package;
30pub mod pattern;
31pub mod serde_document;
32pub mod serde_tagged;
33pub mod serde_v4;
34pub mod tree_files;
35pub mod types;
36pub mod value;
37
38pub use crate::naming::ModuleName;
40pub use crate::naming::Name;
41pub use crate::naming::PackageName;
42pub use crate::naming::Path;
43
44pub use access::{Access, AccessControlled};
46
47pub use annotation::{Annotation, AnnotationArgument, Annotations};
49
50pub use crate::ir::decimal::{DecimalLiteral, InvalidDecimalLexeme};
52pub use attributes::{SourceLocation, TypeAttributes, TypeExpr, ValueAttributes, ValueExpr};
53pub use legacy::{SpellingMode, accept_member, take_warnings, with_spelling_mode};
54pub use linked_metadata::{DocumentMeta, MetadataScope};
55pub use linked_metadata_project::{
56 DocumentGraphError, expand_document_graph, expand_v4_single_file_graph,
57};
58pub use linked_metadata_scan::LinkedMetadataCarrier;
59pub use literal::{FloatLiteral, InvalidFloatLexeme, Literal};
60pub use pattern::Pattern;
61pub use serde_v4::{TypeEncoding, with_type_encoding};
62pub use types::{Field, Type};
63pub use value::{
64 HoleReason as ValueHoleReason, InputType, LetBinding, NativeHint as ValueNativeHint,
65 NativeInfo, PatternCase, RecordFieldEntry, Value, ValueBody as ValueExprBody,
66 ValueDefinition as ValueExprDefinition,
67};
68
69pub use distribution::{
71 ApplicationContent, DefinitionDependencies, Dependencies, Distribution, EntryPoint,
72 EntryPointKind, EntryPoints, LibraryContent, SpecsContent,
73};
74
75pub use module::{Documentation, Documented, ModuleDefinition, ModuleSpecification};
77
78pub use package::{PackageDefinition, PackageSpecification};
80
81pub use tree_files::{
83 DistributionKind, DistributionManifestFile, ExpectedEntries, FILE_STEM_PATTERN,
84 MIN_PATH_BUDGET, ModuleEntries, ModuleManifestFile, NodeFileBody, TypeDefinitionFile,
85 ValueDefinitionFile, is_escaped_stem,
86};
87
88pub use types::{
90 ConstructorArg, ConstructorArgSpec, ConstructorDefinition, ConstructorSpecification,
91 Incompleteness, TypeDefinition, TypeSpecification,
92};
93
94pub use value::{
96 ExternalBinding, HoleReason, NativeHint, ValueBody, ValueDefinition, ValueSpecification,
97};
98
99#[derive(Debug, Clone, PartialEq)]
105pub struct IRFile {
106 pub format_version: FormatVersion,
107 pub distribution: Distribution,
108 pub metadata: Option<Box<DocumentMeta>>,
110}
111
112impl IRFile {
113 pub fn has_linked_metadata(&self) -> bool {
115 self.metadata.is_some() || self.distribution.contains_linked_metadata()
116 }
117}
118
119impl Serialize for IRFile {
120 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
121 let proposed = self.format_version == FormatVersion::String("4.1.0".to_owned());
122 if !proposed && self.has_linked_metadata() {
123 return Err(serde::ser::Error::custom(
124 "linked metadata requires formatVersion 4.1.0",
125 ));
126 }
127 let mut map = serializer.serialize_map(None)?;
128 map.serialize_entry("formatVersion", &self.format_version)?;
129 map.serialize_entry("distribution", &self.distribution)?;
130 if let Some(metadata) = &self.metadata {
131 map.serialize_entry("$meta", metadata)?;
132 }
133 map.end()
134 }
135}
136
137impl<'de> Deserialize<'de> for IRFile {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 serde_document::deserialize_with(deserializer, serde_document::decode_ir_file)
143 }
144}
145
146pub fn decode_ir_file_with_warnings(
153 value: &serde_json::Value,
154) -> Result<(IRFile, Vec<crate::ir::Warning>), crate::ir::DiagnosticError> {
155 let (decoded, warnings) = with_spelling_mode(SpellingMode::Current, || {
156 serde_document::decode_ir_file(value, "")
157 });
158 decoded
159 .map(|file| (file, warnings))
160 .map_err(crate::ir::DiagnosticError)
161}
162
163#[derive(Debug, Clone, PartialEq, JsonSchema)]
165pub enum FormatVersion {
166 String(String),
167 Integer(u32),
168}
169
170impl From<CanonicalSpelling> for FormatVersion {
171 fn from(value: CanonicalSpelling) -> Self {
172 match value {
173 CanonicalSpelling::Integer(version) => Self::Integer(version),
174 CanonicalSpelling::String(version) => Self::String(version),
175 }
176 }
177}
178
179impl FormatVersion {
180 pub fn normalize(
182 &self,
183 ) -> Result<NormalizedFormatVersion, crate::format_version::FormatVersionDiagnostic> {
184 let scalar = match self {
185 Self::Integer(version) => ScalarValue::Integer(*version as u64),
186 Self::String(version) => ScalarValue::String(version.clone()),
187 };
188 NormalizedFormatVersion::from_scalar(&scalar, &SupportTable::reference())
189 }
190}
191
192impl Serialize for FormatVersion {
193 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
194 where
195 S: serde::Serializer,
196 {
197 match self {
198 FormatVersion::String(s) => serializer.serialize_str(s),
199 FormatVersion::Integer(n) => serializer.serialize_u32(*n),
200 }
201 }
202}
203
204impl<'de> Deserialize<'de> for FormatVersion {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: Deserializer<'de>,
208 {
209 serde_document::deserialize_with(deserializer, serde_document::decode_format_version)
210 }
211}
212
213impl Default for FormatVersion {
214 fn default() -> Self {
215 FormatVersion::Integer(4)
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_format_version_string() {
225 let v = FormatVersion::String("4.0.0".to_string());
226 let json = serde_json::to_string(&v).unwrap();
227 assert_eq!(json, "\"4.0.0\"");
228 }
229
230 #[test]
231 fn test_format_version_integer() {
232 let v = FormatVersion::Integer(4);
233 let json = serde_json::to_string(&v).unwrap();
234 assert_eq!(json, "4");
235 }
236}