use super::super::{Argument, Token, TokenArgument, TryFromArgument, TryFromArgumentGroup};
use super::TokenDeserialize;
use df_ls_core::{Any, BangArgN, BangArgNOrValue, BangArgNSequence, Reference};
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
crate::token_deserialize_unary_token!(Any);
impl TryFromArgumentGroup for Any {
fn try_from_argument_group(
token: &mut Token,
source: &str,
diagnostics: &mut DiagnosticsInfo,
add_diagnostics_on_err: bool,
) -> Result<Self, ()> {
token.check_as_required_argument(source, diagnostics, add_diagnostics_on_err)?;
let arg = token.get_current_arg_opt();
let result = Self::try_from_argument(arg, source, diagnostics, add_diagnostics_on_err);
token.consume_argument();
result
}
}
impl TryFromArgument for Any {
fn try_from_argument(
arg_opt: Option<&Argument>,
_source: &str,
diagnostics: &mut DiagnosticsInfo,
add_diagnostics_on_err: bool,
) -> Result<Self, ()> {
if let Some(arg) = arg_opt {
from_token_arg_to_any(arg, &arg.value, diagnostics, add_diagnostics_on_err)
} else {
Err(())
}
}
fn expected_argument_types() -> String {
"Any".to_owned()
}
}
pub(super) fn from_token_arg_to_any(
arg: &Argument,
token_arg: &TokenArgument,
diagnostics: &mut DiagnosticsInfo,
add_diagnostics_on_err: bool,
) -> Result<Any, ()> {
match token_arg {
TokenArgument::TVInteger(i64_value) => {
if *i64_value > (u32::MAX as i64) {
if add_diagnostics_on_err {
diagnostics.add_message(
DMExtraInfo {
range: arg.node.get_range(),
message_template_data: hash_map! {
"max_value" => u32::MAX.to_string(),
},
},
"too_large_int",
);
}
return Err(());
}
if *i64_value < (i32::MIN as i64) {
if add_diagnostics_on_err {
diagnostics.add_message(
DMExtraInfo {
range: arg.node.get_range(),
message_template_data: hash_map! {
"min_value" => i32::MIN.to_string(),
},
},
"too_small_int",
);
}
return Err(());
}
Ok(Any::Integer(*i64_value))
}
TokenArgument::TVCharacter(v) => Ok(Any::Character(*v)),
TokenArgument::TVArgN(v) => Ok(Any::ArgN(*v)),
TokenArgument::TVString(v) => Ok(Any::String(v.clone())),
TokenArgument::TVReference(v) => Ok(Any::Reference(Reference(v.clone()))),
TokenArgument::TVPipeArguments(v) => Ok(Any::PipeArguments({
let mut list = Vec::new();
for item in v {
list.push(match item {
TokenArgument::TVEmpty => None,
_ => Some(from_token_arg_to_any(
arg,
item,
diagnostics,
add_diagnostics_on_err,
)?),
});
}
list
})),
TokenArgument::TVBangArgNSequence(v) => Ok(Any::BangArgNSequence({
let mut list = Vec::new();
for item in v {
list.push(match item {
TokenArgument::TVBangArgN(n) => BangArgNOrValue::BangArgN(BangArgN(*n)),
TokenArgument::TVString(s) => BangArgNOrValue::Value(s.clone()),
_ => unreachable!("Only support BangArgN and String"),
});
}
BangArgNSequence(list)
})),
_ => {
if add_diagnostics_on_err {
diagnostics.add_message(
DMExtraInfo {
range: arg.node.get_range(),
message_template_data: hash_map! {
"expected_parameters" => Any::expected_argument_types(),
"found_parameters" => arg.value.argument_to_token_type_name(),
},
},
"wrong_arg_type",
);
}
Err(())
}
}
}
impl From<Any> for TokenArgument {
fn from(item: Any) -> TokenArgument {
match item {
Any::Integer(v) => TokenArgument::TVInteger(v),
Any::Character(v) => TokenArgument::TVCharacter(v),
Any::ArgN(v) => TokenArgument::TVArgN(v),
Any::String(v) => TokenArgument::TVString(v),
Any::Reference(v) => TokenArgument::TVReference(v.into()),
Any::PipeArguments(v) => {
TokenArgument::TVPipeArguments(v.into_iter().map(|v| v.into()).collect())
}
Any::BangArgNSequence(v) => {
TokenArgument::TVBangArgNSequence(v.0.into_iter().map(|v| v.into()).collect())
}
}
}
}
impl From<Option<Any>> for TokenArgument {
fn from(item: Option<Any>) -> TokenArgument {
match item {
Some(value) => match value {
Any::Integer(v) => TokenArgument::TVInteger(v),
Any::Character(v) => TokenArgument::TVCharacter(v),
Any::ArgN(v) => TokenArgument::TVArgN(v),
Any::String(v) => TokenArgument::TVString(v),
Any::Reference(v) => TokenArgument::TVReference(v.into()),
Any::PipeArguments(v) => {
TokenArgument::TVPipeArguments(v.into_iter().map(|v| v.into()).collect())
}
Any::BangArgNSequence(v) => {
TokenArgument::TVBangArgNSequence(v.0.into_iter().map(|v| v.into()).collect())
}
},
None => TokenArgument::TVEmpty,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_tree_structure;
use crate::test_utils::SyntaxTestBuilder;
use df_ls_diagnostics::{Position, Range};
use df_ls_lexical_analysis::test_utils::LexerTestBuilder;
#[test]
fn test_any_correct() {
let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
LexerTestBuilder::test_source(
"header
[REF:600]
[REF:-2]
[REF:'a']
[REF:ARG2]
[REF:text]
[REF:ANY]
[REF:string|pipe]",
)
.add_test_lexer_diagnostics_codes(vec![])
.add_test_lexer_diagnostics_ranges(vec![]),
)
.add_test_syntax_diagnostics_codes(vec![])
.add_test_syntax_diagnostics_ranges(vec![]);
test_tree_structure!(
test_builder,
[
Any => Any::Integer(600),
Any => Any::Integer(-2),
Any => Any::Character('a'),
Any => Any::ArgN(2),
Any => Any::String("text".to_owned()),
Any => Any::Reference(Reference("ANY".to_owned())),
Any => Any::PipeArguments(vec![
Some(Any::String("string".to_owned())),
Some(Any::String("pipe".to_owned()))
]),
]
);
}
#[test]
fn test_any_incorrect() {
let test_builder = SyntaxTestBuilder::from_lexer_test_builder(
LexerTestBuilder::test_source(
"header
[REF:]
[REF:99999999999999]",
)
.add_test_lexer_diagnostics_codes(vec![])
.add_test_lexer_diagnostics_ranges(vec![]),
)
.add_test_syntax_diagnostics_codes(vec!["wrong_arg_type", "too_large_int"])
.add_test_syntax_diagnostics_ranges(vec![
Range {
start: Position {
line: 1,
character: 21,
},
end: Position {
line: 1,
character: 21,
},
},
Range {
start: Position {
line: 2,
character: 21,
},
end: Position {
line: 2,
character: 35,
},
},
]);
test_tree_structure!(
test_builder,
[
Option<Any> =!,
Option<Any> =!,
]
);
}
}