Skip to content

Commit c219c18

Browse files
authored
RUST-764 Use unsigned integers for values that should always be non-negative (#333)
1 parent 2675903 commit c219c18

File tree

21 files changed

+84
-71
lines changed

21 files changed

+84
-71
lines changed

src/bson_util/mod.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,14 +108,13 @@ where
108108
}
109109

110110
#[allow(clippy::trivially_copy_pass_by_ref)]
111-
pub(crate) fn serialize_u32_as_i32<S: Serializer>(
111+
pub(crate) fn serialize_u32_option_as_i32<S: Serializer>(
112112
val: &Option<u32>,
113113
serializer: S,
114114
) -> std::result::Result<S::Ok, S::Error> {
115115
match val {
116-
Some(val) if { *val <= std::i32::MAX as u32 } => serializer.serialize_i32(*val as i32),
116+
Some(ref val) => bson::serde_helpers::serialize_u32_as_i32(val, serializer),
117117
None => serializer.serialize_none(),
118-
_ => Err(ser::Error::custom("u32 specified does not fit into an i32")),
119118
}
120119
}
121120

@@ -136,6 +135,16 @@ pub(crate) fn serialize_batch_size<S: Serializer>(
136135
}
137136
}
138137

138+
pub(crate) fn serialize_u64_option_as_i64<S: Serializer>(
139+
val: &Option<u64>,
140+
serializer: S,
141+
) -> std::result::Result<S::Ok, S::Error> {
142+
match val {
143+
Some(ref v) => bson::serde_helpers::serialize_u64_as_i64(v, serializer),
144+
None => serializer.serialize_none(),
145+
}
146+
}
147+
139148
/// Deserialize an u64 from any BSON number type if it could be done losslessly.
140149
pub(crate) fn deserialize_u64_from_bson_number<'de, D>(
141150
deserializer: D,

src/client/options/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod resolver_config;
55

