use crate::cache_limits::{
DEFAULT_PERSISTENT_CACHE_LIMITS, PersistentCacheLimitsV0, ensure_cache_root_attribution,
read_cache_shard_with_limits, write_cache_shard_atomically_with_limits,
};
use crate::cache_root::LspCacheStorageConfigV0;
use crate::protocol::file_uri_to_path;
use omena_query::{
OmenaQuerySourceClassValueUniverseAxisV0, OmenaQuerySourceClassValueUniverseEntryV0,
OmenaQuerySourceDomainClassReferenceFactV0, OmenaQuerySourceElementFactV0,
OmenaQuerySourceElementIdentityFactV0, OmenaQuerySourceElementParentFactV0,
OmenaQuerySourceImportedStyleBindingV0, OmenaQuerySourceInlineStyleDeclarationFactV0,
OmenaQuerySourceSelectorReferenceFactV0, OmenaQuerySourceSelectorReferenceMatchKindV0,
OmenaQuerySourceSelectorReferenceSurfaceV0, OmenaQuerySourceStylePropertyAccessFactV0,
OmenaQuerySourceSyntaxIndexV0, OmenaQuerySourceTypeFactExpressionShapeV0,
OmenaQuerySourceTypeFactLexicalAttemptV0, OmenaQuerySourceTypeFactLexicalDispositionV0,
OmenaQuerySourceTypeFactProviderUnavailableFactV0, OmenaQuerySourceTypeFactTargetSkippedFactV0,
OmenaQuerySourceTypeFactTargetV0, OmenaQueryStyleResolutionInputsV0, ParserByteSpanV0,
};
use omena_sif::{compute_omena_sif_leaf_hash_v1, write_omena_canonical_json_bytes_v1};
use serde::Serialize;
use serde_json::{Value, json};
use std::path::{Path, PathBuf};
const SOURCE_DOCUMENT_INDEX_SCHEMA_VERSION: &str = "0";
const SOURCE_DOCUMENT_INDEX_SIDECAR_PRODUCT: &str =
"omena-lsp-server.source-document-index-sidecar";
const SOURCE_DOCUMENT_INDEX_KEY_PRODUCT: &str = "omena-lsp-server.source-document-index-key";
const SOURCE_DOCUMENT_INDEX_DIR: &str = "source-document-index-v1";
const SOURCE_DOCUMENT_INDEX_LIMITS: PersistentCacheLimitsV0 = DEFAULT_PERSISTENT_CACHE_LIMITS;
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
struct SourceDocumentIndexKeyInputV0<'a> {
schema_version: &'a str,
crate_version: &'a str,
product: &'a str,
document_uri: &'a str,
workspace_folder_uri: Option<&'a str>,
language_id: &'a str,
text_hash: &'a str,
resolution_inputs: &'a OmenaQueryStyleResolutionInputsV0,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct LspSourceDocumentIndexSidecarLoadV0 {
pub(crate) source_syntax_index: OmenaQuerySourceSyntaxIndexV0,
pub(crate) source_type_fact_attempts: Vec<OmenaQuerySourceTypeFactLexicalAttemptV0>,
pub(crate) has_unresolved_style_import: bool,
}
pub(crate) fn load_source_document_index_sidecar(
cache_storage: &LspCacheStorageConfigV0,
workspace_folder_uri: Option<&str>,
document_uri: &str,
language_id: &str,
text_hash: &str,
resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
) -> Option<LspSourceDocumentIndexSidecarLoadV0> {
let key = source_document_index_key(
workspace_folder_uri,
document_uri,
language_id,
text_hash,
resolution_inputs,
)?;
let path = source_document_index_sidecar_path(
cache_storage,
workspace_folder_uri,
document_uri,
language_id,
)?;
let bytes = read_source_document_index_shard(path.as_path(), &SOURCE_DOCUMENT_INDEX_LIMITS)?;
let shard: Value = serde_json::from_slice(bytes.as_slice()).ok()?;
if shard.pointer("/schemaVersion").and_then(Value::as_str)
!= Some(SOURCE_DOCUMENT_INDEX_SCHEMA_VERSION)
|| shard.pointer("/product").and_then(Value::as_str)
!= Some(SOURCE_DOCUMENT_INDEX_SIDECAR_PRODUCT)
|| shard.pointer("/key").and_then(Value::as_str) != Some(key.as_str())
|| shard.pointer("/documentUri").and_then(Value::as_str) != Some(document_uri)
|| shard.pointer("/workspaceFolderUri").and_then(Value::as_str) != workspace_folder_uri
|| shard.pointer("/languageId").and_then(Value::as_str) != Some(language_id)
|| shard.pointer("/textHash").and_then(Value::as_str) != Some(text_hash)
{
return None;
}
let payload = shard.pointer("/payload")?;
let payload_digest = source_document_index_payload_digest(payload)?;
if shard.pointer("/payloadDigest").and_then(Value::as_str) != Some(payload_digest.as_str()) {
return None;
}
Some(LspSourceDocumentIndexSidecarLoadV0 {
source_syntax_index: source_syntax_index_from_value(
payload.pointer("/sourceSyntaxIndex")?,
)?,
source_type_fact_attempts: source_type_fact_attempts_from_value(
payload.pointer("/sourceTypeFactAttempts")?,
)?,
has_unresolved_style_import: payload
.pointer("/hasUnresolvedStyleImport")
.and_then(Value::as_bool)?,
})
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn store_source_document_index_sidecar(
cache_storage: &LspCacheStorageConfigV0,
workspace_folder_uri: Option<&str>,
document_uri: &str,
language_id: &str,
text_hash: &str,
resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
source_syntax_index: &OmenaQuerySourceSyntaxIndexV0,
source_type_fact_attempts: &[OmenaQuerySourceTypeFactLexicalAttemptV0],
has_unresolved_style_import: bool,
) {
let Some(key) = source_document_index_key(
workspace_folder_uri,
document_uri,
language_id,
text_hash,
resolution_inputs,
) else {
return;
};
let Some(path) = source_document_index_sidecar_path(
cache_storage,
workspace_folder_uri,
document_uri,
language_id,
) else {
return;
};
let payload = json!({
"sourceSyntaxIndex": source_syntax_index,
"sourceTypeFactAttempts": source_type_fact_attempts,
"hasUnresolvedStyleImport": has_unresolved_style_import,
});
let Some(payload_digest) = source_document_index_payload_digest(&payload) else {
return;
};
let shard = json!({
"schemaVersion": SOURCE_DOCUMENT_INDEX_SCHEMA_VERSION,
"product": SOURCE_DOCUMENT_INDEX_SIDECAR_PRODUCT,
"key": key,
"documentUri": document_uri,
"workspaceFolderUri": workspace_folder_uri,
"languageId": language_id,
"textHash": text_hash,
"payloadDigest": payload_digest,
"payload": payload,
});
let Ok(bytes) = serde_json::to_vec(&shard) else {
return;
};
if write_source_document_index_shard(
path.as_path(),
bytes.as_slice(),
&SOURCE_DOCUMENT_INDEX_LIMITS,
) && let Some(dir) = path.parent()
{
crate::disk_cache::ensure_omena_cache_root_markers(dir);
ensure_cache_root_attribution(dir, workspace_folder_uri.unwrap_or(document_uri));
}
}
fn read_source_document_index_shard(
path: &Path,
limits: &PersistentCacheLimitsV0,
) -> Option<Vec<u8>> {
read_cache_shard_with_limits(path, limits)
}
fn write_source_document_index_shard(
path: &Path,
bytes: &[u8],
limits: &PersistentCacheLimitsV0,
) -> bool {
write_cache_shard_atomically_with_limits(path, bytes, limits)
}
pub(crate) fn source_document_text_hash(text: &str) -> String {
compute_omena_sif_leaf_hash_v1(text.as_bytes())
.as_str()
.to_string()
}
#[cfg(test)]
pub(crate) fn source_document_index_sidecar_file_path_for_test(
cache_storage: &LspCacheStorageConfigV0,
workspace_folder_uri: Option<&str>,
document_uri: &str,
language_id: &str,
) -> Option<PathBuf> {
source_document_index_sidecar_path(
cache_storage,
workspace_folder_uri,
document_uri,
language_id,
)
}
fn source_document_index_key(
workspace_folder_uri: Option<&str>,
document_uri: &str,
language_id: &str,
text_hash: &str,
resolution_inputs: &OmenaQueryStyleResolutionInputsV0,
) -> Option<String> {
let input = SourceDocumentIndexKeyInputV0 {
schema_version: SOURCE_DOCUMENT_INDEX_SCHEMA_VERSION,
crate_version: env!("CARGO_PKG_VERSION"),
product: SOURCE_DOCUMENT_INDEX_KEY_PRODUCT,
document_uri,
workspace_folder_uri,
language_id,
text_hash,
resolution_inputs,
};
let bytes = write_omena_canonical_json_bytes_v1(&input).ok()?;
Some(
compute_omena_sif_leaf_hash_v1(bytes.as_slice())
.as_str()
.to_string(),
)
}
fn source_document_index_sidecar_path(
cache_storage: &LspCacheStorageConfigV0,
workspace_folder_uri: Option<&str>,
document_uri: &str,
language_id: &str,
) -> Option<PathBuf> {
let workspace_folder_uri = workspace_folder_uri?;
let root = file_uri_to_path(workspace_folder_uri)?;
let address = crate::disk_cache::stable_cache_shard_address(
SOURCE_DOCUMENT_INDEX_SIDECAR_PRODUCT,
&[workspace_folder_uri, document_uri, language_id],
)?;
let hex = address.strip_prefix("blake3:")?.to_string();
if hex.is_empty() || !hex.chars().all(|character| character.is_ascii_hexdigit()) {
return None;
}
crate::cache_root::resolved_workspace_cache_dir(
cache_storage,
workspace_folder_uri,
root.as_path(),
SOURCE_DOCUMENT_INDEX_DIR,
)
.map(|dir| dir.join(format!("{hex}.json")))
}
fn source_document_index_payload_digest(value: &Value) -> Option<String> {
let bytes = write_omena_canonical_json_bytes_v1(value).ok()?;
Some(
compute_omena_sif_leaf_hash_v1(bytes.as_slice())
.as_str()
.to_string(),
)
}
fn source_syntax_index_from_value(value: &Value) -> Option<OmenaQuerySourceSyntaxIndexV0> {
if value.pointer("/schemaVersion").and_then(Value::as_str) != Some("0")
|| value.pointer("/product").and_then(Value::as_str)
!= Some("omena-bridge.source-syntax-index")
{
return None;
}
let type_fact_target_skipped = match value.get("typeFactTargetSkipped") {
Some(facts) => type_fact_target_skipped_from_value(facts)?,
None => Vec::new(),
};
let type_fact_target_skipped_count = value
.get("typeFactTargetSkippedCount")
.and_then(Value::as_u64)
.and_then(|count| usize::try_from(count).ok())
.unwrap_or(type_fact_target_skipped.len());
if type_fact_target_skipped_count != type_fact_target_skipped.len() {
return None;
}
Some(OmenaQuerySourceSyntaxIndexV0 {
schema_version: "0",
product: "omena-bridge.source-syntax-index",
imported_style_bindings: imported_style_bindings_from_value(
value.get("importedStyleBindings")?,
)?,
class_string_literals: byte_spans_from_value(value.get("classStringLiterals")?)?,
style_property_accesses: style_property_accesses_from_value(
value.get("stylePropertyAccesses")?,
)?,
inline_style_declarations: inline_style_declarations_from_value(
value.get("inlineStyleDeclarations")?,
)?,
selector_references: selector_references_from_value(value.get("selectorReferences")?)?,
type_fact_targets: type_fact_targets_from_value(value.get("typeFactTargets")?)?,
type_fact_target_skipped,
type_fact_target_skipped_count,
type_fact_provider_unavailable: match value.get("typeFactProviderUnavailable") {
Some(facts) => type_fact_provider_unavailable_from_value(facts)?,
None => Vec::new(),
},
class_value_universes: class_value_universes_from_value(value.get("classValueUniverses")?)?,
domain_class_references: domain_class_references_from_value(
value.get("domainClassReferences")?,
)?,
source_elements: match value.get("sourceElements") {
Some(facts) => source_elements_from_value(facts)?,
None => Vec::new(),
},
element_parent_edges: match value.get("elementParentEdges") {
Some(facts) => element_parent_edges_from_value(facts)?,
None => Vec::new(),
},
})
}
fn imported_style_bindings_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceImportedStyleBindingV0>> {
value
.as_array()?
.iter()
.map(|binding| {
Some(OmenaQuerySourceImportedStyleBindingV0 {
binding: binding.get("binding")?.as_str()?.to_string(),
style_uri: binding.get("styleUri")?.as_str()?.to_string(),
})
})
.collect()
}
fn byte_spans_from_value(value: &Value) -> Option<Vec<ParserByteSpanV0>> {
value.as_array()?.iter().map(byte_span_from_value).collect()
}
fn style_property_accesses_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceStylePropertyAccessFactV0>> {
value
.as_array()?
.iter()
.map(|access| {
Some(OmenaQuerySourceStylePropertyAccessFactV0 {
byte_span: byte_span_from_value(access.get("byteSpan")?)?,
target_style_uri: access
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
})
})
.collect()
}
fn inline_style_declarations_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceInlineStyleDeclarationFactV0>> {
value
.as_array()?
.iter()
.map(|declaration| {
Some(OmenaQuerySourceInlineStyleDeclarationFactV0 {
byte_span: byte_span_from_value(declaration.get("byteSpan")?)?,
value_byte_span: optional_byte_span_from_value(declaration.get("valueByteSpan"))?,
property_name: omena_syntax::ident::AuthoredPropertyTextV0::new(
declaration.get("propertyName")?.as_str()?,
),
value: declaration
.get("value")
.and_then(Value::as_str)
.map(str::to_string),
target_style_uri: declaration
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
cascade_tier: cascade_tier_from_value(declaration.get("cascadeTier")?)?,
important: declaration.get("important")?.as_bool()?,
static_value: declaration.get("staticValue")?.as_bool()?,
})
})
.collect()
}
fn selector_references_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceSelectorReferenceFactV0>> {
value
.as_array()?
.iter()
.map(|reference| {
Some(OmenaQuerySourceSelectorReferenceFactV0 {
byte_span: byte_span_from_value(reference.get("byteSpan")?)?,
selector_name: reference
.get("selectorName")
.and_then(Value::as_str)
.map(str::to_string),
match_kind: selector_match_kind_from_value(reference.get("matchKind")?)?,
target_style_uri: reference
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
surface: selector_reference_surface_from_value(reference.get("surface"))?,
})
})
.collect()
}
fn selector_reference_surface_from_value(
value: Option<&Value>,
) -> Option<OmenaQuerySourceSelectorReferenceSurfaceV0> {
match value.and_then(Value::as_str) {
None | Some("omenaQuerySourceSyntaxIndex") => {
Some(OmenaQuerySourceSelectorReferenceSurfaceV0::OmenaQuerySourceSyntaxIndex)
}
Some("omenaTsgoTypeFactProjection") => {
Some(OmenaQuerySourceSelectorReferenceSurfaceV0::OmenaTsgoTypeFactProjection)
}
Some(_) => None,
}
}
fn type_fact_targets_from_value(value: &Value) -> Option<Vec<OmenaQuerySourceTypeFactTargetV0>> {
value
.as_array()?
.iter()
.map(|target| {
Some(OmenaQuerySourceTypeFactTargetV0 {
byte_span: byte_span_from_value(target.get("byteSpan")?)?,
expression_id: target.get("expressionId")?.as_str()?.to_string(),
target_style_uri: target
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
prefix: target.get("prefix")?.as_str()?.to_string(),
suffix: target.get("suffix")?.as_str()?.to_string(),
})
})
.collect()
}
fn type_fact_provider_unavailable_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceTypeFactProviderUnavailableFactV0>> {
value
.as_array()?
.iter()
.map(|fact| {
Some(OmenaQuerySourceTypeFactProviderUnavailableFactV0 {
byte_span: byte_span_from_value(fact.get("byteSpan")?)?,
expression_id: fact.get("expressionId")?.as_str()?.to_string(),
target_style_uri: fact
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
provider_id: provider_id_from_value(fact.get("providerId")?)?,
reason: type_fact_provider_unavailable_reason_from_value(fact.get("reason")?)?,
})
})
.collect()
}
fn type_fact_target_skipped_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceTypeFactTargetSkippedFactV0>> {
value
.as_array()?
.iter()
.map(|fact| {
Some(OmenaQuerySourceTypeFactTargetSkippedFactV0 {
byte_span: byte_span_from_value(fact.get("byteSpan")?)?,
expression_id: fact.get("expressionId")?.as_str()?.to_string(),
target_style_uri: fact
.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
reason: type_fact_target_skipped_reason_from_value(fact.get("reason")?)?,
})
})
.collect()
}
fn source_type_fact_attempts_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceTypeFactLexicalAttemptV0>> {
value
.as_array()?
.iter()
.map(|fact| {
Some(OmenaQuerySourceTypeFactLexicalAttemptV0::new(
byte_span_from_value(fact.get("byteSpan")?)?,
fact.get("expressionId")?.as_str()?.to_string(),
fact.get("targetStyleUri")
.and_then(Value::as_str)
.map(str::to_string),
source_type_fact_shape_from_value(fact.get("shapeClass")?)?,
source_type_fact_lexical_disposition_from_value(fact.get("lexicalDisposition")?)?,
))
})
.collect()
}
fn source_type_fact_shape_from_value(
value: &Value,
) -> Option<OmenaQuerySourceTypeFactExpressionShapeV0> {
match value.as_str()? {
"identifierPath" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::IdentifierPath),
"lexicallyEnumerable" => {
Some(OmenaQuerySourceTypeFactExpressionShapeV0::LexicallyEnumerable)
}
"call" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::Call),
"arithmetic" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::Arithmetic),
"logicalOperator" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::LogicalOperator),
"computedNonLiteral" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::ComputedNonLiteral),
"nestedTemplate" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::NestedTemplate),
"multiInterpolation" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::MultiInterpolation),
"other" => Some(OmenaQuerySourceTypeFactExpressionShapeV0::Other),
_ => None,
}
}
fn source_type_fact_lexical_disposition_from_value(
value: &Value,
) -> Option<OmenaQuerySourceTypeFactLexicalDispositionV0> {
match value.as_str()? {
"resolved" => Some(OmenaQuerySourceTypeFactLexicalDispositionV0::Resolved),
"typeProviderCandidate" => {
Some(OmenaQuerySourceTypeFactLexicalDispositionV0::TypeProviderCandidate)
}
"unresolved" => Some(OmenaQuerySourceTypeFactLexicalDispositionV0::Unresolved),
_ => None,
}
}
fn provider_id_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"tsgo" => Some("tsgo"),
_ => None,
}
}
fn type_fact_provider_unavailable_reason_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"projectMiss" => Some("projectMiss"),
"noTransport" => Some("noTransport"),
"processUnavailable" => Some("processUnavailable"),
"requestFailed" => Some("requestFailed"),
"missingResult" => Some("missingResult"),
"unresolvable" => Some("unresolvable"),
_ => None,
}
}
fn type_fact_target_skipped_reason_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"identifierPathAwaitingTypeProvider" => Some("identifierPathAwaitingTypeProvider"),
"lexicallyResolvedExpression" => Some("lexicallyResolvedExpression"),
"unsupportedCallExpression" => Some("unsupportedCallExpression"),
"unsupportedArithmeticExpression" => Some("unsupportedArithmeticExpression"),
"unsupportedLogicalExpression" => Some("unsupportedLogicalExpression"),
"unsupportedComputedMemberExpression" => Some("unsupportedComputedMemberExpression"),
"unsupportedNestedTemplateExpression" => Some("unsupportedNestedTemplateExpression"),
"unsupportedMultipleTemplateInterpolations" => {
Some("unsupportedMultipleTemplateInterpolations")
}
"unsupportedExpressionShape" => Some("unsupportedExpressionShape"),
_ => None,
}
}
fn class_value_universes_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceClassValueUniverseEntryV0>> {
value
.as_array()?
.iter()
.map(|universe| {
Some(OmenaQuerySourceClassValueUniverseEntryV0 {
plugin_id: recipe_plugin_id_from_value(universe.get("pluginId")?)?,
domain: recipe_domain_from_value(universe.get("domain")?)?,
owner_name: universe.get("ownerName")?.as_str()?.to_string(),
class_names: strings_from_value(universe.get("classNames")?)?,
axes: class_value_universe_axes_from_value(universe.get("axes")?)?,
patterns: class_value_patterns_from_value(universe.get("patterns")),
unresolved: class_value_unresolved_from_value(universe.get("unresolved")),
byte_span: byte_span_from_value(universe.get("byteSpan")?)?,
})
})
.collect()
}
fn class_value_patterns_from_value(
value: Option<&Value>,
) -> Vec<omena_query::OmenaQuerySourceClassValuePatternV0> {
value
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(|pattern| {
let matcher = match pattern.get("matcher")?.as_str()? {
"prefixSuffix" => {
omena_query::OmenaQuerySourceClassValuePatternMatcherV0::PrefixSuffix
}
"regexSource" => {
omena_query::OmenaQuerySourceClassValuePatternMatcherV0::RegexSource
}
_ => return None,
};
Some(omena_query::OmenaQuerySourceClassValuePatternV0 {
matcher,
source: pattern.get("source")?.as_str()?.to_string(),
completion_hint: pattern.get("completionHint")?.as_str()?.to_string(),
prefix: pattern
.get("prefix")
.and_then(Value::as_str)
.map(str::to_string),
suffix: pattern
.get("suffix")
.and_then(Value::as_str)
.map(str::to_string),
})
})
.collect()
}
fn class_value_unresolved_from_value(
value: Option<&Value>,
) -> Vec<omena_query::OmenaQuerySourceClassValueUnresolvedV0> {
value
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(|item| {
Some(omena_query::OmenaQuerySourceClassValueUnresolvedV0 {
path: item.get("path")?.as_str()?.to_string(),
reason: item.get("reason")?.as_str()?.to_string(),
detail: item.get("detail")?.as_str()?.to_string(),
})
})
.collect()
}
fn class_value_universe_axes_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceClassValueUniverseAxisV0>> {
value
.as_array()?
.iter()
.map(|axis| {
Some(OmenaQuerySourceClassValueUniverseAxisV0 {
axis_name: axis.get("axisName")?.as_str()?.to_string(),
values: strings_from_value(axis.get("values")?)?,
})
})
.collect()
}
fn domain_class_references_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceDomainClassReferenceFactV0>> {
value
.as_array()?
.iter()
.map(|reference| {
Some(OmenaQuerySourceDomainClassReferenceFactV0 {
byte_span: byte_span_from_value(reference.get("byteSpan")?)?,
plugin_id: recipe_plugin_id_from_value(reference.get("pluginId")?)?,
domain: recipe_domain_from_value(reference.get("domain")?)?,
owner_name: reference.get("ownerName")?.as_str()?.to_string(),
axis_name: reference.get("axisName")?.as_str()?.to_string(),
option_name: reference
.get("optionName")
.and_then(Value::as_str)
.map(str::to_string),
prefix: reference
.get("prefix")
.and_then(Value::as_str)
.map(str::to_string),
})
})
.collect()
}
fn source_elements_from_value(value: &Value) -> Option<Vec<OmenaQuerySourceElementFactV0>> {
value
.as_array()?
.iter()
.map(|fact| {
Some(OmenaQuerySourceElementFactV0 {
identity: source_element_identity_from_value(fact.get("identity")?)?,
intrinsic_tag_name: match fact.get("intrinsicTagName") {
Some(name) => Some(name.as_str()?.to_string()),
None => None,
},
static_class_names: match fact.get("staticClassNames") {
Some(names) => names
.as_array()?
.iter()
.map(|name| name.as_str().map(str::to_string))
.collect::<Option<Vec<_>>>()?,
None => Vec::new(),
},
classes_are_exact: fact
.get("classesAreExact")
.and_then(Value::as_bool)
.unwrap_or(false),
})
})
.collect()
}
fn element_parent_edges_from_value(
value: &Value,
) -> Option<Vec<OmenaQuerySourceElementParentFactV0>> {
value
.as_array()?
.iter()
.map(|fact| {
Some(OmenaQuerySourceElementParentFactV0 {
child: source_element_identity_from_value(fact.get("child")?)?,
parent: source_element_identity_from_value(fact.get("parent")?)?,
})
})
.collect()
}
fn source_element_identity_from_value(
value: &Value,
) -> Option<OmenaQuerySourceElementIdentityFactV0> {
Some(OmenaQuerySourceElementIdentityFactV0 {
source_path: value.get("sourcePath")?.as_str()?.to_string(),
byte_span: byte_span_from_value(value.get("byteSpan")?)?,
})
}
fn byte_span_from_value(value: &Value) -> Option<ParserByteSpanV0> {
Some(ParserByteSpanV0 {
start: value.get("start")?.as_u64()? as usize,
end: value.get("end")?.as_u64()? as usize,
})
}
fn optional_byte_span_from_value(value: Option<&Value>) -> Option<Option<ParserByteSpanV0>> {
match value {
Some(Value::Null) | None => Some(None),
Some(value) => byte_span_from_value(value).map(Some),
}
}
fn selector_match_kind_from_value(
value: &Value,
) -> Option<OmenaQuerySourceSelectorReferenceMatchKindV0> {
match value.as_str()? {
"exact" | "Exact" => Some(OmenaQuerySourceSelectorReferenceMatchKindV0::Exact),
"prefix" | "Prefix" => Some(OmenaQuerySourceSelectorReferenceMatchKindV0::Prefix),
_ => None,
}
}
fn strings_from_value(value: &Value) -> Option<Vec<String>> {
value
.as_array()?
.iter()
.map(|item| item.as_str().map(str::to_string))
.collect()
}
fn cascade_tier_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"authorInlineStyle" => Some("authorInlineStyle"),
_ => None,
}
}
fn recipe_plugin_id_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"cva-recipe-domain" => Some("cva-recipe-domain"),
"vanilla-extract-recipe-domain" => Some("vanilla-extract-recipe-domain"),
_ => None,
}
}
fn recipe_domain_from_value(value: &Value) -> Option<&'static str> {
match value.as_str()? {
"cva-recipe" => Some("cva-recipe"),
"vanilla-extract-recipe" => Some("vanilla-extract-recipe"),
_ => None,
}
}
#[cfg(test)]
mod cache_limit_tests {
use super::*;
use crate::protocol::path_to_file_uri;
#[test]
fn source_document_store_enforces_reachable_count_byte_and_shard_limits() {
let root = std::env::temp_dir().join(format!(
"omena-source-document-store-limits-{}",
std::process::id()
));
let _ = std::fs::remove_dir_all(root.as_path());
assert!(std::fs::create_dir_all(root.join("src")).is_ok());
let workspace_uri = path_to_file_uri(root.as_path());
let editor_workspace_storage = root.join("editor-storage").join("workspace");
let cache_storage = LspCacheStorageConfigV0 {
initialization_global_storage: Some(root.join("editor-storage").join("global")),
initialization_workspace_storage: Some(editor_workspace_storage.clone()),
location: crate::cache_root::CacheLocationV0::Editor,
..LspCacheStorageConfigV0::default()
};
let resolution_inputs = OmenaQueryStyleResolutionInputsV0::default();
let syntax_index = OmenaQuerySourceSyntaxIndexV0::default();
let default_document_uri = path_to_file_uri(root.join("src/default-limit.tsx").as_path());
let default_path = source_document_index_sidecar_path(
&cache_storage,
Some(workspace_uri.as_str()),
default_document_uri.as_str(),
"typescriptreact",
);
assert!(default_path.is_some(), "source-document default path");
let Some(default_path) = default_path else {
return;
};
assert!(
default_path.starts_with(editor_workspace_storage.as_path()),
"source-document production cap exercise must use the resolved editor root"
);
if let Some(parent) = default_path.parent() {
assert!(std::fs::create_dir_all(parent).is_ok());
}
let oversized_text_hash = "x".repeat(
usize::try_from(SOURCE_DOCUMENT_INDEX_LIMITS.max_shard_bytes)
.unwrap_or(8 * 1024 * 1024)
+ 1,
);
store_source_document_index_sidecar(
&cache_storage,
Some(workspace_uri.as_str()),
default_document_uri.as_str(),
"typescriptreact",
oversized_text_hash.as_str(),
&resolution_inputs,
&syntax_index,
&[],
false,
);
assert!(
!default_path.exists(),
"source-document default max-shard constant must be reachable through the real store"
);
let cache_dir = default_path.parent();
assert!(cache_dir.is_some(), "source-document default cache dir");
if let Some(cache_dir) = cache_dir {
crate::cache_limits::assert_production_store_enforces_default_count_and_total(
"source-document-index",
cache_dir,
default_path.as_path(),
|| {
store_source_document_index_sidecar(
&cache_storage,
Some(workspace_uri.as_str()),
default_document_uri.as_str(),
"typescriptreact",
"blake3:default-cap-fixture",
&resolution_inputs,
&syntax_index,
&[],
false,
);
},
);
}
crate::cache_limits::assert_real_cache_store_enforces_reachable_limits(
"source-document-index",
write_source_document_index_shard,
read_source_document_index_shard,
);
eprintln!(
"storeEntryCaps cache=source-document-index resolvedEditorRoot=true defaultCount=true defaultTotalBytes=true defaultMaxShardRefused=true lowLevelCount=true lowLevelTotalBytes=true lowLevelShardBytes=true"
);
let _ = std::fs::remove_dir_all(root);
}
}