Skip to content

Commit 1f2062d

Browse files
committed
feat: added diagnostics for runtime functions
1 parent c02d688 commit 1f2062d

File tree

3 files changed

+218
-1
lines changed

3 files changed

+218
-1
lines changed

cli/src/analyser/diagnostics.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,11 @@ pub enum DiagnosticKind {
8686
DeserializationError { description: String },
8787
DuplicateDataTypeIdentifier { identifier: String },
8888
DuplicateFlowTypeIdentifier { identifier: String },
89+
DuplicateRuntimeFunctionIdentifier { identifier: String },
90+
DuplicateRuntimeParameterIdentifier { identifier: String },
8991
UndefinedDataTypeIdentifier { identifier: String },
92+
EmptyGenericMapper,
93+
GenericKeyNotInMappingTarget {key: String, target: String },
9094
NullField { field_name: String },
9195
ForbiddenVariant,
9296
UnusedGenericKey { key: String },
@@ -101,6 +105,10 @@ impl DiagnosticKind {
101105
DeserializationError { .. }
102106
| DuplicateDataTypeIdentifier { .. }
103107
| DuplicateFlowTypeIdentifier { .. }
108+
| DuplicateRuntimeFunctionIdentifier { .. }
109+
| DuplicateRuntimeParameterIdentifier { .. }
110+
| GenericKeyNotInMappingTarget { .. }
111+
| EmptyGenericMapper { .. }
104112
| UndefinedDataTypeIdentifier { .. }
105113
| NullField { .. }
106114
| ForbiddenVariant { .. }
@@ -132,12 +140,20 @@ impl Diagnose {
132140

133141
use DiagnosticKind::*;
134142
match &self.kind {
143+
EmptyGenericMapper =>
144+
error(format!("`{}` defined a generic_type but its mapper are empty!`", self.definition_name), &path),
135145
DeserializationError { description } =>
136146
error(format!("A JSON paring error occurred: `{}`", description), &path),
147+
GenericKeyNotInMappingTarget {key, target} =>
148+
error(format!("`{}` is mapping the key: {} onto the target: {}. But the target did not define this generic_key!", self.definition_name, key, target), &path),
137149
DuplicateDataTypeIdentifier { identifier } =>
138150
error(format!("The data_type `{}` is already defined resulting in a duplicate!", identifier), &path),
139151
DuplicateFlowTypeIdentifier { identifier } =>
140152
error(format!("The flow_type `{}` is already defined resulting in a duplicate!", identifier), &path),
153+
DuplicateRuntimeFunctionIdentifier { identifier } =>
154+
error(format!("The runtime_function `{}` is already defined resulting in a duplicate!", identifier), &path),
155+
DuplicateRuntimeParameterIdentifier { identifier } =>
156+
error(format!("The runtime_parameter `{}` is already defined resulting in a duplicate!", identifier), &path),
141157
UndefinedDataTypeIdentifier { identifier } =>
142158
error(format!("`{}` uses an undefined data_type_identifier: `{}`!", self.definition_name, identifier), &path),
143159
NullField { field_name } =>

cli/src/analyser/mod.rs

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
mod diagnostics;
22

3+
use clap::builder::Str;
34
use tucana::shared::{DataTypeIdentifier, DefinitionDataType, FlowType, RuntimeFunctionDefinition};
45
use tucana::shared::data_type_identifier::Type;
56
use tucana::shared::definition_data_type_rule::Config;
67
use code0_definition_reader::parser::Parser;
78
use code0_definition_reader::reader::{MetaType, ParsableDefinition, Reader};
89
use crate::analyser::diagnostics::{Diagnose, DiagnosticKind, Reporter};
9-
use crate::analyser::diagnostics::DiagnosticKind::{DuplicateDataTypeIdentifier, DuplicateFlowTypeIdentifier, NullField, UndefinedDataTypeIdentifier, UndefinedGenericKey, UndefinedTranslation, UnusedGenericKey};
10+
use crate::analyser::diagnostics::DiagnosticKind::{DuplicateDataTypeIdentifier, DuplicateFlowTypeIdentifier, DuplicateRuntimeParameterIdentifier, EmptyGenericMapper, GenericKeyNotInMappingTarget, NullField, UndefinedDataTypeIdentifier, UndefinedGenericKey, UndefinedTranslation, UnusedGenericKey};
1011

1112
#[derive(Clone)]
1213
pub struct AnalysableDataType {
@@ -22,6 +23,7 @@ pub struct AnalysableFlowType {
2223
pub id: i16
2324
}
2425

26+
#[derive(Clone)]
2527
pub struct AnalysableFunction {
2628
pub original_definition: ParsableDefinition,
2729
pub function: RuntimeFunctionDefinition,
@@ -156,6 +158,14 @@ impl Analyser {
156158
));
157159
}
158160

161+
if generic.generic_mappers.is_empty() {
162+
self.reporter.add_report(Diagnose::new(
163+
analysable_data_type.definition_data_type.clone().identifier,
164+
analysable_data_type.original_definition.clone(),
165+
EmptyGenericMapper
166+
))
167+
}
168+
159169
for mapper in generic.generic_mappers {
160170
if data_type.generic_keys.contains(&mapper.target) {
161171
result.push(mapper.target.clone())
@@ -397,7 +407,194 @@ impl Analyser {
397407
break;
398408
}
399409
}
410+
}
411+
412+
pub fn analyse_runtime_function(&mut self, analysable_function: AnalysableFunction) {
413+
let name = analysable_function.function.runtime_name.clone();
414+
let function = analysable_function.function;
415+
let original = analysable_function.original_definition;
416+
let id = analysable_function.id;
417+
418+
// Check if at least one Translation is present
419+
if function.name.is_empty() {
420+
self.reporter.add_report(Diagnose::new(
421+
name.clone(),
422+
original.clone(),
423+
UndefinedTranslation { translation_field: String::from("name") }
424+
));
425+
}
426+
427+
if function.description.is_empty() {
428+
self.reporter.add_report(Diagnose::new(
429+
name.clone(),
430+
original.clone(),
431+
UndefinedTranslation { translation_field: String::from("description") }
432+
));
433+
}
434+
435+
if function.documentation.is_empty() {
436+
self.reporter.add_report(Diagnose::new(
437+
name.clone(),
438+
original.clone(),
439+
UndefinedTranslation { translation_field: String::from("documentation") }
440+
));
441+
}
442+
443+
// Check if runtime function already exists
444+
for func in &self.functions {
445+
if func.id == id {
446+
continue
447+
}
448+
449+
if func.function.runtime_name.to_lowercase() == name.clone().to_lowercase() {
450+
self.reporter.add_report(Diagnose::new(
451+
name.clone(),
452+
original.clone(),
453+
DuplicateFlowTypeIdentifier { identifier: name.clone() }
454+
));
455+
break;
456+
}
457+
}
458+
459+
let mut detected_generic_keys: Vec<String> = vec![];
460+
if let Some(identifier) = function.return_type_identifier {
461+
detected_generic_keys.append(&mut self.handle_function_data_type_identifier(name.clone(), original.clone(), identifier));
462+
}
463+
464+
let mut parameter_names: Vec<String> = vec![];
465+
for parameter in function.runtime_parameter_definitions {
466+
467+
// Check if at least one Translation is present
468+
if parameter.name.is_empty() {
469+
self.reporter.add_report(Diagnose::new(
470+
name.clone(),
471+
original.clone(),
472+
UndefinedTranslation { translation_field: String::from("name") }
473+
));
474+
}
475+
476+
if parameter.description.is_empty() {
477+
self.reporter.add_report(Diagnose::new(
478+
name.clone(),
479+
original.clone(),
480+
UndefinedTranslation { translation_field: String::from("description") }
481+
));
482+
}
483+
484+
if parameter.documentation.is_empty() {
485+
self.reporter.add_report(Diagnose::new(
486+
name.clone(),
487+
original.clone(),
488+
UndefinedTranslation { translation_field: String::from("documentation") }
489+
));
490+
}
491+
492+
// Check if data_type exists
493+
if let Some(identifier) = parameter.data_type_identifier {
494+
detected_generic_keys.append(&mut self.handle_function_data_type_identifier(name.clone(), original.clone(), identifier));
495+
} else {
496+
self.reporter.add_report(Diagnose::new(
497+
name.clone(),
498+
original.clone(),
499+
NullField { field_name: String::from("data_type") }
500+
));
501+
}
502+
503+
if parameter_names.contains(&parameter.runtime_name) {
504+
self.reporter.add_report(Diagnose::new(
505+
name.clone(),
506+
original.clone(),
507+
DuplicateRuntimeParameterIdentifier { identifier: parameter.runtime_name.clone() }
508+
));
509+
}
510+
511+
parameter_names.push(parameter.runtime_name);
512+
}
513+
514+
let defined_but_unused = function.generic_keys.iter().filter(|key| !detected_generic_keys.contains(key)).collect::<Vec<&String>>();
515+
let used_but_undefined = detected_generic_keys.iter().filter(|key| !function.generic_keys.contains(key)).collect::<Vec<&String>>();
516+
517+
for key in defined_but_unused {
518+
self.reporter.add_report(Diagnose::new(
519+
name.clone(),
520+
original.clone(),
521+
UnusedGenericKey { key: key.clone() }
522+
));
523+
}
524+
525+
for key in used_but_undefined {
526+
self.reporter.add_report(Diagnose::new(
527+
name.clone(),
528+
original.clone(),
529+
UndefinedGenericKey { key: key.clone() }
530+
));
531+
}
532+
}
533+
534+
fn handle_function_data_type_identifier(&mut self, name: String, original: ParsableDefinition, identifier: DataTypeIdentifier) -> Vec<String> {
535+
let mut result: Vec<String> = vec![];
536+
if let Some(r#type) = identifier.r#type {
537+
match r#type {
538+
Type::DataTypeIdentifier(data_type) => {
539+
if !self.data_type_identifier_exists(data_type.clone(), -1) {
540+
self.reporter.add_report(Diagnose::new(
541+
name.clone(),
542+
original.clone(),
543+
UndefinedDataTypeIdentifier { identifier: data_type.clone() }
544+
))
545+
};
546+
},
547+
Type::GenericType(generic_type) => {
548+
if !self.data_type_identifier_exists(generic_type.data_type_identifier.clone(), -1) {
549+
self.reporter.add_report(Diagnose::new(
550+
name.clone(),
551+
original.clone(),
552+
UndefinedDataTypeIdentifier { identifier: generic_type.data_type_identifier.clone() }
553+
))
554+
}
555+
556+
if generic_type.generic_mappers.is_empty() {
557+
self.reporter.add_report(Diagnose::new(
558+
name.clone(),
559+
original.clone(),
560+
EmptyGenericMapper
561+
))
562+
}
563+
564+
for mapper in &generic_type.generic_mappers {
565+
for source in mapper.source.clone() {
566+
result.append(&mut self.handle_function_data_type_identifier(name.clone(), original.clone(), source))
567+
}
568+
569+
if !self.generic_key_in_target(mapper.target.clone(), generic_type.data_type_identifier.clone()) {
570+
self.reporter.add_report(Diagnose::new(
571+
name.clone(),
572+
original.clone(),
573+
GenericKeyNotInMappingTarget { key: mapper.target.clone(), target: generic_type.data_type_identifier.clone() }
574+
))
575+
}
576+
}
577+
},
578+
Type::GenericKey(key) => {
579+
result.push(key.clone())
580+
},
581+
}
582+
}
583+
584+
result
585+
}
400586

587+
fn generic_key_in_target(&mut self, key: String, target: String) -> bool {
588+
let data_types: Vec<DefinitionDataType> = self.data_types.iter().map(|d| d.definition_data_type.clone()).collect();
589+
for data_type in data_types {
590+
if target.to_lowercase() != data_type.identifier.to_lowercase() {
591+
continue;
592+
}
593+
594+
return data_type.generic_keys.contains(&key);
595+
}
596+
597+
false
401598
}
402599

403600
pub fn report(&self) {

cli/src/command/report.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ pub fn report_errors(path: Option<String>) {
2222
analyser.analyse_flow_type(flow_type.clone());
2323
}
2424

25+
for functions in analyser.functions.clone() {
26+
analyser.analyse_runtime_function(functions.clone());
27+
}
28+
2529
analyser.report();
2630

2731
error_table(&parser.features);

0 commit comments

Comments
 (0)