66
use std::{
77
collections::HashSet,
8+
convert::TryFrom,
89
fmt::{self, Display, Formatter},
910
fs::File,
1011
hash::{Hash, Hasher},
@@ -1654,18 +1655,17 @@ impl ClientOptionsParser {
16541655
let mut write_concern = self.write_concern.get_or_insert_with(Default::default);
16551656

16561657
match i32::from_str_radix(value, 10) {
1657-
Ok(w) => {
1658-
if w < 0 {
1658+
Ok(w) => match u32::try_from(w) {
1659+
Ok(uw) => write_concern.w = Some(Acknowledgment::from(uw)),
1660+
Err(_) => {
16591661
return Err(ErrorKind::ArgumentError {
16601662
message: "connection string `w` option cannot be a negative \
16611663
integer"
16621664
.to_string(),
16631665
}
1664-
.into());
1666+
.into())
16651667
}
1666-
1667-
write_concern.w = Some(Acknowledgment::from(w));
1668-
}
1668+
},
16691669
Err(_) => {
16701670
write_concern.w = Some(Acknowledgment::from(value.to_string()));
16711671
}

src/coll/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ where
260260
pub async fn estimated_document_count(
261261
&self,
262262
options: impl Into<Option<EstimatedDocumentCountOptions>>,
263-
) -> Result<i64> {
263+
) -> Result<u64> {
264264
let mut options = options.into();
265265
resolve_options!(self, options, [read_concern, selection_criteria]);
266266

@@ -274,7 +274,7 @@ where
274274
filter: impl Into<Option<Document>>,
275275
options: impl Into<Option<CountOptions>>,
276276
session: impl Into<Option<&mut ClientSession>>,
277-
) -> Result<i64> {
277+
) -> Result<u64> {
278278
let mut options = options.into();
279279
resolve_options!(self, options, [read_concern, selection_criteria]);
280280

@@ -290,7 +290,7 @@ where
290290
&self,
291291
filter: impl Into<Option<Document>>,
292292
options: impl Into<Option<CountOptions>>,
293-
) -> Result<i64> {
293+
) -> Result<u64> {
294294
self.count_documents_common(filter, options, None).await
295295
}
296296

@@ -303,7 +303,7 @@ where
303303
filter: impl Into<Option<Document>>,
304304
options: impl Into<Option<CountOptions>>,
305305
session: &mut ClientSession,
306-
) -> Result<i64> {
306+
) -> Result<u64> {
307307
self.count_documents_common(filter, options, session).await
308308
}
309309

src/coll/options.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ use crate::{
1010
deserialize_duration_from_u64_millis,
1111
serialize_batch_size,
1212
serialize_duration_as_int_millis,
13-
serialize_u32_as_i32,
13+
serialize_u32_option_as_i32,
14+
serialize_u64_option_as_i64,
1415
},
1516
concern::{ReadConcern, WriteConcern},
1617
options::Collation,
@@ -498,7 +499,7 @@ pub struct CountOptions {
498499
pub hint: Option<Hint>,
499500

500501
/// The maximum number of documents to count.
501-
pub limit: Option<i64>,
502+
pub limit: Option<u64>,
502503

503504
/// The maximum amount of time to allow the query to run.
504505
///
@@ -508,7 +509,7 @@ pub struct CountOptions {
508509
pub max_time: Option<Duration>,
509510

510511
/// The number of documents to skip before counting.
511-
pub skip: Option<i64>,
512+
pub skip: Option<u64>,
512513

513514
/// The collation to use for the operation.
514515
///
@@ -519,7 +520,6 @@ pub struct CountOptions {
519520
/// The criteria used to select a server for this operation.
520521
///
521522
/// If none specified, the default set on the collection will be used.
522-
#[serde(skip_serializing)]
523523
pub selection_criteria: Option<SelectionCriteria>,
524524

525525
/// The level of the read concern.
@@ -618,7 +618,7 @@ pub struct FindOptions {
618618
/// only the number of documents kept in memory at a given time (and by extension, the
619619
/// number of round trips needed to return the entire set of documents returned by the
620620
/// query.
621-
#[serde(serialize_with = "serialize_u32_as_i32")]
621+
#[serde(serialize_with = "serialize_u32_option_as_i32")]
622622
pub batch_size: Option<u32>,
623623

624624
/// Tags the query with an arbitrary string to help trace the operation through the database
@@ -650,7 +650,8 @@ pub struct FindOptions {
650650
///
651651
/// Note: this option is deprecated starting in MongoDB version 4.0 and removed in MongoDB 4.2.
652652
/// Use the maxTimeMS option instead.
653-
pub max_scan: Option<i64>,
653+
#[serde(serialize_with = "serialize_u64_option_as_i64")]
654+
pub max_scan: Option<u64>,
654655

655656
/// The maximum amount of time to allow the query to run.
656657
///
@@ -689,7 +690,8 @@ pub struct FindOptions {
689690
pub show_record_id: Option<bool>,
690691

691692
/// The number of documents to skip before counting.
692-
pub skip: Option<i64>,
693+
#[serde(serialize_with = "serialize_u64_option_as_i64")]
694+
pub skip: Option<u64>,
693695

694696
/// The order of the documents for the purposes of the operation.
695697
pub sort: Option<Document>,
@@ -774,7 +776,8 @@ pub struct FindOneOptions {
774776
///
775777
/// Note: this option is deprecated starting in MongoDB version 4.0 and removed in MongoDB 4.2.
776778
/// Use the maxTimeMS option instead.
777-
pub max_scan: Option<i64>,
779+
#[serde(serialize_with = "serialize_u64_option_as_i64")]
780+
pub max_scan: Option<u64>,
778781

779782
/// The maximum amount of time to allow the query to run.
780783
///
@@ -806,7 +809,8 @@ pub struct FindOneOptions {
806809
pub show_record_id: Option<bool>,
807810

808811
/// The number of documents to skip before counting.
809-
pub skip: Option<i64>,
812+
#[serde(serialize_with = "serialize_u64_option_as_i64")]
813+
pub skip: Option<u64>,
810814

811815
/// The order of the documents for the purposes of the operation.
812816
pub sort: Option<Document>,

src/concern/mod.rs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use serde_with::skip_serializing_none;
1010
use typed_builder::TypedBuilder;
1111

1212
use crate::{
13-
bson::doc,
13+
bson::{doc, serde_helpers},
1414
bson_util,
1515
error::{ErrorKind, Result},
1616
};
@@ -169,9 +169,11 @@ pub enum Acknowledgment {
169169
/// the driver will not receive a response indicating whether an operation succeeded or failed.
170170
/// It also means that the operation cannot be associated with a session. It is reccommended to
171171
/// avoid using unacknowledged write concerns.
172-
Nodes(i32),
172+
Nodes(u32),
173+
173174
/// Requires acknowledgement that the write has reached the majority of nodes.
174175
Majority,
176+
175177
/// Requires acknowledgement according to the given custom write concern. See [here](https://docs.mongodb.com/manual/tutorial/configure-replica-set-tag-sets/#tag-sets-and-custom-write-concern-behavior)
176178
/// for more information.
177179
Custom(String),
@@ -184,7 +186,7 @@ impl Serialize for Acknowledgment {
184186
{
185187
match self {
186188
Acknowledgment::Majority => serializer.serialize_str("majority"),
187-
Acknowledgment::Nodes(n) => serializer.serialize_i32(*n),
189+
Acknowledgment::Nodes(n) => serde_helpers::serialize_u32_as_i32(n, serializer),
188190
Acknowledgment::Custom(name) => serializer.serialize_str(name),
189191
}
190192
}
@@ -198,7 +200,7 @@ impl<'de> Deserialize<'de> for Acknowledgment {
198200
#[derive(Deserialize)]
199201
#[serde(untagged)]
200202
enum IntOrString {
201-
Int(i32),
203+
Int(u32),
202204
String(String),
203205
}
204206
match IntOrString::deserialize(deserializer)? {
@@ -208,8 +210,8 @@ impl<'de> Deserialize<'de> for Acknowledgment {
208210
}
209211
}
210212

211-
impl From<i32> for Acknowledgment {
212-
fn from(i: i32) -> Self {
213+
impl From<u32> for Acknowledgment {
214+
fn from(i: u32) -> Self {
213215
Acknowledgment::Nodes(i)
214216
}
215217
}
@@ -231,7 +233,7 @@ impl Acknowledgment {
231233
#[cfg(all(test, not(feature = "sync")))]
232234
pub(crate) fn to_bson(&self) -> Bson {
233235
match self {
234-
Acknowledgment::Nodes(i) => Bson::Int32(*i),
236+
Acknowledgment::Nodes(i) => Bson::Int32(*i as i32),
235237
Acknowledgment::Majority => Bson::String("majority".to_string()),
236238
Acknowledgment::Custom(s) => Bson::String(s.to_string()),
237239
}
@@ -244,18 +246,9 @@ impl WriteConcern {
244246
self.w != Some(Acknowledgment::Nodes(0)) || self.journal == Some(true)
245247
}
246248

247-
/// Validates that the write concern. A write concern is invalid if the `w` field is 0
249+
/// Validates that the write concern. A write concern is invalid if both the `w` field is 0
248250
/// and the `j` field is `true`.
249-
pub fn validate(&self) -> Result<()> {
250-
if let Some(Acknowledgment::Nodes(i)) = self.w {
251-
if i < 0 {
252-
return Err(ErrorKind::ArgumentError {
253-
message: "write concern `w` field cannot be negative integer".to_string(),
254-
}
255-
.into());
256-
}
257-
}
258-
251+
pub(crate) fn validate(&self) -> Result<()> {
259252
if self.w == Some(Acknowledgment::Nodes(0)) && self.journal == Some(true) {
260253
return Err(ErrorKind::ArgumentError {
261254
message: "write concern cannot have w=0 and j=true".to_string(),

src/db/options.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,14 @@ pub struct CreateCollectionOptions {
3939

4040
/// The maximum size (in bytes) for a capped collection. This option is ignored if `capped` is
4141
/// not set to true.
42-
pub size: Option<i64>,
42+
#[serde(serialize_with = "bson_util::serialize_u64_option_as_i64")]
43+
pub size: Option<u64>,
4344

4445
/// The maximum number of documents in a capped collection. The `size` limit takes precedence
4546
/// over this option. If a capped collection reaches the size limit before it reaches the
4647
/// maximum number of documents, MongoDB removes old documents.
47-
pub max: Option<i64>,
48+
#[serde(serialize_with = "bson_util::serialize_u64_option_as_i64")]
49+
pub max: Option<u64>,
4850

4951
/// The storage engine that the collection should use. The value should take the following
5052
/// form:

src/operation/count/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ impl Count {
3737
}
3838

3939
impl Operation for Count {
40-
type O = i64;
40+
type O = u64;
4141
const NAME: &'static str = "count";
4242

4343
fn build(&self, description: &StreamDescription) -> Result<Command> {
@@ -122,5 +122,5 @@ impl Operation for Count {
122122

123123
#[derive(Debug, Deserialize)]
124124
struct ResponseBody {
125-
n: i64,
125+
n: u64,
126126
}

src/operation/count_documents/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl CountDocuments {
6464
}
6565

6666
impl Operation for CountDocuments {
67-
type O = i64;
67+
type O = u64;
6868
const NAME: &'static str = Aggregate::NAME;
6969

7070
fn build(&self, description: &StreamDescription) -> Result<Command> {
@@ -98,7 +98,7 @@ impl Operation for CountDocuments {
9898
}
9999
};
100100

101-
bson_util::get_int(n).ok_or_else(|| {
101+
bson_util::get_u64(n).ok_or_else(|| {
102102
ErrorKind::ResponseError {
103103
message: format!(
104104
"server response to count_documents aggregate should have contained integer \

src/operation/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ struct WriteResponseBody<T = EmptyBody> {
155155
#[serde(flatten)]
156156
body: T,
157157

158-
n: i64,
158+
n: u64,
159159

160160
#[serde(rename = "writeErrors")]
161161
write_errors: Option<Vec<BulkWriteError>>,

src/operation/update/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,6 @@ impl Operation for Update {
152152
#[derive(Deserialize)]
153153
struct UpdateBody {
154154
#[serde(rename = "nModified")]
155-
n_modified: i64,
155+
n_modified: u64,
156156
upserted: Option<Vec<Document>>,
157157
}

0 commit comments

Comments
 (0)