aerospike / aerospike-client-rust Goto Github PK
View Code? Open in Web Editor NEWRust client for the Aerospike database
Home Page: https://www.aerospike.com/
License: Other
Rust client for the Aerospike database
Home Page: https://www.aerospike.com/
License: Other
I don't see the support for batchRead or batchGet in the https://www.aerospike.com/apidocs/rust/aerospike/index.html?search=batch. Either the docs are not updated or something is off, we cannot map one to one correlation with github codes and API docs.
There is a bug with the append_items list operation, resulting in adding 2 int values in front of the values.
I'm not sure what the first one (always 0) is, but the second one looks like the length of the value list that should be appended.
This is reproducible with every execution of the append_items operation.
Using a batch of single append operations works as workaround.
Server is 5.2.0.5 EE.
Implement support for batch read requests using the Batch Index protocol.
this is a repost from #104 (comment) since it's a more specific issue about the expressions branch PR #100
here's a repro with aerospike:ce-5.6.0.13
#[test]
fn test2() {
use aerospike::{self, expressions, operations::{self, maps, cdt_context, lists}};
let ac = aerospike::Client::new(
&aerospike::ClientPolicy::default(), &"0.0.0.0:3000".to_string()
).unwrap();
let key = aerospike::as_key!("test", "test", "test");
ac.put(
&aerospike::WritePolicy::default(),
&key,
&[aerospike::as_bin!("bin", aerospike::as_map!())]
).unwrap();
ac.operate(
&aerospike::WritePolicy::default(),
&key,
&[
operations::exp::write_exp(
"bin",
&expressions::cond(
vec![
expressions::eq(
expressions::maps::get_by_key(
maps::MapReturnType::Count,
expressions::ExpType::INT,
expressions::string_val("key".to_string()),
expressions::map_bin("bin".to_string()),
&[]
),
expressions::int_val(0)
),
expressions::maps::put(
&maps::MapPolicy::default(),
expressions::string_val("key".to_string()),
expressions::list_val(vec![]),
expressions::map_bin("bin".to_string()),
&[]
),
expressions::unknown(),
]
),
operations::exp::ExpWriteFlags::Default
),
operations::lists::append(
&operations::lists::ListPolicy::new(
operations::lists::ListOrderType::Ordered,
operations::lists::ListWriteFlags::Default
),
"bin",
&aerospike::as_val!("val")
)
.set_context(&[
cdt_context::ctx_map_key(aerospike::as_val!("key")),
])
]
).unwrap();
println!("{:?}", ac.get(&aerospike::ReadPolicy::default(), &key, aerospike::Bins::All).unwrap().bins);
ac.operate(
&aerospike::WritePolicy::default(),
&key,
&[
operations::lists::remove_by_value(
"bin", &aerospike::as_val!("val"), operations::lists::ListReturnType::None
).set_context(&[operations::cdt_context::ctx_map_key(aerospike::as_val!("key"))]),
]
).unwrap();
println!("{:?}", ac.get(&aerospike::ReadPolicy::default(), &key, aerospike::Bins::All).unwrap().bins);
ac.operate(
&aerospike::WritePolicy::default(),
&key,
&[
operations::exp::write_exp(
"bin",
&expressions::cond(vec![
expressions::eq(
expressions::lists::size(
expressions::map_bin("bin".to_string()),
&[operations::cdt_context::ctx_map_key(aerospike::as_val!("key"))]
),
expressions::int_val(0)
),
expressions::maps::remove_by_key(
expressions::string_val("key".to_string()),
expressions::map_bin("bin".to_string()),
&[]
),
expressions::unknown()
]),
operations::exp::ExpWriteFlags::EvalNoFail
)
]
).unwrap();
println!("{:?}", ac.get(&aerospike::ReadPolicy::default(), &key, aerospike::Bins::All).unwrap().bins);
}
output:
{"bin": HashMap({String("key"): List([String("val")])})}
{"bin": HashMap({String("key"): List([])})}
thread 'tests::store::test2' panicked at 'index out of bounds: the len is 125 but the index is 125', .../.cargo/git/checkouts/aerospike-client-rust-34a63ba2784a38de/231a73e/src/commands/buffer.rs:1330:9
the failing removal write expression works fine using the python client
import aerospike
from aerospike_helpers import cdt_ctx, expressions
from aerospike_helpers.operations import expression_operations
ac = aerospike.client({'hosts': [('localhost', 3000)]})
ac.connect()
key = ('test', 'test', 'test')
ac.put(key, {'bin': {'key': []}})
print(ac.get(key)[2])
ac.operate(
key,
[
expression_operations.expression_write(
'bin',
expressions.Cond(
expressions.Eq(expressions.ListSize([cdt_ctx.cdt_ctx_map_key('key')], expressions.MapBin('bin')), 0),
expressions.MapRemoveByKey(None, 'key', 'bin'),
expressions.Unknown()
).compile(),
aerospike.EXP_WRITE_EVAL_NO_FAIL
)
]
)
print(ac.get(key)[2])
{'bin': {'key': []}}
{'bin': {}}
so this isn't a problem with expressions::cond
in general since the initial map put works fine and replacing
expressions::lists::size(
expressions::map_bin("bin".to_string()),
&[operations::cdt_context::ctx_map_key(aerospike::as_val!("key"))]
)
with expressions::int_val(0)
(a trivial equality) works as expected
{"bin": HashMap({String("key"): List([String("val")])})}
{"bin": HashMap({String("key"): List([])})}
{"bin": HashMap({})}
and cdt_context::ctx_map_key
works in different contexts (like the initial list append) so i'm lead to believe it's something about the combo of the two
Hi!
I'm trying to use FloatValue
and it looks weird that it has u32
and u64
in the enum declaration instead of f32
and f64
correspondingly:
aerospike-client-rust/src/value.rs
Lines 35 to 42 in 430780a
So in order to use for example f32
value it is necessary to invoke mem::transmute_copy::<u32, f32>
.
Query operations using a secondary index filter fail on Aerospike Server 3.15.1.x. The server returns a parameter error (status code 4). The same queries work against Aerospike Server 3.15.0.x and earlier.
The error is caused by an incorrectly sized AS_MSG_FIELD_TYPE_INDEX_RANGE
field in the protocol message. Server versions 3.15.0.x and prior are processing the query despite the incorrect field size but server 3.15.1.x returns a parameter error instead. (Ref. aerospike/aerospike-server@69a81d9)
Hello,
i just started to implement the Rust Client into one of my Services.
While doing that, i found out that it is not supporting Predicate Filtering filtering. It would be great if this Client would also support it.
Querying for multiple secondary bins is really hacky without this.
Thanks
Jonas
the python docs say
map_write_mode should be used for Aerospike Server versions < 4.3.0 and map_write_flags should be used for Aerospike server versions greater than or equal to 4.3.0 .
The current batch read response protocol sends back the digest for every record. This information was used to verify that the batch index (also sent back for every record) pointed to the correct key entry. Because the digest is not strictly necessary, we plan to eventually remove it from the batch response protocol.
E.g. Java client change (CLIENT-1119): aerospike/aerospike-client-java@27a855a
Hello,
currently the client only supports a limited set of operations for the custom data types and bitwise.
Is there a specific reasons only the few currently are supported or was there just no time to implement the others like list remove by value?
Im probably going to implement them, but i was curious why some are supported and some are not.
Also the list cdt seems to be not fully implemented (return types etc)
Add support for durable delete write policy.
Note: Requires Aerospike Server v3.10 or later.
Create a benchmark suite to measure client performance in terms of throughput (transactions/second) as well as overhead (e.g. memory consumption). Goal is to be able to run the benchmark before/after major changes and before every release in order to detect performance degradations. Secondary goal is to be able to compare the client performance to that of other Aerospike clients, e.g. C, Java, Go, etc.
Ref.: Java client benchmark: https://github.com/aerospike/aerospike-client-java/tree/master/benchmarks
Hi team,
We are new to Rust and we are trying something to achieve here, like other languages, we want to create a client and share the same with many parts of our code.
We cannot find a simple way to share the client with other functions in the code, as we don't find a sample anywhere, it would be helpful if anyone gives a simple example.
Thanks
Note: Requires Aerospike Server version 3.10 or later.
Value currently only has a to_string method.
As far as i understand thats more meant as some sort of "debug" function to just print whats inside the value.
I guess it might be a good idea to add more functions for other Data Types.
This functions could return a Result with the Value or an Error if the Type is not correct.
Currently the only way to do this is pattern matching. But if you need to match for example 10 bins, that becomes very ugly.
The type of the bins is probably known in most applications anyways. A strictly typed language like rust more or less forces you to know when you write it.
Im thinking about something like
pub fn to_map(&self) -> Result<HashMap<Value, Value>, TypeError> {
match &self {
Value::HashMap(map) => Ok(map)
_ => Err(TypeError::new())
}
}
pub fn to_list(&self) -> Result<Vec<Value>, TypeError> {
match &self {
Value::List(list) => Ok(list)
_ => Err(TypeError::new())
}
}
In the end it will not change how this fields are matched, but it reduces the boilerplate and nesting required to parse the record into a struct for example. In that case, you probably want to throw an Error if a bin type does not match the struct type.
Any thoughts on this?
Hi!
I noticed that last year, you added support for batch-read. For the small program I'm writing, I would have loved to have batch-exists.
I was just wondering if this was an easy fix for you guys, or if I need to use another language than Rust for this project.
Thanks!
Alexis
This issue targets the as_eq
filter.
When a as_eq
filter is created with a string value, but the bin does not have a string type Secondary Index, it can fail memory allocation. It does not fail, if the filter is any other than as_eq
or the bin has an Index. Also integer values are safe. Even with perfect conditions for this error, it seems not to fail everytime.
This is a hard to reproduce bug. For more about it, see this comment
// Update
I just found out, that also Numeric Queries on a String type Index bin can cause this to happen. Seems to be even more reproduceable.
I'm running macOS 10.14.6.
Reproduction Steps:
git clone https://github.com/aerospike/aerospike-client-rust.git \
&& docker run -v ${HOME}/.aerospike/data:/opt/aerospike/data -e "NAMESPACE=test" --rm -d -p 3000:3000 --name aerospike aerospike/aerospike-server \
&& cd aerospike-client-rust \
&& cargo test
Results:
running 20 tests
test bin::tests::into_bins ... ok
test net::parser::tests::read_addr_part ... ok
test net::host::tests::to_hosts ... ok
test key::tests::unsupported_float_key ... ok
test key::tests::unsupported_u64_key ... ok
test net::parser::tests::read_addr_tuple ... ok
test query::filter::tests::geo_filter_macros ... ok
test net::parser::tests::read_hosts ... ok
test record::tests::ttl_expiration_future ... ok
test record::tests::ttl_expiration_past ... ok
test record::tests::ttl_never_expires ... ok
test result_code::tests::from_result_code ... ok
test result_code::tests::from_unknown_result_code ... ok
test result_code::tests::into_string ... ok
test result_code::tests::unknown_into_string ... ok
test value::tests::as_geo ... ok
test value::tests::as_string ... ok
test key::tests::int_keys ... ok
test key::tests::string_keys ... ok
test key::tests::blob_keys ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/debug/deps/client-0cb4beb2f607c922
running 5 tests
test cluster_name ... ok
test nodes ... ok
test get_node ... ok
test node_names ... ok
test close ... ok
test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/debug/deps/lib-33687cbfd16d35e7
running 27 tests
test src::batch::batch_get ... ok
test src::cdt_bitwise::cdt_bitwise ... ok
test src::cdt_list::cdt_list ... ok
test src::cdt_map::map_operations ... ok
test src::exp::expression_rec_ops ... FAILED
test src::exp::expression_commands ... ok
test src::hll::hll ... ok
test src::index::recreate_index ... ok
test src::kv::connect ... ok
test src::exp::expression_condition ... ok
test src::query::query_multi_consumer ... ok
test src::query::query_nobins ... ok
test src::exp::expression_data_types ... ok
test src::exp::expression_compare ... ok
test src::query::query_node ... ok
test src::query::query_single_consumer ... ok
test src::exp_hll::expression_hll ... ok
test src::task::index_task_test ... ok
test src::truncate::truncate ... ok
test src::scan::scan_multi_consumer ... ok
test src::udf::execute_udf ... ok
test src::scan::scan_single_consumer ... ok
test src::scan::scan_node ... ok
test src::task::register_task_test ... ok
test src::exp_bitwise::expression_bitwise ... ok
test src::exp_list::expression_list ... ok
test src::exp_map::expression_map ... ok
failures:
---- src::exp::expression_rec_ops stdout ----
thread 'src::exp::expression_rec_ops' panicked at 'assertion failed: `(left == right)`
left: `0`,
right: `100`: DEVICE SIZE Test Failed', tests/src/exp.rs:244:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Running cargo clippy:
warning: this could be a `const fn`
--> src/value.rs:221:5
|
221 | / pub fn is_nil(&self) -> bool {
222 | | match *self {
223 | | Value::Nil => true,
224 | | _ => false,
225 | | }
226 | | }
| |_____^
|
note: the lint level is defined here
--> src/lib.rs:25:40
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^^^^^
= note: `#[warn(clippy::missing_const_for_fn)]` implied by `#[warn(clippy::nursery)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: match expression looks like `matches!` macro
--> src/value.rs:222:9
|
222 | / match *self {
223 | | Value::Nil => true,
224 | | _ => false,
225 | | }
| |_________^ help: try this: `matches!(*self, Value::Nil)`
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::match_like_matches_macro)]` implied by `#[warn(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: this could be a `const fn`
--> src/bin.rs:70:5
|
70 | / pub fn is_all(&self) -> bool {
71 | | match *self {
72 | | Bins::All => true,
73 | | _ => false,
74 | | }
75 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: match expression looks like `matches!` macro
--> src/bin.rs:71:9
|
71 | / match *self {
72 | | Bins::All => true,
73 | | _ => false,
74 | | }
| |_________^ help: try this: `matches!(*self, Bins::All)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: this could be a `const fn`
--> src/bin.rs:78:5
|
78 | / pub fn is_none(&self) -> bool {
79 | | match *self {
80 | | Bins::None => true,
81 | | _ => false,
82 | | }
83 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: match expression looks like `matches!` macro
--> src/bin.rs:79:9
|
79 | / match *self {
80 | | Bins::None => true,
81 | | _ => false,
82 | | }
| |_________^ help: try this: `matches!(*self, Bins::None)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: redundant closure found
--> src/batch/batch_executor.rs:102:44
|
102 | map.entry(node).or_insert_with(|| vec![]).push(idx);
| ^^^^^^^^^ help: remove closure as shown: `$crate::vec::Vec::new`
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::redundant_closure)]` implied by `#[warn(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
warning: this could be a `const fn`
--> src/cluster/node.rs:139:5
|
139 | / fn services_name(&self) -> &'static str {
140 | | if self.client_policy.use_services_alternate {
141 | | "services-alternate"
142 | | } else {
143 | | "services"
144 | | }
145 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: comparison to empty slice
--> src/cluster/node.rs:196:36
|
196 | Some(friend_string) if friend_string == "" => return Ok(friends),
| ^^^^^^^^^^^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `friend_string.is_empty()`
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::comparison_to_empty)]` implied by `#[warn(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
--> src/cluster/node.rs:290:9
|
290 | / Message::info(&mut conn, commands).or_else(|e| {
291 | | conn.invalidate();
292 | | Err(e)
293 | | })
| |__________^
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::bind_instead_of_map)]` implied by `#[warn(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
help: try this
|
290 | Message::info(&mut conn, commands).map_err(|e| {
291 | conn.invalidate();
292 | e
|
warning: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
--> src/cluster/mod.rs:249:22
|
249 | let tokens = PartitionTokenizer::new(&mut conn).or_else(|e| {
| ______________________^
250 | | conn.invalidate();
251 | | Err(e)
252 | | })?;
| |__________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
help: try this
|
249 | let tokens = PartitionTokenizer::new(&mut conn).map_err(|e| {
250 | conn.invalidate();
251 | e
|
warning: wildcard match will miss any future added variants
--> src/cluster/mod.rs:324:17
|
324 | _ => {
| ^ help: try this: `std::prelude::v1::Err(..)`
|
note: the lint level is defined here
--> src/lib.rs:25:22
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::match_wildcard_for_single_variants)]` implied by `#[warn(clippy::pedantic)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
warning: wildcard match will miss any future added variants
--> src/commands/batch_read_command.rs:93:17
|
93 | _ => continue, // Node is currently inactive. Retry.
| ^ help: try this: `std::prelude::v1::Err(..)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
warning: match expression looks like `matches!` macro
--> src/commands/buffer.rs:511:27
|
511 | let each_op = match operation.data {
| ___________________________^
512 | | OperationData::CdtMapOp(_) | OperationData::CdtBitOp(_) => true,
513 | | _ => false,
514 | | };
| |_____________^ help: try this: `matches!(operation.data, OperationData::CdtMapOp(_) | OperationData::CdtBitOp(_))`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: comparison to empty slice
--> src/commands/buffer.rs:606:12
|
606 | if namespace != "" {
| ^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:611:12
|
611 | if set_name != "" {
| ^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:653:12
|
653 | if namespace != "" {
| ^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:657:12
|
657 | if set_name != "" {
| ^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:712:12
|
712 | if statement.namespace != "" {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!statement.namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:717:12
|
717 | if statement.set_name != "" {
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!statement.set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:723:16
|
723 | if index_name != "" {
| ^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!index_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:810:12
|
810 | if statement.namespace != "" {
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!statement.namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:820:12
|
820 | if statement.set_name != "" {
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!statement.set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:905:12
|
905 | if key.namespace != "" {
| ^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!key.namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:910:12
|
910 | if key.set_name != "" {
| ^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!key.set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:1073:12
|
1073 | if key.namespace != "" {
| ^^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!key.namespace.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: comparison to empty slice
--> src/commands/buffer.rs:1077:12
|
1077 | if key.set_name != "" {
| ^^^^^^^^^^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!key.set_name.is_empty()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1196:9
|
1196 | / if let Some(pos) = pos {
1197 | | Ok(self.data_buffer[pos])
1198 | | } else {
1199 | | let res = self.data_buffer[self.data_offset];
1200 | | self.data_offset += 1;
1201 | | Ok(res)
1202 | | }
| |_________^
|
note: the lint level is defined here
--> src/lib.rs:25:22
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::option_if_let_else)]` implied by `#[warn(clippy::pedantic)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1196 | pos.map_or_else(|| {
1197 | let res = self.data_buffer[self.data_offset];
1198 | self.data_offset += 1;
1199 | Ok(res)
1200 | }, |pos| Ok(self.data_buffer[pos]))
|
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1206:9
|
1206 | / if let Some(pos) = pos {
1207 | | Ok(self.data_buffer[pos] as i8)
1208 | | } else {
1209 | | let res = self.data_buffer[self.data_offset] as i8;
1210 | | self.data_offset += 1;
1211 | | Ok(res)
1212 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1206 | pos.map_or_else(|| {
1207 | let res = self.data_buffer[self.data_offset] as i8;
1208 | self.data_offset += 1;
1209 | Ok(res)
1210 | }, |pos| Ok(self.data_buffer[pos] as i8))
|
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1217:9
|
1217 | / if let Some(pos) = pos {
1218 | | Ok(NetworkEndian::read_u16(&self.data_buffer[pos..pos + len]))
1219 | | } else {
1220 | | let res = NetworkEndian::read_u16(
... |
1224 | | Ok(res)
1225 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1217 | pos.map_or_else(|| {
1218 | let res = NetworkEndian::read_u16(
1219 | &self.data_buffer[self.data_offset..self.data_offset + len],
1220 | );
1221 | self.data_offset += len;
1222 | Ok(res)
...
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1235:9
|
1235 | / if let Some(pos) = pos {
1236 | | Ok(NetworkEndian::read_u32(&self.data_buffer[pos..pos + len]))
1237 | | } else {
1238 | | let res = NetworkEndian::read_u32(
... |
1242 | | Ok(res)
1243 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1235 | pos.map_or_else(|| {
1236 | let res = NetworkEndian::read_u32(
1237 | &self.data_buffer[self.data_offset..self.data_offset + len],
1238 | );
1239 | self.data_offset += len;
1240 | Ok(res)
...
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1253:9
|
1253 | / if let Some(pos) = pos {
1254 | | Ok(NetworkEndian::read_u64(&self.data_buffer[pos..pos + len]))
1255 | | } else {
1256 | | let res = NetworkEndian::read_u64(
... |
1260 | | Ok(res)
1261 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1253 | pos.map_or_else(|| {
1254 | let res = NetworkEndian::read_u64(
1255 | &self.data_buffer[self.data_offset..self.data_offset + len],
1256 | );
1257 | self.data_offset += len;
1258 | Ok(res)
...
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1277:9
|
1277 | / if let Some(pos) = pos {
1278 | | Ok(NetworkEndian::read_f32(&self.data_buffer[pos..pos + len]))
1279 | | } else {
1280 | | let res = NetworkEndian::read_f32(
... |
1284 | | Ok(res)
1285 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1277 | pos.map_or_else(|| {
1278 | let res = NetworkEndian::read_f32(
1279 | &self.data_buffer[self.data_offset..self.data_offset + len],
1280 | );
1281 | self.data_offset += len;
1282 | Ok(res)
...
warning: use Option::map_or_else instead of an if let/else
--> src/commands/buffer.rs:1290:9
|
1290 | / if let Some(pos) = pos {
1291 | | Ok(NetworkEndian::read_f64(&self.data_buffer[pos..pos + len]))
1292 | | } else {
1293 | | let res = NetworkEndian::read_f64(
... |
1297 | | Ok(res)
1298 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
1290 | pos.map_or_else(|| {
1291 | let res = NetworkEndian::read_f64(
1292 | &self.data_buffer[self.data_offset..self.data_offset + len],
1293 | );
1294 | self.data_offset += len;
1295 | Ok(res)
...
warning: `self.write_u8(0)?` is being shadowed
--> src/commands/buffer.rs:1407:9
|
1407 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> src/lib.rs:25:22
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::shadow_unrelated)]` implied by `#[warn(clippy::pedantic)]`
note: initialization happens here
--> src/commands/buffer.rs:1407:9
|
1407 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/commands/buffer.rs:1406:33
|
1406 | pub fn write_geo(&mut self, val: &str) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `self.write_u8(0)?` is being shadowed
--> src/commands/buffer.rs:1408:9
|
1408 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/commands/buffer.rs:1408:9
|
1408 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/commands/buffer.rs:1407:9
|
1407 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `self.write_u8(0)?` is being shadowed
--> src/commands/buffer.rs:1409:9
|
1409 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/commands/buffer.rs:1409:9
|
1409 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/commands/buffer.rs:1408:9
|
1408 | self.write_u8(0)?;
| ^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: wildcard match will miss any future added variants
--> src/commands/single_command.rs:103:17
|
103 | _ => continue, // Node is currently inactive. Retry.
| ^ help: try this: `std::prelude::v1::Err(..)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
warning: this could be a `const fn`
--> src/commands/mod.rs:66:1
|
66 | / pub fn keep_connection(err: &Error) -> bool {
67 | | match *err {
68 | | Error(ErrorKind::ServerError(result_code), _) => match result_code {
69 | | ResultCode::KeyNotFoundError => true,
... |
73 | | }
74 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: match expression looks like `matches!` macro
--> src/commands/mod.rs:68:58
|
68 | Error(ErrorKind::ServerError(result_code), _) => match result_code {
| __________________________________________________________^
69 | | ResultCode::KeyNotFoundError => true,
70 | | _ => false,
71 | | },
| |_________^ help: try this: `matches!(result_code, ResultCode::KeyNotFoundError)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: this could be a `const fn`
--> src/expressions/lists.rs:690:1
|
690 | / fn get_value_type(return_type: ListReturnType) -> ExpType {
691 | | if (return_type as u8 & !(ListReturnType::Inverted as u8)) == ListReturnType::Values as u8 {
692 | | ExpType::LIST
693 | | } else {
694 | | ExpType::INT
695 | | }
696 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/expressions/maps.rs:833:1
|
833 | / fn get_value_type(return_type: MapReturnType) -> ExpType {
834 | | let t = return_type as u8 & !(MapReturnType::Inverted as u8);
835 | | if t == MapReturnType::Key as u8 || t == MapReturnType::Value as u8 {
836 | | ExpType::LIST
... |
841 | | }
842 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: use Option::map_or instead of an if let/else
--> src/expressions/mod.rs:129:9
|
129 | / if let Some(bin) = bin {
130 | | FilterExpression {
131 | | cmd,
132 | | val,
... |
148 | | }
149 | | }
| |_________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
help: try
|
129 | bin.map_or(FilterExpression {
130 | cmd,
131 | val,
132 | bin: None,
133 | flags,
134 | module,
...
warning: parameter of type `HashMap` should be generalized over different hashers
--> src/expressions/mod.rs:666:21
|
666 | pub fn map_val(val: HashMap<Value, Value>) -> FilterExpression {
| ^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> src/lib.rs:25:22
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^^^^^^
= note: `#[warn(clippy::implicit_hasher)]` implied by `#[warn(clippy::pedantic)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
help: consider adding a type parameter
|
666 | pub fn map_val<S: ::std::hash::BuildHasher>(val: HashMap<Value, Value, S>) -> FilterExpression {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^
warning: this could be a `const fn`
--> src/expressions/mod.rs:703:1
|
703 | / pub fn and(exps: Vec<FilterExpression>) -> FilterExpression {
704 | | FilterExpression {
705 | | cmd: Some(ExpOp::And),
706 | | val: None,
... |
712 | | }
713 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/expressions/mod.rs:721:1
|
721 | / pub fn or(exps: Vec<FilterExpression>) -> FilterExpression {
722 | | FilterExpression {
723 | | cmd: Some(ExpOp::Or),
724 | | val: None,
... |
730 | | }
731 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/msgpack/decoder.rs:222:1
|
222 | / fn is_ext(byte: u8) -> bool {
223 | | match byte {
224 | | 0xc7 | 0xc8 | 0xc9 | 0xd4 | 0xd5 | 0xd6 | 0xd7 | 0xd8 => true,
225 | | _ => false,
226 | | }
227 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: match expression looks like `matches!` macro
--> src/msgpack/decoder.rs:223:5
|
223 | / match byte {
224 | | 0xc7 | 0xc8 | 0xc9 | 0xd4 | 0xd5 | 0xd6 | 0xd7 | 0xd8 => true,
225 | | _ => false,
226 | | }
| |_____^ help: try this: `matches!(byte, 0xc7 | 0xc8 | 0xc9 | 0xd4 | 0xd5 | 0xd6 | 0xd7 | 0xd8)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
warning: `buf.write_u8(marker)?` is being shadowed
--> src/msgpack/encoder.rs:227:9
|
227 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:227:9
|
227 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:225:61
|
225 | pub fn pack_byte(buf: &mut Option<&mut Buffer>, marker: u8, val: u8) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(MSGPACK_MARKER_BOOL_TRUE)?` is being shadowed
--> src/msgpack/encoder.rs:245:13
|
245 | buf.write_u8(MSGPACK_MARKER_BOOL_TRUE)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:245:13
|
245 | buf.write_u8(MSGPACK_MARKER_BOOL_TRUE)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:242:49
|
242 | pub fn pack_bool(buf: &mut Option<&mut Buffer>, val: bool) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(MSGPACK_MARKER_BOOL_FALSE)?` is being shadowed
--> src/msgpack/encoder.rs:247:13
|
247 | buf.write_u8(MSGPACK_MARKER_BOOL_FALSE)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:247:13
|
247 | buf.write_u8(MSGPACK_MARKER_BOOL_FALSE)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:245:13
|
245 | buf.write_u8(MSGPACK_MARKER_BOOL_TRUE)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: manual `Range::contains` implementation
--> src/msgpack/encoder.rs:257:16
|
257 | val if val >= 16 && val < 2 ^ 16 => pack_i16(buf, 0xde, length as i16),
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(16..2 ^ 16).contains(&val)`
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::manual_range_contains)]` implied by `#[warn(clippy::all)]`
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
warning: manual `Range::contains` implementation
--> src/msgpack/encoder.rs:266:16
|
266 | val if val >= 16 && val < 2 ^ 16 => pack_i16(buf, 0xdc, length as i16),
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(16..2 ^ 16).contains(&val)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
warning: `buf.write_u8(ParticleType::BLOB as u8)?` is being shadowed
--> src/msgpack/encoder.rs:286:9
|
286 | buf.write_u8(ParticleType::BLOB as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:286:9
|
286 | buf.write_u8(ParticleType::BLOB as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:281:49
|
281 | pub fn pack_blob(buf: &mut Option<&mut Buffer>, val: &[u8]) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(ParticleType::STRING as u8)?` is being shadowed
--> src/msgpack/encoder.rs:299:9
|
299 | buf.write_u8(ParticleType::STRING as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:299:9
|
299 | buf.write_u8(ParticleType::STRING as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:294:51
|
294 | pub fn pack_string(buf: &mut Option<&mut Buffer>, val: &str) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(ParticleType::GEOJSON as u8)?` is being shadowed
--> src/msgpack/encoder.rs:324:9
|
324 | buf.write_u8(ParticleType::GEOJSON as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:324:9
|
324 | buf.write_u8(ParticleType::GEOJSON as u8)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:319:49
|
319 | fn pack_geo_json(buf: &mut Option<&mut Buffer>, val: &str) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: manual `Range::contains` implementation
--> src/msgpack/encoder.rs:334:16
|
334 | val if val >= 0 && val < 2 ^ 7 => pack_half_byte(buf, val as u8),
| ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..2 ^ 7).contains(&val)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
warning: `buf.write_u8(marker)?` is being shadowed
--> src/msgpack/encoder.rs:367:9
|
367 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:367:9
|
367 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:365:60
|
365 | pub fn pack_i16(buf: &mut Option<&mut Buffer>, marker: u8, val: i16) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(marker)?` is being shadowed
--> src/msgpack/encoder.rs:376:9
|
376 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:376:9
|
376 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:374:60
|
374 | pub fn pack_i32(buf: &mut Option<&mut Buffer>, marker: u8, val: i32) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(marker)?` is being shadowed
--> src/msgpack/encoder.rs:385:9
|
385 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:385:9
|
385 | buf.write_u8(marker)?;
| ^^^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:383:60
|
383 | pub fn pack_i64(buf: &mut Option<&mut Buffer>, marker: u8, val: i64) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(0xcf)?` is being shadowed
--> src/msgpack/encoder.rs:398:9
|
398 | buf.write_u8(0xcf)?;
| ^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:398:9
|
398 | buf.write_u8(0xcf)?;
| ^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:392:48
|
392 | pub fn pack_u64(buf: &mut Option<&mut Buffer>, val: u64) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(0xca)?` is being shadowed
--> src/msgpack/encoder.rs:407:9
|
407 | buf.write_u8(0xca)?;
| ^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:407:9
|
407 | buf.write_u8(0xca)?;
| ^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:405:48
|
405 | pub fn pack_f32(buf: &mut Option<&mut Buffer>, val: f32) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: `buf.write_u8(0xcb)?` is being shadowed
--> src/msgpack/encoder.rs:416:9
|
416 | buf.write_u8(0xcb)?;
| ^^^^^^^^^^^^^^^^^^^
|
note: initialization happens here
--> src/msgpack/encoder.rs:416:9
|
416 | buf.write_u8(0xcb)?;
| ^^^^^^^^^^^^^^^^^^^
note: previous binding is here
--> src/msgpack/encoder.rs:414:48
|
414 | pub fn pack_f64(buf: &mut Option<&mut Buffer>, val: f64) -> Result<usize> {
| ^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
warning: use Option::map_or instead of an if let/else
--> src/net/connection.rs:100:9
|
100 | / if let Some(idle_dl) = self.idle_deadline {
101 | | Instant::now() >= idle_dl
102 | | } else {
103 | | false
104 | | }
| |_________^ help: try: `self.idle_deadline.map_or(false, |idle_dl| Instant::now() >= idle_dl)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#option_if_let_else
warning: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as `map_err(|x| y)`
--> src/net/connection_pool.rs:86:9
|
86 | / connection.set_timeout(timeout).or_else(|err| {
87 | | internals.num_conns -= 1;
88 | | Err(err)
89 | | })?;
| |__________^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#bind_instead_of_map
help: try this
|
86 | connection.set_timeout(timeout).map_err(|err| {
87 | internals.num_conns -= 1;
88 | err
|
warning: this could be a `const fn`
--> src/operations/lists.rs:177:1
|
177 | / pub fn list_order_flag(order: ListOrderType, pad: bool) -> u8 {
178 | | if let ListOrderType::Ordered = order {
179 | | return 0xc0;
180 | | }
... |
184 | | 0x40
185 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/maps.rs:195:1
|
195 | / pub(crate) fn map_write_op(policy: &MapPolicy, multi: bool) -> CdtMapOpType {
196 | | match policy.write_mode {
197 | | MapWriteMode::Update => {
198 | | if multi {
... |
218 | | }
219 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/maps.rs:221:1
|
221 | / fn map_order_arg(policy: &MapPolicy) -> Option<CdtArgument> {
222 | | match policy.write_mode {
223 | | MapWriteMode::UpdateOnly => None,
224 | | _ => Some(CdtArgument::Byte(policy.order as u8)),
225 | | }
226 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/scalar.rs:43:1
|
43 | / pub fn get_bin(bin_name: &str) -> Operation {
44 | | Operation {
45 | | op: OperationType::Read,
46 | | ctx: DEFAULT_CTX,
... |
49 | | }
50 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/scalar.rs:53:1
|
53 | / pub fn put<'a>(bin: &'a Bin) -> Operation<'a> {
54 | | Operation {
55 | | op: OperationType::Write,
56 | | ctx: DEFAULT_CTX,
... |
59 | | }
60 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/scalar.rs:63:1
|
63 | / pub fn append<'a>(bin: &'a Bin) -> Operation<'a> {
64 | | Operation {
65 | | op: OperationType::Append,
66 | | ctx: DEFAULT_CTX,
... |
69 | | }
70 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/scalar.rs:73:1
|
73 | / pub fn prepend<'a>(bin: &'a Bin) -> Operation<'a> {
74 | | Operation {
75 | | op: OperationType::Prepend,
76 | | ctx: DEFAULT_CTX,
... |
79 | | }
80 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: this could be a `const fn`
--> src/operations/scalar.rs:83:1
|
83 | / pub fn add<'a>(bin: &'a Bin) -> Operation<'a> {
84 | | Operation {
85 | | op: OperationType::Incr,
86 | | ctx: DEFAULT_CTX,
... |
89 | | }
90 | | }
| |_^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: field assignment outside of initializer for an instance created with Default::default()
--> src/policy/write_policy.rs:75:9
|
75 | wp.generation = gen;
| ^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> src/lib.rs:25:9
|
25 | #![warn(clippy::all, clippy::pedantic, clippy::nursery)]
| ^^^^^^^^^^^
= note: `#[warn(clippy::field_reassign_with_default)]` implied by `#[warn(clippy::all)]`
note: consider initializing the variable with `policy::write_policy::WritePolicy { generation: gen, expiration: exp, ..Default::default() }` and removing relevant reassignments
--> src/policy/write_policy.rs:74:9
|
74 | let mut wp = WritePolicy::default();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#field_reassign_with_default
warning: wildcard match will miss any future added variants
--> src/record.rs:77:21
|
77 | _ => Some(Duration::new(1u64, 0)),
| ^ help: try this: `std::prelude::v1::Err(..)`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#match_wildcard_for_single_variants
warning: this could be a `const fn`
--> src/result_code.rs:216:5
|
216 | / pub fn from_u8(n: u8) -> ResultCode {
217 | | match n {
218 | | 0 => ResultCode::Ok,
219 | | 1 => ResultCode::ServerError,
... |
281 | | }
282 | | }
| |_____^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
warning: 76 warnings emitted
Use new peers list protocol introduced in AS v3.10 for more efficient node discovery.
Note: Requires Aerospike Server version 3.10 or later.
Certain server tasks are initiated by the client but are asynchronously executed by the server in the background. The client should provide APIs to monitor these tasks using the info protocol and notify the application when they are completed.
Support for Transport Layer Security for all communication between the client and all cluster nodes. Support for all 3 TLS security modes:
Note: Requires Aerospike Enterprise Server Version 3.11 or later.
I have structured data and I'd like a convenient mechansim for keeping that structure while pushing and pulling to and from aerospike. It looks like there may have been some initial work done here, but I'd like to push that bar further.
I'm proposing a trait, AerospikeRecord, that can be applied to an arbitrary Rust structure provided it meets certain criteria. The API would look something like this:
let client = build_aerospike_client(...); // not included in API...
let record = SomeRecord {...}; // not included in API...
// Check if the record is in aerospike
if exists_record(&record, &client)? != true {
// Create the record in aerospike
set_record(&value, &client)?;
// Retrieve the record from aerospike
let aerospike_copy: SomeRecord = get_record(&record, &client)?;
// Update the record in aerospike
let updated_record = SomeRecord { ... };
set_record(&updated_record, &client)?;
// Delete the record from aerospike
remove_record(&updated_record, &client)?;
}
The trait would have a variety of different methods to support the API above
pub trait AerospikeRecord: ... {
/// Builds a stringified version of the record's key
fn aerospike_record_key(&self) -> String;
/// Builds aerospike bin name for this record
/// default: "payload"
fn aerospike_bin_name(&self) -> String;
/// Returns the timeout in seconds
/// A timeout of 0 indicates that the value will never be culled
/// Default: whatever AEROSPIKE_TIMEOUT has (or 0)
fn aerospike_timeout(&self) -> u32;
/// Returns the namespace
/// Default: whatever AEROSPIKE_NAMESPACE has (or "undefined")
fn aerospike_namespace() -> String;
/// Returns the set name
/// Default: whatever AEROSPIKE_SET_NAME has (or "undefined")
fn aerospike_set_name() -> String;
/// Builds record data for aerospike entry
/// Default: uses data from above
fn aerospike_as_bins(&self) -> Vec<Bin>;
/// Builds an aerospike key from record
/// Default: uses data from above
fn aerospike_key(&self) -> Key;
/// Builds an aerospike key using parameters
fn aerospike_build_key(&self, namespace: &str, set_name: &str) -> Key;
/// Build dyn AerospikeRecord from aerospike::Record
fn aerospike_from_record(&self, aerospike_record: Record) -> Self;
}
For a default behavior, the only thing that would need to be implemented for a given structure would be the aerospike_record_key
. I think this makes sense because the key could be any kind of value within a given record. It might even need to be a composite key made of several values or a calculated set of values. But this allows the maintainer to decide how to build that key explicitly.
struct Foo {
bar: String
}
impl AerospikeRecord for Foo {
fn aerospike_record_key(&self) -> String {
self.bar.clone()
}
Any of the other traits can easily be overwritten:
impl AerospikeRecord for Foo {
...
fn aerospike_namespace() -> String {
let default = String::from("real-time");
let fall_through = std::env::var("AEROSPIKE_NAMESPACE").unwrap_or(default);
std::env::var("FOO_AEROSPIKE_NAMESPACE").unwrap_or(fall_through)
}
}
One caveat is that I'd need to make changes to the Bin lifetime. Right now, the way that it's implemented, it's impossible to do this:
impl Into<Vec<Bin>> for TestRecord {
fn into(self) -> Vec<Bin> {
let json_string = serde_json::to_string(&self).unwrap_or_else(|_| String::default());
vec![as_bin!("key", self.aerospike_record_key()), as_bin!("payload", json_string)]
}
}
Bin<'a> requires that the control of the lifetime be outside of its use. As a result, you'd need to carry that lifetime all the way up to the top of the call stack or set it to Bin<'static> and remain for the lifetime of the program. It's hard to know why the lifetimes are there in the first place as they only seem to make the API more complicated than is necessary. As a more general statement, I suspect some of the other lifetime requirements could also be removed but I don't have a use case for them right now. And, I wonder if this may be why the API doesn't get as much usage.
Rust 1.26.2 was the minimum supported Rust version (MSRV), but now cargo build
fails to build using that version:
$ cargo build
Downloading lazy_static v1.4.0
Downloading log v0.4.8
Downloading rand v0.5.6
Downloading error-chain v0.12.1
Downloading byteorder v1.3.2
Downloading rand v0.3.23
Downloading time v0.1.42
Downloading libc v0.2.65
Downloading rand v0.4.6
Downloading gcc v0.3.55
Downloading cfg-if v0.1.10
error: unable to get packages from source
Caused by:
failed to parse manifest at `/Users/jhecking/.cargo/registry/src/github.com-1ecc6299db9ec823/cfg-if-0.1.10/Cargo.toml`
Caused by:
feature `rename-dependency` is required
this Cargo does not support nightly features, but if you
switch to nightly channel you can add
`cargo-features = ["rename-dependency"]` to enable this feature
$ rustc --version
rustc 1.26.2 (594fb253c 2018-06-01)
It would be great to be able to write native modules similar to how this can be done with Redis. Is that on the road map?
Running the unit tests results in two failures:
---- src::cdt_map::map_operations stdout ----
thread 'src::cdt_map::map_operations' panicked at 'called `Result::unwrap()` on an `Err` value: Error(ServerError(ParameterError), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
---- src::cdt_list::cdt_list stdout ----
thread 'src::cdt_list::cdt_list' panicked at 'called `Result::unwrap()` on an `Err` value: Error(ServerError(ParameterError), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })', libcore/result.rs:945:5
Server logs show the following warning:
Nov 18 2019 01:54:03 GMT: WARNING (rw): (write.c:1079) {test} write_master: has read op but read flag not set <Digest>:0xbd7a1b71a449d964b52298514b042b9416040ae5
The Client::batch_get function seems to make a lot memory leaks. After thousands call the process memory grow up to 10 GB. The same requests with simply Client::get looks good. The target is x86_64-unknown-linux-gnu
When fetching a CDT bin with an ordered list/map from the server, the client panics with an error message "Msgpack header skipped. You should not see this message".
Note: It's not currently possible to create an ordered list/map using only the Rust client. Therefore, this error can only occur when other clients (C, Java, etc.) are used to write ordered lists/maps to an Aerospike cluster and this data is subsequently read using the Rust client.
here's a repro following the example from the docs here
#[test]
fn test() {
use aerospike::{self, operations::{maps, cdt_context}};
let ac = aerospike::Client::new(&aerospike::ClientPolicy::default(), &"0.0.0.0:3000".to_string()).unwrap();
let key = as_key!("test", "test", "test");
ac.put(
&aerospike::WritePolicy::default(),
&key,
&[as_bin!("m", as_map!("name" => "chuck norris"))]
);
ac.operate(
&aerospike::WritePolicy::default(),
&key,
&[
maps::increment_value(
&maps::MapPolicy::default(),
"m",
&as_val!("jokes"),
&as_val!(317)
).set_context(&[
cdt_context::ctx_map_key_create(as_val!("stats"), maps::MapOrder::KeyOrdered),
cdt_context::ctx_map_key_create(as_val!("accolades"), maps::MapOrder::KeyOrdered),
])
]
);
assert_eq!(
"{}",
ac.get(
&aerospike::ReadPolicy::default(),
&key,
aerospike::Bins::Some(vec!["m".to_string()])
).unwrap().bins.get("m").unwrap().to_string()
)
}
i did the assert_eq!
as a convenience to see what's inside bin "m"
and i get
thread 'tests::test' panicked at 'assertion failed: `(left == right)`
left: `"{}"`,
right: `"{String(\"name\"): String(\"chuck norris\")}"`' ...
when i add a .unwrap()
to the ac.operate(...)
above it turns out it's actually erroring with thread 'tests::test' panicked at 'called `Result::unwrap()` on an `Err` value: Error(ServerError(Unknown(26)), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })'
from here, 26
means AS_ERR_OP_NOT_APPLICABLE Operation isn't able to be applied to the current contents of the bin.
which seems weird to me because this works with the python client. am i missing something?
Reading a bin of particle type GeoJSON returns a Value::String
value. It should return a Value::GeoJSON
value.
First of all, thanks for amazing work done with this library.
I came across an optional FilterExpression in QueryPolicy
,
So I was wondering about few things:
Thanks in advance, cheers!
Note: Requires Aerospike Server version 3.10 or later.
For clusters with more than 1 defined namespace, the client fails to parse the partition maps with an error message like
ERROR:aerospike::cluster: Error: Invalid character '59' at position 684
The Client should feature HLL Operations and Expressions for a 1.0 Release.
The PR for HLL is already finished. It just misses the Tests (same as the FilterExpressions PR).
I will merge request this as soon as the other one is merged.
By the way, the Documentation of HyperLogLog seems broken: https://www.aerospike.com/docs/guides/hyperloglog
It only shows 404 and the Links to the HyperLogLog Page also dont work.
Please assign me.
During cluster tend, when new nodes are found through the services
lookup and added to the cluster, they get removed again immediately because their reference count doesn't get updated.
The rustc-serialize
crate has been officially deprecated: https://users.rust-lang.org/t/deprecation-of-rustc-serialize/10485. While not critical or urgent it would probably be a good idea to remove the dependency on that crate at some point. We are only using it for the included Base64 module.
Possible alternatives:
https://crates.io/crates/base64
In tests/src/task.rs1
#[macro_export]
macro_rules! matches_override {
($expression:expr, $($pattern:tt)+) => {
match $expression {
$($pattern)+ => true,
_ => false
}
}
}
Should be replaced with default match
macro https://doc.rust-lang.org/beta/std/macro.matches.html
I'm working on an Elixir nif using Rust and rustler to access an Aerospike server from Elixir. I'm new to Rust and had an issue trying to implement a wrapper for put and needed some help. You can see the full discussion on the rust-lang forum here: https://users.rust-lang.org/t/newbie-ownership-issue/17079
My issue was trying to translate an Elixir Map into a &[&Bin] to pass to put. I needed to generate a [Bin]
and then do:
let bins = bins.iter().collect::<Vec<_>>();
to generate the [&Bin]
They made the suggestion:
fn put<A: AsRef<Bin>>(..., bins: &[A]) ...
// or if a slice isn’t necessary and iterator works
fn put<I: IntoIterator<Item=A>, A: AsRef<Bin>>(iter: I) ...
Keeping in mind that I'm a Rust newbie, I thought I'd mention it here.
I would like to start a discussion about making this async.
@khaf already mentioned that this is planned anyways.
Before i will start to test around, i would like to get a two questions cleared.
If you have any additional ideas or feedback on that, please share it!
1: Project layout
There are three ways to look at that. First one would be splitting in two crates (one repo with sub crates), 1 sync and 1 async. Would result in maintaining 2 codebases (some parts could be shared) but would probably be cleaner and with less overhead. Way two would be building async native and implementing sync via manual blocking via feature flag. Third one would be dropping the sync support. Either way, this will be a breaking change because it will require users to use a runtime.
2: Tokio or async-std? Tokio is more mature and way more adopted in the ecosystem. Based on that, i would decide for Tokio.
hi
am trying with a simple code to check the basics of Rust client, but the client.close()
is not working.
#[macro_use]
extern crate aerospike;
use std::env;
use std::sync::Arc;
use std::time::Instant;
use std::thread;
use aerospike::Client;
use aerospike::{Bins, ClientPolicy, ReadPolicy, WritePolicy};
use aerospike::operations;
fn main() {
let mut cpolicy = ClientPolicy::default();
let user = env::var("AEROSPIKE_USER")
.unwrap_or(String::from("admin"));
let password = env::var("AEROSPIKE_PASSWORD")
.unwrap_or(String::from("admin"));
cpolicy.set_user_password(user, password).unwrap();
let hosts = env::var("AEROSPIKE_HOSTS")
.unwrap_or(String::from("127.0.0.1:3000"));
let client = Client::new(&cpolicy, &hosts)
.expect("Failed to connect to cluster");
let client = Arc::new(client);
let now = Instant::now();
let rpolicy = ReadPolicy::default();
let wpolicy = WritePolicy::default();
let key = as_key!("tempspace", "test", "test");
let bins = [
as_bin!("int", 999),
as_bin!("str", "Hello, World!"),
];
client.put(&wpolicy, &key, &bins).unwrap();
let rec = client.get(&rpolicy, &key, Bins::All);
println!("Record: {}", rec.unwrap());
client.touch(&wpolicy, &key).unwrap();
let rec = client.get(&rpolicy, &key, Bins::All);
println!("Record: {}", rec.unwrap());
let rec = client.get(&rpolicy, &key, Bins::None);
println!("Record Header: {}", rec.unwrap());
let exists = client.exists(&wpolicy, &key).unwrap();
println!("exists: {}", exists);
let bin = as_bin!("int", "123");
let ops = &vec![operations::put(&bin), operations::get()];
let op_rec = client.operate(&wpolicy, &key, ops);
println!("operate: {}", op_rec.unwrap());
let existed = client.delete(&wpolicy, &key).unwrap();
println!("existed (should be true): {}", existed);
let existed = client.delete(&wpolicy, &key).unwrap();
println!("existed (should be false): {}", existed);
client.close().expect("unable to close the connection");
if client.is_connected() {
println!("After close call-connected to AS servers");
} else {
println!("After close call-not connected");
}
println!("total time: {:?}", now.elapsed());
}
output observed is:
Finished dev [unoptimized + debuginfo] target(s) in 1.14s
Running `target/debug/hello-world`
Record: key: None, bins: {int: 999, str: Hello, World!}, generation: 1, ttl: none
Record: key: None, bins: {str: Hello, World!, int: 999}, generation: 2, ttl: none
Record Header: key: None, bins: {}, generation: 2, ttl: none
exists: true
operate: key: None, bins: {int: 123, str: Hello, World!}, generation: 3, ttl: none
existed (should be true): true
existed (should be false): false
After close call-connected to AS servers
total time: 2.902384ms
Hello,
I just ran into an issue with Node Name Validation.
My services use consul as service discovery for the Aerospike Hosts.
This results in using aerospike.service.consul:3000
as the hosts parameter.
I first did that with the nodejs client and everything worked well.
The Rust client aborts with the error
[2021-01-07T05:54:06Z ERROR aerospike::cluster] Failed to validate seed host: aerospike.service.consul:3000
[2021-01-07T05:54:06Z ERROR aerospike::cluster] Error: Invalid cluster node: Missing node name
The function validate_alias
in https://github.com/aerospike/aerospike-client-rust/blob/master/src/cluster/node_validator.rs#L61 throws that.
Is there any specific reason why the Rust client does that and the nodejs client not?
Is this error related to #10?
The client needs to provide an interface to get a list of the active cluster nodes.
For batch operations using batch index protocol, the server does not return the namespace/set name/user key in the batch results. The client should substitute the orig. user-provided record keys in the batch results.
Refs:
New nodes are not discovered if services-alternate is used.
rust-skeptic: "Test your Rust Markdown documentation via Cargo"
Support for MapReduce style of programming using Stream UDFs: http://www.aerospike.com/docs/guide/aggregation.html
Given the issues I had experienced with just the testing aspect, I am proposing here a Makefile shim which standardizes specific, common development operations including but not limited to:
The primary goal here is to limit the amount of ramp-up time anyone new would need to have in order to start contributing and simultaneously reduce the differences across development environments so that debugging differences becomes simple.
Additionally, I think the shim could also be used in CI though I'm not sure I have actually access to the Travis output (it looked like notifications were sent to a team specifically at Aerospike). Despite being sanctioned under the Aerospike umbrella, since this particular project community driven, it makes me wonder if Github's Actions are a better option. The only downside I see here is that a Makefile approach is probably limited to a Posix environment. As a consequence, it probably wouldn't work for Appveyor/Windows.
The Client should support the new Expression API as main target for the 1.0 release. Draft PR will follow soon.
There are several instances of this specific pattern, though I only find it in the one file.
See: https://github.com/aerospike/aerospike-client-rust/blob/master/src/msgpack/encoder.rs#L257
And a playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=b2ebb5a04b0a8464862963e413f57155
In this line, it kind of looks like 2 ^ 16
implies 2 raised to the power of 16. However, Rust will calculate this as a bitwise operation and will result in 18. So I think in this specific instance, if a value is less than 16, then we'll pack a half byte. If it's between 16 and 18, then we'll pack an i16. Otherwise, we'll pack an i32. And my assumption here is that we're nearly always packing an i32. This isn't a "breaking" bug, but it definitely would lead to inefficiencies for some use-cases and it should be really easy to build a test-case.
I looked at git blame, and I think this has been a bug for several years.
I am trying to cargo test
but reached error after setting up vagrant up
https://www.aerospike.com/docs/operations/install/vagrant/mac
I verified I can ssh
into vagrant
and insert element and that 127.0.0.1 3000
is able to be telnet
Error:
test node_names ... FAILED
test nodes ... FAILED
test get_node ... FAILED
failures:
---- node_names stdout ----
thread 'node_names' panicked at 'called `Result::unwrap()` on an `Err` value: Error(Connection("Failed to connect to host(s). The network connection(s) to cluster nodes may have timed out, or the cluster may be in a state of flux."), State { next_error: None, backtrace: InternalBacktrace { backtrace: None } })', tests/common/mod.rs:43:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- nodes stdout ----
thread 'nodes' panicked at 'Once instance has previously been poisoned', src/libstd/sync/once.rs:395:21
---- get_node stdout ----
thread 'get_node' panicked at 'Once instance has previously been poisoned', src/libstd/sync/once.rs:395:21
failures:
get_node
node_names
nodes
test result: FAILED. 1 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out
error: test failed, to rerun pass '--test client'
Can someone point me a direction for minimum steps to run cargo test
? Is there local cluster install instruction lying somewhere?
Under highly concurrent workloads the RwLock
guarding a Node
's connection pool becomes a bottle neck.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.