1use std::{collections::BTreeMap, sync::Arc};
10
11use serde::{Deserialize, Serialize};
12use serde_json::{value::RawValue, Value};
13
14use crate::{AnalysisError, AnalysisResult, Analyzer};
15
16mod config;
17#[cfg(feature = "kuromoji")]
18pub(crate) use config::check_config as check_configuration;
19mod hash;
20mod json;
21pub(crate) mod limits;
22mod profiles;
23
24pub use hash::AnalyzerFingerprint;
25pub use limits::AnalyzerLimits;
26use profiles::RuntimeProfiles;
27
28const FORMAT: &str = "uqa-analyzer";
29const FORMAT_VERSION: u32 = 1;
30const ALGORITHM_REVISION: u32 = 1;
31const SOURCE_MAPPING_REVISION: u32 = 1;
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
35#[serde(rename_all = "snake_case")]
36pub enum TokenLengthPolicy {
37 EmittedTokens,
39 DiscountOverlaps,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44#[serde(deny_unknown_fields)]
45struct DescriptorData {
46 format: String,
47 format_version: u32,
48 algorithm_revision: u32,
49 source_mapping_revision: u32,
50 length_policy: TokenLengthPolicy,
51 pipeline: Value,
52 runtime_profiles: RuntimeProfiles,
53}
54
55#[derive(Serialize, Deserialize)]
56#[serde(deny_unknown_fields)]
57struct Wire {
58 descriptor: DescriptorData,
59 fingerprint: AnalyzerFingerprint,
60}
61
62#[derive(Debug)]
64pub struct AnalyzerDescriptor {
65 data: DescriptorData,
66 fingerprint: AnalyzerFingerprint,
67 wire: Box<RawValue>,
68}
69
70pub(crate) struct ResolvedDescriptor {
71 pub descriptor: Arc<AnalyzerDescriptor>,
72 #[cfg(feature = "nori")]
73 pub nori: crate::nori::pipeline::ResolvedNoriPipeline,
74 #[cfg(feature = "kuromoji")]
75 pub kuromoji: crate::kuromoji::pipeline::ResolvedKuromojiPipeline,
76}
77
78impl Serialize for AnalyzerDescriptor {
79 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
80 self.wire.serialize(serializer)
81 }
82}
83
84impl AnalyzerDescriptor {
85 pub fn resolve(
86 config: &Analyzer,
87 length_policy: TokenLengthPolicy,
88 limits: AnalyzerLimits,
89 ) -> AnalysisResult<Arc<Self>> {
90 Ok(Self::resolve_inputs(
91 config,
92 length_policy,
93 limits,
94 #[cfg(feature = "nori")]
95 &crate::nori::NoriResources::default(),
96 #[cfg(feature = "kuromoji")]
97 &crate::kuromoji::KuromojiResources::default(),
98 )?
99 .descriptor)
100 }
101
102 pub(crate) fn resolve_inputs(
103 config: &Analyzer,
104 length_policy: TokenLengthPolicy,
105 limits: AnalyzerLimits,
106 #[cfg(feature = "nori")] resources: &crate::nori::NoriResources,
107 #[cfg(feature = "kuromoji")] kuromoji_resources: &crate::kuromoji::KuromojiResources,
108 ) -> AnalysisResult<ResolvedDescriptor> {
109 config::check_config(config, limits)?;
110 let profiles = RuntimeProfiles::resolve(config)?;
111 #[cfg(any(feature = "nori", feature = "kuromoji"))]
112 let mut resolved_config = config.clone();
113 #[cfg(feature = "nori")]
114 let nori =
115 crate::nori::pipeline::ResolvedNoriPipeline::resolve(&mut resolved_config, resources)?;
116 #[cfg(feature = "kuromoji")]
117 let kuromoji = crate::kuromoji::pipeline::ResolvedKuromojiPipeline::resolve(
118 &mut resolved_config,
119 kuromoji_resources,
120 limits,
121 )?;
122 #[cfg(any(feature = "nori", feature = "kuromoji"))]
123 let config = &resolved_config;
124 let pipeline = config::snapshot(config, limits)?;
125 let descriptor = Self::finish(
126 DescriptorData {
127 format: FORMAT.into(),
128 format_version: FORMAT_VERSION,
129 algorithm_revision: ALGORITHM_REVISION,
130 source_mapping_revision: SOURCE_MAPPING_REVISION,
131 length_policy,
132 pipeline,
133 runtime_profiles: profiles,
134 },
135 limits,
136 )?;
137 Ok(ResolvedDescriptor {
138 descriptor,
139 #[cfg(feature = "nori")]
140 nori,
141 #[cfg(feature = "kuromoji")]
142 kuromoji,
143 })
144 }
145
146 pub fn from_json(json: &str, limits: AnalyzerLimits) -> AnalysisResult<Arc<Self>> {
148 limits::check_limit(
149 "analyzer descriptor bytes",
150 json.len(),
151 limits.max_descriptor_bytes,
152 )?;
153 json::check_unique_keys(json)?;
154 let wire: Wire = serde_json::from_str(json)?;
155 let data_bytes = limits::encode(
156 &canonical(&serde_json::to_value(&wire.descriptor)?),
157 limits.max_descriptor_bytes,
158 true,
159 )?;
160 let fingerprint = AnalyzerFingerprint::digest(&data_bytes);
161 if wire.fingerprint != fingerprint {
162 return Err(AnalysisError::DescriptorFingerprint {
163 expected: wire.fingerprint,
164 actual: fingerprint,
165 });
166 }
167 Self::check_revision(&wire.descriptor)?;
168 let config = config::restore(&wire.descriptor.pipeline, limits)?;
169 if RuntimeProfiles::resolve(&config)? != wire.descriptor.runtime_profiles {
170 return Err(invalid(
171 "runtime Unicode or regular-expression profile differs",
172 ));
173 }
174 Self::finish(wire.descriptor, limits)
175 }
176
177 fn finish(data: DescriptorData, limits: AnalyzerLimits) -> AnalysisResult<Arc<Self>> {
178 let bytes = limits::encode(
179 &canonical(&serde_json::to_value(&data)?),
180 limits.max_descriptor_bytes,
181 true,
182 )?;
183 let fingerprint = AnalyzerFingerprint::digest(&bytes);
184 let wire = canonical(&serde_json::to_value(Wire {
185 descriptor: data.clone(),
186 fingerprint,
187 })?);
188 let bytes = limits::encode(&wire, limits.max_descriptor_bytes, true)?;
189 let json = String::from_utf8(bytes).expect("JSON serializer returns UTF-8");
190 Ok(Arc::new(Self {
191 data,
192 fingerprint,
193 wire: RawValue::from_string(json)?,
194 }))
195 }
196
197 fn check_revision(data: &DescriptorData) -> AnalysisResult<()> {
198 if data.format != FORMAT {
199 return Err(invalid("unknown descriptor format"));
200 }
201 for (component, expected, actual) in [
202 ("format", FORMAT_VERSION, data.format_version),
203 ("algorithm", ALGORITHM_REVISION, data.algorithm_revision),
204 (
205 "source mapping",
206 SOURCE_MAPPING_REVISION,
207 data.source_mapping_revision,
208 ),
209 ] {
210 if expected != actual {
211 return Err(AnalysisError::DescriptorRevision {
212 component,
213 expected,
214 actual,
215 });
216 }
217 }
218 Ok(())
219 }
220
221 pub fn fingerprint(&self) -> AnalyzerFingerprint {
222 self.fingerprint
223 }
224 pub fn length_policy(&self) -> TokenLengthPolicy {
225 self.data.length_policy
226 }
227 pub fn canonical_json(&self) -> &str {
228 self.wire.get()
229 }
230
231 pub fn configuration(&self) -> AnalysisResult<Analyzer> {
233 Ok(serde_json::from_value(self.data.pipeline.clone())?)
234 }
235
236 pub(crate) fn validate_limits(&self, limits: AnalyzerLimits) -> AnalysisResult<()> {
237 limits::check_limit(
238 "analyzer descriptor bytes",
239 self.canonical_json().len(),
240 limits.max_descriptor_bytes,
241 )?;
242 config::check_config(&self.configuration()?, limits)
243 }
244}
245
246fn invalid(reason: &'static str) -> AnalysisError {
247 AnalysisError::Descriptor(reason)
248}
249
250fn canonical(value: &Value) -> Value {
251 match value {
252 Value::Object(object) => Value::Object(
253 object
254 .iter()
255 .map(|(key, value)| (key.clone(), canonical(value)))
256 .collect::<BTreeMap<_, _>>()
257 .into_iter()
258 .collect(),
259 ),
260 Value::Array(array) => Value::Array(array.iter().map(canonical).collect()),
261 _ => value.clone(),
262 }
263}