Skip to content

Commit ea669ac

Browse files
nagisarnarubin
authored andcommitted
Make Topic a newtype (standard-ai#29)
Turns out having anything with any sort of lifetime in stream/sink chains will trigger nasty issues such as rust-lang/rust#79648 which is pretty difficult to figure out how to work around. Making `Topic` a newtype erases the lifetime from the type, making it significantly easier to work with in thsoe contexts.
1 parent 9dc5917 commit ea669ac

File tree

8 files changed

+41
-22
lines changed

8 files changed

+41
-22
lines changed

examples/publish.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ struct UserCreatedMessage {
1515
impl<'a> EncodableMessage for &'a UserCreatedMessage {
1616
type Error = hedwig::validators::JsonSchemaValidatorError;
1717
type Validator = hedwig::validators::JsonSchemaValidator;
18-
fn topic(&self) -> &'static str {
19-
"user.created"
18+
fn topic(&self) -> hedwig::Topic {
19+
"user.created".into()
2020
}
2121
fn encode(self, validator: &Self::Validator) -> Result<hedwig::ValidatedMessage, Self::Error> {
2222
Ok(validator

src/lib.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
//! impl<'a> hedwig::publish::EncodableMessage for &'a UserCreatedMessage {
5959
//! type Error = hedwig::validators::JsonSchemaValidatorError;
6060
//! type Validator = hedwig::validators::JsonSchemaValidator;
61-
//! fn topic(&self) -> hedwig::Topic { "user.created" }
61+
//! fn topic(&self) -> hedwig::Topic { "user.created".into() }
6262
//! fn encode(self, validator: &Self::Validator)
6363
//! -> Result<hedwig::ValidatedMessage, Self::Error> {
6464
//! validator.validate(
@@ -107,20 +107,17 @@
107107
#![cfg_attr(docsrs, feature(doc_cfg))]
108108

109109
use std::{collections::BTreeMap, time::SystemTime};
110-
110+
pub use topic::Topic;
111111
use uuid::Uuid;
112112

113113
#[cfg(feature = "publish")]
114114
#[cfg_attr(docsrs, doc(cfg(feature = "publish")))]
115115
pub mod publish;
116-
117116
#[cfg(test)]
118117
mod tests;
118+
mod topic;
119119
pub mod validators;
120120

121-
/// A message queue topic name to which messages can be published
122-
pub type Topic = &'static str;
123-
124121
/// All errors that may be returned when operating top level APIs.
125122
#[derive(Debug, thiserror::Error)]
126123
#[non_exhaustive]

src/publish/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ where
185185
None => None,
186186
Some(stream_item) => Some((
187187
stream_item,
188-
this.topic,
188+
*this.topic,
189189
this.messages
190190
.next()
191191
.expect("should be as many messages as publishes"),

src/publish/publishers/mock.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ impl MockPublisher {
4545
/// be published, was indeed published
4646
///
4747
/// Panics if the message was not published.
48-
pub fn assert_message_published(&self, topic: Topic, uuid: &Uuid) {
48+
pub fn assert_message_published<T: Into<Topic>>(&self, topic: T, uuid: &Uuid) {
49+
let topic = topic.into();
4950
{
5051
let lock = self.0.lock().expect("this mutex cannot get poisoned");
5152
for (mt, msg) in &lock[..] {

src/publish/publishers/null.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl Publisher for NullPublisher {
2020
type MessageError = std::convert::Infallible;
2121
type PublishStream = NullPublishStream;
2222

23-
fn publish<'a, I>(&self, _: &'static str, messages: I) -> Self::PublishStream
23+
fn publish<'a, I>(&self, _: crate::Topic, messages: I) -> Self::PublishStream
2424
where
2525
I: Iterator<Item = &'a ValidatedMessage> + ExactSizeIterator,
2626
{

src/publish/sink.rs

+9-9
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ mod test {
272272
type Validator = TestValidator;
273273

274274
fn topic(&self) -> Topic {
275-
"test_topic"
275+
"test_topic".into()
276276
}
277277

278278
fn encode(self, _: &Self::Validator) -> Result<ValidatedMessage, Self::Error> {
@@ -382,7 +382,7 @@ mod test {
382382
assert_eq!(1, sink.sink.poll_close_called);
383383
assert_eq!(
384384
vec![(
385-
"test_topic",
385+
"test_topic".into(),
386386
TestMessage("foo").encode(&TestValidator).unwrap()
387387
)],
388388
sink.sink.elements
@@ -517,14 +517,14 @@ mod test {
517517
assert_eq!(
518518
Ok(()),
519519
sink.as_mut()
520-
.start_send(("test_topic", test_validated_message("foo")))
520+
.start_send(("test_topic".into(), test_validated_message("foo")))
521521
);
522522
}
523523

524524
/// The publisher should start flushing when the batch size has been exceeded
525525
#[test]
526526
fn batching_batches() {
527-
let topic = "test_topic";
527+
let topic = "test_topic".into();
528528
let batch_size = 3;
529529
let publisher = MockPublisher::new();
530530
let sink = publisher_sink(publisher, batch_size);
@@ -565,7 +565,7 @@ mod test {
565565
/// The publisher should flush buffered elements when asked to close
566566
#[test]
567567
fn close_flushes_batch() {
568-
let topic = "test_topic";
568+
let topic = "test_topic".into();
569569
let batch_size = 3;
570570
let publisher = MockPublisher::new();
571571
let sink = publisher_sink(publisher, batch_size);
@@ -599,7 +599,7 @@ mod test {
599599
/// The publisher should flush buffered elements when asked to flush
600600
#[test]
601601
fn flush_incomplete_batch() {
602-
let topic = "test_topic";
602+
let topic = "test_topic".into();
603603
let batch_size = 3;
604604
let publisher = MockPublisher::new();
605605
let sink = publisher_sink(publisher, batch_size);
@@ -635,7 +635,7 @@ mod test {
635635
#[test]
636636
#[should_panic]
637637
fn panic_at_buffer_full_without_ready_check() {
638-
let topic = "test_topic";
638+
let topic = "test_topic".into();
639639
let batch_size = 1;
640640
let publisher = MockPublisher::new();
641641
let sink = publisher_sink(publisher, batch_size);
@@ -656,7 +656,7 @@ mod test {
656656
/// Step through flushing a non-full batch and see that yield points are respected
657657
#[test]
658658
fn partial_flushing_check() {
659-
let topic = "test_topic";
659+
let topic = "test_topic".into();
660660
let batch_size = 3;
661661
let (publisher, command) = ControlledPublisher::new();
662662
let sink = publisher_sink(publisher, batch_size);
@@ -724,7 +724,7 @@ mod test {
724724
/// A failed message can be re-sent to the sink and eventually succeed
725725
#[test]
726726
fn flushing_error_retry() {
727-
let topic = "test_topic";
727+
let topic = "test_topic".into();
728728
let batch_size = 5;
729729
let (publisher, command) = ControlledPublisher::new();
730730
let sink = publisher_sink(publisher, batch_size);

src/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ impl<'a, I: serde::Serialize> EncodableMessage for &'a JsonUserCreatedMessage<I>
7171
type Error = validators::JsonSchemaValidatorError;
7272
type Validator = validators::JsonSchemaValidator;
7373

74-
fn topic(&self) -> &'static str {
75-
"user.created"
74+
fn topic(&self) -> crate::Topic {
75+
"user.created".into()
7676
}
7777
fn encode(self, validator: &Self::Validator) -> Result<ValidatedMessage, Self::Error> {
7878
validator.validate(

src/topic.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/// A message queue topic name to which messages can be published
2+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
3+
pub struct Topic(&'static str);
4+
5+
impl std::fmt::Display for Topic {
6+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
7+
std::fmt::Display::fmt(self.0, f)
8+
}
9+
}
10+
11+
impl From<&'static str> for Topic {
12+
fn from(s: &'static str) -> Topic {
13+
Topic(s)
14+
}
15+
}
16+
17+
impl From<Topic> for &'static str {
18+
fn from(s: Topic) -> &'static str {
19+
s.0
20+
}
21+
}

0 commit comments

Comments
 (0)