Skip to content

Commit b7aa852

Browse files
b5rklaehn
andauthored
feat(tags api)!: return number of tags removed by delete operations (#178)
## Description It's a huge footgun to silently return `Ok(())`, even when no tags are actually removed. Returning the number of tags removed. Also adds documentation calling out this behaviour. ## Breaking Changes breaks the return value of the tags API ## Notes & open questions Should we do this with rename, and other mutating operations? ## Change checklist - [ ] Self-review. - [ ] Documentation updates following the [style guide](https://rust-lang.github.io/rfcs/1574-more-api-documentation-conventions.html#appendix-a-full-conventions-text), if relevant. - [ ] Tests if relevant. - [ ] All breaking changes documented. --------- Co-authored-by: Ruediger Klaehn <[email protected]>
1 parent f890c79 commit b7aa852

File tree

4 files changed

+26
-11
lines changed

4 files changed

+26
-11
lines changed

src/api/proto.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ pub enum Request {
118118
ListTags(ListTagsRequest),
119119
#[rpc(tx = oneshot::Sender<super::Result<()>>)]
120120
SetTag(SetTagRequest),
121-
#[rpc(tx = oneshot::Sender<super::Result<()>>)]
121+
#[rpc(tx = oneshot::Sender<super::Result<u64>>)]
122122
DeleteTags(DeleteTagsRequest),
123123
#[rpc(tx = oneshot::Sender<super::Result<()>>)]
124124
RenameTag(RenameTagRequest),

src/api/tags.rs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,21 +107,28 @@ impl Tags {
107107
self.list_with_opts(ListOptions::hash_seq()).await
108108
}
109109

110-
/// Deletes a tag.
111-
pub async fn delete_with_opts(&self, options: DeleteOptions) -> super::RequestResult<()> {
110+
/// Deletes a tag, with full control over options. All other delete methods
111+
/// wrap this.
112+
///
113+
/// Returns the number of tags actually removed. Attempting to delete a non-existent tag will *not* fail.
114+
pub async fn delete_with_opts(&self, options: DeleteOptions) -> super::RequestResult<u64> {
112115
trace!("{:?}", options);
113-
self.client.rpc(options).await??;
114-
Ok(())
116+
let deleted = self.client.rpc(options).await??;
117+
Ok(deleted)
115118
}
116119

117120
/// Deletes a tag.
118-
pub async fn delete(&self, name: impl AsRef<[u8]>) -> super::RequestResult<()> {
121+
///
122+
/// Returns the number of tags actually removed. Attempting to delete a non-existent tag will *not* fail.
123+
pub async fn delete(&self, name: impl AsRef<[u8]>) -> super::RequestResult<u64> {
119124
self.delete_with_opts(DeleteOptions::single(name.as_ref()))
120125
.await
121126
}
122127

123128
/// Deletes a range of tags.
124-
pub async fn delete_range<R, E>(&self, range: R) -> super::RequestResult<()>
129+
///
130+
/// Returns the number of tags actually removed. Attempting to delete a non-existent tag will *not* fail.
131+
pub async fn delete_range<R, E>(&self, range: R) -> super::RequestResult<u64>
125132
where
126133
R: RangeBounds<E>,
127134
E: AsRef<[u8]>,
@@ -130,13 +137,17 @@ impl Tags {
130137
}
131138

132139
/// Delete all tags with the given prefix.
133-
pub async fn delete_prefix(&self, prefix: impl AsRef<[u8]>) -> super::RequestResult<()> {
140+
///
141+
/// Returns the number of tags actually removed. Attempting to delete a non-existent tag will *not* fail.
142+
pub async fn delete_prefix(&self, prefix: impl AsRef<[u8]>) -> super::RequestResult<u64> {
134143
self.delete_with_opts(DeleteOptions::prefix(prefix.as_ref()))
135144
.await
136145
}
137146

138147
/// Delete all tags. Use with care. After this, all data will be garbage collected.
139-
pub async fn delete_all(&self) -> super::RequestResult<()> {
148+
///
149+
/// Returns the number of tags actually removed. Attempting to delete a non-existent tag will *not* fail.
150+
pub async fn delete_all(&self) -> super::RequestResult<u64> {
140151
self.delete_with_opts(DeleteOptions {
141152
from: None,
142153
to: None,

src/store/fs/meta.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,10 +631,12 @@ impl Actor {
631631
.extract_from_if((from, to), |_, _| true)
632632
.context(StorageSnafu)?;
633633
// drain the iterator to actually remove the tags
634+
let mut deleted = 0;
634635
for res in removing {
635636
res.context(StorageSnafu)?;
637+
deleted += 1;
636638
}
637-
tx.send(Ok(())).await.ok();
639+
tx.send(Ok(deleted)).await.ok();
638640
Ok(())
639641
}
640642

src/store/mem.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ impl Actor {
227227
info!("deleting tags from {:?} to {:?}", from, to);
228228
// state.tags.remove(&from.unwrap());
229229
// todo: more efficient impl
230+
let mut deleted = 0;
230231
self.state.tags.retain(|tag, _| {
231232
if let Some(from) = &from {
232233
if tag < from {
@@ -239,9 +240,10 @@ impl Actor {
239240
}
240241
}
241242
info!(" removing {:?}", tag);
243+
deleted += 1;
242244
false
243245
});
244-
tx.send(Ok(())).await.ok();
246+
tx.send(Ok(deleted)).await.ok();
245247
}
246248
Command::RenameTag(cmd) => {
247249
let RenameTagMsg {

0 commit comments

Comments
 (0)