mirror of
https://github.com/fluencelabs/aquavm
synced 2025-06-24 12:11:33 +00:00
Get rid of unsafe code in the interpreter (#303)
* Get rid of unsafe code unless really necessary * Add lint levels where appropriate Some crates (air-beautifier, air-testing-framework) have lot of rust_2018_idioms violations, that will be resolved later.
This commit is contained in:
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
|
@ -14,6 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
nonstandard_style,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_variables,
|
||||||
|
unused_unsafe,
|
||||||
|
unreachable_patterns
|
||||||
|
)]
|
||||||
|
|
||||||
mod call_request_parameters;
|
mod call_request_parameters;
|
||||||
mod call_service_result;
|
mod call_service_result;
|
||||||
mod outcome;
|
mod outcome;
|
||||||
|
@ -18,6 +18,8 @@ use super::*;
|
|||||||
use air_lambda_parser::LambdaAST;
|
use air_lambda_parser::LambdaAST;
|
||||||
use air_lambda_parser::ValueAccessor;
|
use air_lambda_parser::ValueAccessor;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
impl<'i> ScalarWithLambda<'i> {
|
impl<'i> ScalarWithLambda<'i> {
|
||||||
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
pub fn new(name: &'i str, lambda: Option<LambdaAST<'i>>, position: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -27,16 +29,15 @@ impl<'i> ScalarWithLambda<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's unsafe method that should be used only for tests
|
|
||||||
pub(crate) fn from_raw_lambda(
|
pub(crate) fn from_raw_lambda(
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
lambda: Vec<ValueAccessor<'i>>,
|
lambda: Vec<ValueAccessor<'i>>,
|
||||||
position: usize,
|
position: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
let lambda = LambdaAST::try_from(lambda).ok();
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
lambda: Some(lambda),
|
lambda,
|
||||||
position,
|
position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -51,17 +52,16 @@ impl<'i> StreamWithLambda<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's unsafe method that should be used only for tests
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn from_raw_lambda(
|
pub(crate) fn from_raw_lambda(
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
lambda: Vec<ValueAccessor<'i>>,
|
lambda: Vec<ValueAccessor<'i>>,
|
||||||
position: usize,
|
position: usize,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let lambda = unsafe { LambdaAST::new_unchecked(lambda) };
|
let lambda = LambdaAST::try_from(lambda).ok();
|
||||||
Self {
|
Self {
|
||||||
name,
|
name,
|
||||||
lambda: Some(lambda),
|
lambda,
|
||||||
position,
|
position,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,7 +154,6 @@ impl<'i> VariableWithLambda<'i> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn from_raw_lambda_scalar(
|
pub(crate) fn from_raw_lambda_scalar(
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
@ -165,7 +164,6 @@ impl<'i> VariableWithLambda<'i> {
|
|||||||
Self::Scalar(scalar)
|
Self::Scalar(scalar)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function is unsafe and lambda must be non-empty, although it's used only for tests
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn from_raw_lambda_stream(
|
pub(crate) fn from_raw_lambda_stream(
|
||||||
name: &'i str,
|
name: &'i str,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
nonstandard_style,
|
nonstandard_style,
|
||||||
|
@ -25,6 +25,8 @@ use air_lambda_parser::ValueAccessor;
|
|||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
use fstrings::format_args_f;
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
fn run_lexer(input: &str) -> Vec<Spanned<Token<'_>, usize, LexerError>> {
|
||||||
let lexer = AIRLexer::new(input);
|
let lexer = AIRLexer::new(input);
|
||||||
lexer.collect()
|
lexer.collect()
|
||||||
@ -306,14 +308,13 @@ fn lambda() {
|
|||||||
0,
|
0,
|
||||||
Token::ScalarWithLambda {
|
Token::ScalarWithLambda {
|
||||||
name: "value",
|
name: "value",
|
||||||
lambda: unsafe {
|
lambda: LambdaAST::try_from(vec![
|
||||||
LambdaAST::new_unchecked(vec![
|
|
||||||
ValueAccessor::FieldAccessByName {
|
ValueAccessor::FieldAccessByName {
|
||||||
field_name: "field",
|
field_name: "field",
|
||||||
},
|
},
|
||||||
ValueAccessor::ArrayAccess { idx: 1 },
|
ValueAccessor::ArrayAccess { idx: 1 },
|
||||||
])
|
])
|
||||||
},
|
.unwrap(),
|
||||||
position: 0,
|
position: 0,
|
||||||
},
|
},
|
||||||
LAMBDA.len(),
|
LAMBDA.len(),
|
||||||
@ -413,11 +414,12 @@ fn last_error() {
|
|||||||
fn last_error_instruction() {
|
fn last_error_instruction() {
|
||||||
const LAST_ERROR: &str = r#"%last_error%.$.instruction"#;
|
const LAST_ERROR: &str = r#"%last_error%.$.instruction"#;
|
||||||
|
|
||||||
let token = Token::LastErrorWithLambda(unsafe {
|
let token = Token::LastErrorWithLambda(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "instruction",
|
field_name: "instruction",
|
||||||
}])
|
}])
|
||||||
});
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
||||||
}
|
}
|
||||||
@ -426,11 +428,12 @@ fn last_error_instruction() {
|
|||||||
fn last_error_message() {
|
fn last_error_message() {
|
||||||
const LAST_ERROR: &str = r#"%last_error%.$.message"#;
|
const LAST_ERROR: &str = r#"%last_error%.$.message"#;
|
||||||
|
|
||||||
let token = Token::LastErrorWithLambda(unsafe {
|
let token = Token::LastErrorWithLambda(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "message",
|
field_name: "message",
|
||||||
}])
|
}])
|
||||||
});
|
.unwrap(),
|
||||||
|
);
|
||||||
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,11 +441,12 @@ fn last_error_message() {
|
|||||||
fn last_error_peer_id() {
|
fn last_error_peer_id() {
|
||||||
const LAST_ERROR: &str = r#"%last_error%.$.peer_id"#;
|
const LAST_ERROR: &str = r#"%last_error%.$.peer_id"#;
|
||||||
|
|
||||||
let token = Token::LastErrorWithLambda(unsafe {
|
let token = Token::LastErrorWithLambda(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "peer_id",
|
field_name: "peer_id",
|
||||||
}])
|
}])
|
||||||
});
|
.unwrap(),
|
||||||
|
);
|
||||||
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,11 +454,12 @@ fn last_error_peer_id() {
|
|||||||
fn last_error_non_standard_field() {
|
fn last_error_non_standard_field() {
|
||||||
const LAST_ERROR: &str = r#"%last_error%.$.asdasd"#;
|
const LAST_ERROR: &str = r#"%last_error%.$.asdasd"#;
|
||||||
|
|
||||||
let token = Token::LastErrorWithLambda(unsafe {
|
let token = Token::LastErrorWithLambda(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "asdasd",
|
field_name: "asdasd",
|
||||||
}])
|
}])
|
||||||
});
|
.unwrap(),
|
||||||
|
);
|
||||||
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
lexer_test(LAST_ERROR, Single(Ok((0, token, LAST_ERROR.len()))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
|
|||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
use fstrings::format_args_f;
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ap_with_literal() {
|
fn ap_with_literal() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
@ -75,11 +77,12 @@ fn ap_with_last_error() {
|
|||||||
|
|
||||||
let actual = parse(source_code);
|
let actual = parse(source_code);
|
||||||
let expected = ap(
|
let expected = ap(
|
||||||
ApArgument::LastError(Some(unsafe {
|
ApArgument::LastError(Some(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "message",
|
field_name: "message",
|
||||||
}])
|
}])
|
||||||
})),
|
.unwrap(),
|
||||||
|
)),
|
||||||
ApResult::Stream(Stream::new("$stream", 37)),
|
ApResult::Stream(Stream::new("$stream", 37)),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -175,7 +178,7 @@ fn ap_with_canon_stream_with_lambda() {
|
|||||||
let expected = ap(
|
let expected = ap(
|
||||||
ApArgument::CanonStream(CanonStreamWithLambda::new(
|
ApArgument::CanonStream(CanonStreamWithLambda::new(
|
||||||
canon_stream,
|
canon_stream,
|
||||||
Some(unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) }),
|
Some(LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap()),
|
||||||
13,
|
13,
|
||||||
)),
|
)),
|
||||||
ApResult::Scalar(Scalar::new(scalar, 33)),
|
ApResult::Scalar(Scalar::new(scalar, 33)),
|
||||||
|
@ -24,6 +24,7 @@ use fstrings::f;
|
|||||||
use fstrings::format_args_f;
|
use fstrings::format_args_f;
|
||||||
use lalrpop_util::ParseError;
|
use lalrpop_util::ParseError;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -471,12 +472,11 @@ fn canon_stream_with_lambda_in_triplet() {
|
|||||||
let expected = call(
|
let expected = call(
|
||||||
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
|
CallInstrValue::Variable(VariableWithLambda::canon_stream_wl(
|
||||||
canon_stream,
|
canon_stream,
|
||||||
unsafe {
|
LambdaAST::try_from(vec![
|
||||||
LambdaAST::new_unchecked(vec![
|
|
||||||
ValueAccessor::ArrayAccess { idx: 0 },
|
ValueAccessor::ArrayAccess { idx: 0 },
|
||||||
ValueAccessor::FieldAccessByName { field_name: "path" },
|
ValueAccessor::FieldAccessByName { field_name: "path" },
|
||||||
])
|
])
|
||||||
},
|
.unwrap(),
|
||||||
19,
|
19,
|
||||||
)),
|
)),
|
||||||
CallInstrValue::Literal(service_id),
|
CallInstrValue::Literal(service_id),
|
||||||
|
@ -21,6 +21,8 @@ use crate::ast::ScalarWithLambda;
|
|||||||
use air_lambda_ast::LambdaAST;
|
use air_lambda_ast::LambdaAST;
|
||||||
use air_lambda_ast::ValueAccessor;
|
use air_lambda_ast::ValueAccessor;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_fail_last_error() {
|
fn parse_fail_last_error() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
@ -59,11 +61,12 @@ fn parse_fail_scalar_with_lambda() {
|
|||||||
let instruction = parse(source_code);
|
let instruction = parse(source_code);
|
||||||
let expected = fail_scalar(ScalarWithLambda::new(
|
let expected = fail_scalar(ScalarWithLambda::new(
|
||||||
"scalar",
|
"scalar",
|
||||||
Some(unsafe {
|
Some(
|
||||||
LambdaAST::new_unchecked(vec![ValueAccessor::FieldAccessByName {
|
LambdaAST::try_from(vec![ValueAccessor::FieldAccessByName {
|
||||||
field_name: "field_accessor",
|
field_name: "field_accessor",
|
||||||
}])
|
}])
|
||||||
}),
|
.unwrap(),
|
||||||
|
),
|
||||||
18,
|
18,
|
||||||
));
|
));
|
||||||
assert_eq!(instruction, expected)
|
assert_eq!(instruction, expected)
|
||||||
|
@ -22,6 +22,8 @@ use air_lambda_ast::{LambdaAST, ValueAccessor};
|
|||||||
use fstrings::f;
|
use fstrings::f;
|
||||||
use fstrings::format_args_f;
|
use fstrings::format_args_f;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_match() {
|
fn parse_match() {
|
||||||
let source_code = r#"
|
let source_code = r#"
|
||||||
@ -52,7 +54,7 @@ fn parse_match_with_canon_stream() {
|
|||||||
let expected = match_(
|
let expected = match_(
|
||||||
Value::Variable(VariableWithLambda::canon_stream_wl(
|
Value::Variable(VariableWithLambda::canon_stream_wl(
|
||||||
canon_stream,
|
canon_stream,
|
||||||
unsafe { LambdaAST::new_unchecked(vec![ValueAccessor::ArrayAccess { idx: 0 }]) },
|
LambdaAST::try_from(vec![ValueAccessor::ArrayAccess { idx: 0 }]).unwrap(),
|
||||||
16,
|
16,
|
||||||
)),
|
)),
|
||||||
Value::Variable(VariableWithLambda::scalar("v2", 36)),
|
Value::Variable(VariableWithLambda::scalar("v2", 36)),
|
||||||
|
@ -14,6 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
nonstandard_style,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_variables,
|
||||||
|
unused_unsafe,
|
||||||
|
unreachable_patterns
|
||||||
|
)]
|
||||||
|
|
||||||
mod instructions_tracker;
|
mod instructions_tracker;
|
||||||
|
|
||||||
pub use instructions_tracker::*;
|
pub use instructions_tracker::*;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
|
@ -14,6 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
nonstandard_style,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_variables,
|
||||||
|
unused_unsafe,
|
||||||
|
unreachable_patterns
|
||||||
|
)]
|
||||||
|
|
||||||
mod call_request_parameters;
|
mod call_request_parameters;
|
||||||
mod call_service_result;
|
mod call_service_result;
|
||||||
mod interpreter_outcome;
|
mod interpreter_outcome;
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
nonstandard_style,
|
nonstandard_style,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
nonstandard_style,
|
nonstandard_style,
|
||||||
|
@ -23,6 +23,8 @@ use crate::ValueAccessor;
|
|||||||
|
|
||||||
use va_lambda::LambdaParser;
|
use va_lambda::LambdaParser;
|
||||||
|
|
||||||
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
// Caching parser to cache internal regexes, which are expensive to instantiate
|
// Caching parser to cache internal regexes, which are expensive to instantiate
|
||||||
// See also https://github.com/lalrpop/lalrpop/issues/269
|
// See also https://github.com/lalrpop/lalrpop/issues/269
|
||||||
thread_local!(static PARSER: LambdaParser = LambdaParser::new());
|
thread_local!(static PARSER: LambdaParser = LambdaParser::new());
|
||||||
@ -43,10 +45,5 @@ pub fn parse(lambda: &str) -> LambdaParserResult<'_, LambdaAST> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
|
fn try_to_lambda(accessors: Vec<ValueAccessor>) -> LambdaParserResult<'_, LambdaAST> {
|
||||||
if accessors.is_empty() {
|
LambdaAST::try_from(accessors).or(Err(LambdaParserError::EmptyLambda))
|
||||||
return Err(LambdaParserError::EmptyLambda);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ast = unsafe { LambdaAST::new_unchecked(accessors) };
|
|
||||||
Ok(ast)
|
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
#![warn(rust_2018_idioms)]
|
#![warn(rust_2018_idioms)]
|
||||||
#![deny(
|
#![deny(
|
||||||
dead_code,
|
dead_code,
|
||||||
|
@ -14,6 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#![forbid(unsafe_code)]
|
||||||
|
#![warn(rust_2018_idioms)]
|
||||||
|
#![deny(
|
||||||
|
dead_code,
|
||||||
|
nonstandard_style,
|
||||||
|
unused_imports,
|
||||||
|
unused_mut,
|
||||||
|
unused_variables,
|
||||||
|
unused_unsafe,
|
||||||
|
unreachable_patterns
|
||||||
|
)]
|
||||||
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
Reference in New Issue
Block a user