return new suggestion values serialization w/ serde

This commit is contained in:
HWienhold 2025-08-03 10:25:40 +02:00
parent 0189c4479a
commit b1bc9b9837
14 changed files with 321 additions and 69 deletions

View File

@ -14,6 +14,8 @@ log = "0.4.25"
env_logger = "0.11.6"
once_cell = "1.10.0"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
surf = "2.3.2"
async-std = "1.13.0"
@ -22,9 +24,12 @@ async-std = "1.13.0"
wasm-logger = "0.2.0"
wasm-bindgen = "0.2.76"
wasm-bindgen-futures = "0.4.50"
serde-wasm-bindgen = "0.6.5"
js-sys = "0.3.53"
futures = "0.3"
serde = { version = "1.0.218", features = ["derive"] }
serde_yaml = "0.9.34+deprecated"
[lib]
crate-type = ["cdylib", "rlib"]
crate-type = ["cdylib", "rlib"]

View File

@ -1,3 +1,4 @@
pub mod generation;
pub mod node;
pub mod validate;
pub mod types;

View File

@ -10,10 +10,10 @@ use yaml_rust2::Yaml;
#[derive(PartialEq, Clone, Default)]
pub struct BaseInfo {
title: Option<String>,
description: Option<String>,
pub description: Option<String>,
markdown_description: Option<String>,
default: Option<String>,
deprecated: Option<String>,
pub deprecated: Option<String>,
used: Option<bool>,
disallowed: Option<bool>,
required: Option<bool>, // parrtialValidmarker?
@ -190,7 +190,7 @@ impl<'a> From<&SchemaMetadata<'a>> for BaseInfo {
fn from(meta: &SchemaMetadata<'a>) -> Self {
Self {
title: meta.title().map(|x| x.to_string()),
description: meta.description().map(|x| x.to_string()),
description: meta.description().map(|x| x.to_string()),
markdown_description: meta.markdown_description().map(|x| x.to_string()),
default: meta.default().map(|x| format!("{x:?}")),
deprecated: meta.deprecated().map(|x| x.to_string()),
@ -360,6 +360,38 @@ pub struct SingleNodeInfo {
meta: BaseInfo,
}
impl SingleNodeInfo {
pub fn new() -> Self {
Self {
keys: HashMap::new(),
values: HashMap::new(),
meta: BaseInfo::empty(),
}
}
pub fn meta(&self) -> &BaseInfo {
&self.meta
}
pub fn keys(&self) -> &HashMap<String, PropertyInfo> {
&self.keys
}
pub fn values(&self) -> &HashMap<String, ValueInfo> {
&self.values
}
pub fn with_suggested_values( keys: HashMap<String, PropertyInfo>,
values: HashMap<String, ValueInfo>,
) -> Self {
Self {
keys,
values,
meta: BaseInfo::empty(),
}
}
}
pub struct FullInfoNode {
node: SingleNodeInfo,
array_child: Vec<FullInfoNode>,

View File

@ -5,7 +5,7 @@ use yaml_rust2::{Event, Yaml};
use super::skips::RemovedHandler;
use crate::error::add_err::AnyErr;
use crate::info::types::{PropertyInfo, ValueInfo};
use crate::info::types::{PropertyInfo, SingleNodeInfo, ValueInfo};
use crate::{
error::{validate::ValidationError, with_context::WError},
info::node::YamlNode,
@ -235,8 +235,8 @@ impl<'a> YamlWrapper<'a> {
path: YamlPath,
) -> Option<(HashMap<String, PropertyInfo>, HashMap<String, ValueInfo>)> {
log::info!("Generating ext info for path: {:?}", path);
let a = context.gen_ex(&self.yaml, Some(schema), path.clone());
let b = context.gen_ex2(&self.yaml, Some(schema), path.clone());
let a = context.generate_property_info(&self.yaml, Some(schema), path.clone());
let b = context.genereate_value_info(&self.yaml, Some(schema), path.clone());
Some((a, b))
/* let b = context.with_ref_node(Some(schema), path, |node| {
@ -248,6 +248,28 @@ impl<'a> YamlWrapper<'a> {
*/
}
pub fn generate_extended_suggestions(
&'a self,
schema: &'a str,
context: &'a Context<'a>,
path: YamlPath,
) -> Option<SingleNodeInfo> {
log::info!("Generating ext info for path: {:?}", path);
let keys = context.generate_property_info(&self.yaml, Some(schema), path.clone());
let values = context.genereate_value_info(&self.yaml, Some(schema), path.clone());
Some(SingleNodeInfo::with_suggested_values(keys, values))
/* let b = context.with_ref_node(Some(schema), path, |node| {
Some(
node.compute_ext_path_info(&self.yaml, context, path),
// ExtInfoGen<'_, '_, ValueInfo>::compute_ext_path_info(node, &self.yaml, context, path),
)
});
*/
}
pub fn get_path_for_line_col(&self, line: usize, col: usize) -> Option<YamlPath> {
// yaml_rust2 starts counting w/ line 1
// also need to handle when line / col would point to a commented section

View File

@ -2,7 +2,9 @@ use crate::data::cache::preload_schema_file;
use crate::data::read::map_urls;
use crate::error::add_err::AnyErr;
use crate::error::validate::ValidationError;
use crate::info::types::SingleNodeInfo;
use crate::schema::ctx::Context;
use crate::schema::meta::path::YamlPath;
use crate::{info::node::YamlNode, input::wrapper::YamlWrapper};
pub fn get_info_for_line_and_col_for_yaml<'a, 'b>(
@ -20,6 +22,23 @@ pub fn get_info_for_line_and_col_for_yaml<'a, 'b>(
result.ok_or(AnyErr::NoInfo)
}
pub fn execute_evaluation_on_position<'a, 'b, S: 'a, F>(
test_yaml: &'a YamlWrapper<'a>,
line: usize,
col: usize,
calc: F,
) -> Result<S, AnyErr>
where
F: FnOnce(YamlPath) -> Option<S>,
{
let path = test_yaml
.get_path_for_line_col(line, col)
.ok_or(AnyErr::PathNotFound)?;
log::info!("Generating info for: {:?}", path);
let result = calc(path);
result.ok_or(AnyErr::NoInfo)
}
pub fn get_info_for_idx_for_yaml<'a, 'b>(
test_yaml: &'a YamlWrapper<'a>,
schema: &'b str,
@ -60,6 +79,40 @@ pub fn get_info_for_line_and_col<S>(
get_info_for_line_and_col_for_yaml(&yaml_wrapper, schema, line, col, &context).map(map)
}
pub fn base_inffor_line_and_col<S>(
test_yaml_raw: &str,
validation_schema: &str,
line: usize,
col: usize,
map: impl FnOnce(YamlNode<'_>) -> S,
) -> Result<S, AnyErr> {
let yaml_wrapper = YamlWrapper::try_from(test_yaml_raw)?;
let schema = map_urls(validation_schema);
let context = Context::new();
execute_evaluation_on_position(&yaml_wrapper, line, col, |path| {
yaml_wrapper.generate_info_for_path(schema, &context, path)
})
.map(map)
}
pub fn get_suggestions_for_line_and_col<S>(
test_yaml_raw: &str,
validation_schema: &str,
line: usize,
col: usize,
map: impl FnOnce(SingleNodeInfo) -> S,
) -> Result<S, AnyErr> {
let yaml_wrapper = YamlWrapper::try_from(test_yaml_raw)?;
let schema = map_urls(validation_schema);
let context = Context::new();
execute_evaluation_on_position(&yaml_wrapper, line, col, |path| {
yaml_wrapper.generate_extended_suggestions(schema, &context, path)
})
.map(map)
}
pub fn get_validation_result<S>(
test_yaml_raw: &str,
validation_schema: &str,

View File

@ -25,7 +25,6 @@ use crate::{
};
use crate::{
info::types::{ExtInfoGen, PropertyInfo, ValueInfo},
misc::Cap,
};
use std::collections::HashMap;
use yaml_rust2::Yaml;

View File

@ -10,13 +10,12 @@ use super::super::{
use crate::info::node::YamlNode;
use crate::info::types::{merge_info, BaseInfo, ExtInfoGen, PropertyInfo, ValueInfo};
use crate::info::validate::{
PartialValidResult, Schemavalidator, SoftValidator, StrictYamlValidator, YamlValidator,
PartialValidResult, Schemavalidator, YamlValidator,
};
use crate::info::{
generation::{InfoGenerator, PathInfoGenerator},
types::NodeBaseInfo,
};
use crate::schema::meta::annotation::HasMetaData;
use crate::schema::utils::helper::PropFromKey;
use crate::schema::utils::path::try_map_opt_child_node;
use crate::schema::utils::path::{
@ -355,8 +354,8 @@ fn is_boolean_error_true(val_res: &[WError<ValidationError>]) -> bool {
// when validating an value against a bool(scheme) - we get a BooleanMismatch error. When we do this for some additional property we map this error to ExtraProperty here
// Unfortunately the current logic in the boolean validating is: if a value is null, then it validates with Bool(false) (bc no value is present duh)
// and inalidates with Bool(true), so the additional parameter is to denote if the input was null to begin with in the yaml.
// we could prob just ignore this error alltogether, but for now i mapped it to a MissingProperty (bc the valueto the key is missing)
// and invalidates with Bool(true), so the additional parameter is to denote if the input was null to begin with in the yaml.
// we could prob just ignore this error altogether, but for now i mapped it to a MissingProperty (bc the value to the key is missing)
// yes, i recognize the inconsistency - if additionalProperties is false, and i add "unknownKey: ", then this would not be reported as an additional Property
// BUT - ignoring this error would at least make sense if we progressively validating against a scheme - because otherwise in those cases all keys would prob fail until they are completly writte n out
// lets just add a TODO and ignore this for now
@ -599,7 +598,7 @@ impl<'a: 'b, 'b, 'c: 'b> ExtInfoGen<'b, 'c, PropertyInfo> for ObjectScheme<'a> {
let mut prop_info: PropertyInfo =
additional_prop.get_node_info(storage).into();
prop_info.set_used(true);
prop_info.into()
prop_info
}
};
info.insert(hash_key, prop_info);

View File

@ -68,7 +68,7 @@ impl<'a> Context<'a> {
})
}
pub fn gen_ex2(
pub fn genereate_value_info(
&'a self,
yaml: &'a Yaml,
file: Option<&'a str>,
@ -79,7 +79,7 @@ impl<'a> Context<'a> {
schema.compute_ext_path_info(yaml, self, path)
})
}
pub fn gen_ex(
pub fn generate_property_info(
&'a self,
yaml: &'a Yaml,
file: Option<&'a str>,

View File

@ -49,7 +49,7 @@ impl SchemaPathSegment {
pub fn is(&self, str: &str) -> bool {
match self {
SchemaPathSegment::Path(p) => p == str,
SchemaPathSegment::Index(i) => str.parse::<usize>().map_or(false, |x| x == *i),
SchemaPathSegment::Index(i) => str.parse::<usize>() == Ok (*i),
_ => false,
}
}

View File

@ -7,7 +7,7 @@ use super::{
composition::{CompositionalConstrain, ExtInf, InfoGeneratorHelper},
};
use crate::info::types::{merge_info, ExtInfoGen, PropertyInfo, ValueInfo};
use crate::schema::utils::discriminator::get_property_name_discriminator;
use crate::schema::utils::discriminator::{get_property_name_discriminator, Discriminator};
use crate::schema::{ctx::Context, meta::path::YamlPath, utils::helper::YamlHelper};
use crate::schema::{nested::composition::try_get_children, utils::types::YamlValues};
use crate::{
@ -151,7 +151,6 @@ impl OneOf<'_> {
let value = yaml
.get_str_opt_yaml(self.discriminator?)
.unwrap_or(&Yaml::Null);
let key = Yaml::String(self.discriminator?.to_string());
let mut discr_map = LinkedHashMap::new();
discr_map.insert(key, value.clone());
@ -177,10 +176,19 @@ impl<'a: 'b, 'b, 'c: 'b> ExtInfoGen<'b, 'c, ValueInfo> for OneOf<'a> {
storage: &'b Context<'b>,
path: YamlPath,
) -> HashMap<String, ValueInfo> {
let info = self.derive_info_nodee(yaml, storage, |x, y, s| {
ExtInfoGen::<'_, '_, ValueInfo>::compute_ext_path_info(x, y, s, path.clone())
});
let info = if self
.discriminator
.is_some_and(|s| Discriminator(s).matches(&path))
{
self.child
.iter()
.map(|c| ExtInfoGen::<'_, '_, ValueInfo>::compute_ext_path_info(c,yaml,storage,path.clone()))
.collect()
} else {
self.derive_info_nodee(yaml, storage, |x, y, s| {
ExtInfoGen::<'_, '_, ValueInfo>::compute_ext_path_info(x, y, s, path.clone())
})
};
merge_info(info)
}
}
@ -202,19 +210,32 @@ impl<'a: 'b, 'b, 'c: 'b> ExtInfoGen<'b, 'c, PropertyInfo> for OneOf<'a> {
storage: &'b Context<'b>,
path: YamlPath,
) -> HashMap<String, PropertyInfo> {
let info = self.derive_info_nodee(yaml, storage, |x, y, s| {
ExtInfoGen::<'_, '_, PropertyInfo>::compute_ext_path_info(x, y, s, path.clone())
});
// let info = self.derive_info_nodee(yaml, storage, |x, y, s| {
// ExtInfoGen::<'_, '_, PropertyInfo>::compute_ext_path_info(x, y, s, path.clone())
// });
merge_info(info)
/*for v in self
.discriminator_values_info_if_for_sub_node(yaml, storage, path.clone())
.unwrap_or_default()
.into_iter()
.map(YamlValues::RealString)
let info = if self
.discriminator
.is_some_and(|s| Discriminator(s).matches(&path))
{
//info.add_suggested_value(&v);
}
Some(info)*/
self.child
.iter()
.map(|c| {
ExtInfoGen::<'_, '_, PropertyInfo>::compute_ext_path_info(
c,
yaml,
storage,
path.clone(),
)
})
.collect()
} else {
self.derive_info_nodee(yaml, storage, |x, y, s| {
ExtInfoGen::<'_, '_, PropertyInfo>::compute_ext_path_info(x, y, s, path.clone())
})
};
merge_info(info)
}
}

View File

@ -2,3 +2,4 @@ pub mod fetch_helper;
pub mod highlight;
pub mod provider;
mod transformer;
pub mod suggestions;

View File

@ -1,45 +1,16 @@
use serde::Serialize;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use crate::info::node::YamlNode;
use super::transformer::*;
#[wasm_bindgen]
#[derive(Default)]
#[derive(Default, Serialize)]
pub struct CodeTokenInfo {
description: String,
deprecated: bool,
value_suggestion: Vec<String>,
}
#[wasm_bindgen]
impl CodeTokenInfo {
#[wasm_bindgen(constructor)]
pub fn new(description: String, deprecated: bool, value_suggestion: Vec<String>) -> Self {
Self {
description,
deprecated,
value_suggestion,
}
}
#[wasm_bindgen(getter)]
pub fn description(&self) -> String {
self.description.clone()
}
#[wasm_bindgen(getter)]
pub fn deprecated(&self) -> bool {
self.deprecated
}
#[wasm_bindgen(getter)]
pub fn value_suggestion(&self) -> js_sys::Array {
self.value_suggestion
.iter()
.map(|s| JsValue::from_str(s))
.collect()
}
}
impl From<YamlNode<'_>> for CodeTokenInfo {
fn from(node: YamlNode) -> Self {
@ -53,3 +24,16 @@ impl From<YamlNode<'_>> for CodeTokenInfo {
}
}
}
#[cfg(target_arch = "wasm32")]
pub(crate) trait JsSerializable {
fn to_js(&self) -> Result<JsValue, JsValue>;
}
#[cfg(target_arch = "wasm32")]
impl JsSerializable for CodeTokenInfo {
fn to_js(&self) -> Result<JsValue, JsValue> {
Ok(serde_wasm_bindgen::to_value(self)?)
}
}

122
src/wasm/suggestions.rs Normal file
View File

@ -0,0 +1,122 @@
use crate::info::types::{BaseInfo, PropertyInfo, SingleNodeInfo, ValueInfo};
use crate::wasm::provider::{CodeTokenInfo, JsSerializable};
use serde::Serialize;
use wasm_bindgen::prelude::wasm_bindgen;
use wasm_bindgen::JsValue;
#[wasm_bindgen]
#[derive(Default, Serialize)]
pub struct GenericInfo {
description: String,
deprecated: bool,
title: Option<String>,
}
#[wasm_bindgen]
impl GenericInfo {
#[wasm_bindgen(constructor)]
pub fn new(description: String, deprecated: bool) -> Self {
Self {
description,
deprecated,
title: None,
}
}
#[wasm_bindgen(getter)]
pub fn description(&self) -> String {
self.description.clone()
}
#[wasm_bindgen(getter)]
pub fn deprecated(&self) -> bool {
self.deprecated
}
}
#[wasm_bindgen]
#[derive(Default, Serialize)]
pub struct AllowedValues {
values: Vec<GenericInfo>,
}
#[wasm_bindgen]
impl AllowedValues {
#[wasm_bindgen(getter)]
pub fn values(&self) -> Result<js_sys::Array, JsValue> {
self.values.iter().map(|v| v.to_js()).collect()
}
}
#[cfg(target_arch = "wasm32")]
impl JsSerializable for AllowedValues {
fn to_js(&self) -> Result<JsValue, JsValue> {
Ok(serde_wasm_bindgen::to_value(self)?)
}
}
#[cfg(target_arch = "wasm32")]
impl JsSerializable for GenericInfo {
fn to_js(&self) -> Result<JsValue, JsValue> {
Ok(serde_wasm_bindgen::to_value(self)?)
}
}
impl From<&BaseInfo> for GenericInfo {
fn from(info: &BaseInfo) -> Self {
GenericInfo {
description: info.description.clone().unwrap_or_default(),
deprecated: false,
title: None,
}
}
}
impl From<&PropertyInfo> for GenericInfo {
fn from(info: &PropertyInfo) -> Self {
GenericInfo {
description: info.description.clone().unwrap_or_default(),
deprecated: false,
title: None,
}
}
}
impl From<&ValueInfo> for GenericInfo {
fn from(info: &ValueInfo) -> Self {
GenericInfo {
description: info.description.clone().unwrap_or_default(),
deprecated: false,
title: None,
}
}
}
impl GenericInfo {
pub fn set_title(&mut self, title: String) {
self.title = Some(title);
}
}
impl From<SingleNodeInfo> for AllowedValues {
fn from(info: SingleNodeInfo) -> Self {
let values = info
.keys()
.iter()
.map(|(k, v)| {
let mut el: GenericInfo = v.into();
el.set_title(k.clone());
el
})
.chain(info.values().iter().map(|(k, v)| {
let mut el: GenericInfo = v.into();
el.set_title(k.clone());
el
}))
.collect::<Vec<GenericInfo>>();
AllowedValues { values }
}
}

View File

@ -1,7 +1,7 @@
use js_sys::Promise;
use log::info;
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
use crate::wasm::provider::JsSerializable;
use crate::{
misc::line_col_to_idx,
orchestrator::{
@ -10,6 +10,8 @@ use crate::{
},
wasm::{highlight::HighlightError, provider::CodeTokenInfo},
};
use crate::info::types::SingleNodeInfo;
use crate::orchestrator::get_suggestions_for_line_and_col;
#[wasm_bindgen]
extern "C" {
@ -35,8 +37,8 @@ pub fn get_hover_info_for_line_col(
schema: &str,
line: usize,
col: usize,
) -> Option<CodeTokenInfo> {
get_info_for_line_and_col(yaml, schema, line, col, |x| x.into()).ok()
) -> Result<JsValue, JsValue> {
get_info_for_line_and_col(yaml, schema, line, col, |x| Into::<CodeTokenInfo>::into(x).to_js()).unwrap_or(Err(JsValue::NULL))
}
#[wasm_bindgen]
@ -46,6 +48,17 @@ pub fn get_hover_info_for_idx(yaml: &str, schema: &str, index: usize) -> Option<
get_info_for_idx(yaml, schema, index, |x| x.into()).ok()
}
#[wasm_bindgen]
pub fn get_suggested_values_for_line_col(
yaml: &str,
schema: &str,
line: usize,
col: usize,
) -> Result<JsValue, JsValue> {
info!("get_suggested_values_for_line_col at line {line}, col {col}");
get_suggestions_for_line_and_col(yaml, schema, line, col, |x: SingleNodeInfo| Into::<crate::wasm::suggestions::AllowedValues>::into(x).to_js()).unwrap_or(Err(JsValue::NULL))
}
#[wasm_bindgen]
pub async fn add_schema_file(to_get: &str) -> Result<JsValue, JsValue> {
info!("add_schema_file {to_get}");