Skip to content

Commit f5e7192

Browse files
committed
Found a way around Rust compiler bugs rust-lang/rust#100013 and rust-lang/rust#102211
1 parent d649ec8 commit f5e7192

File tree

4 files changed

+141
-32
lines changed

4 files changed

+141
-32
lines changed

crates/fhir-sdk/src/client/r4b/search/mod.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Client search implementation.
22
33
use crate::client::search::NextPageCursor;
4-
use crate::client::{search::SearchExecutor, Client, Error, FhirR4B, SearchParameters};
4+
use crate::client::{
5+
search::PagedSearchExecutor, search::UnpagedSearchExecutor, Client, Error, FhirR4B,
6+
SearchParameters,
7+
};
58
use async_trait::async_trait;
69
use fhir_model::r4b::resources::{Bundle, DomainResource, NamedResource, Resource};
710
use paging::{Page, Unpaged};
@@ -15,10 +18,12 @@ mod paging;
1518
mod params;
1619

1720
#[async_trait]
18-
impl<R> SearchExecutor<R> for Client<FhirR4B>
21+
impl<R> PagedSearchExecutor<R> for Client<FhirR4B>
1922
where
2023
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
2124
{
25+
type Stream = Page<R>;
26+
2227
#[allow(refining_impl_trait)]
2328
async fn search_paged(
2429
self,
@@ -56,6 +61,14 @@ where
5661

5762
Ok((page, cursor))
5863
}
64+
}
65+
66+
#[async_trait]
67+
impl<R> UnpagedSearchExecutor<R> for Client<FhirR4B>
68+
where
69+
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
70+
{
71+
type Stream = Unpaged<R>;
5972

6073
#[allow(refining_impl_trait)]
6174
async fn search_unpaged(self, params: SearchParameters) -> Result<Unpaged<R>, Error> {
@@ -75,3 +88,25 @@ pub(self) fn find_next_page_url(bundle: &Bundle) -> Option<Result<Url, Error>> {
7588

7689
Some(Url::parse(url_str).map_err(|_| Error::UrlParse(url_str.to_string())))
7790
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use fhir_model::r4b::resources::Observation;
95+
96+
use crate::client::{Client, FhirR4B};
97+
98+
/// The search code is prone to run into rustc bugs [rust-lang/rust#100013](https://github.com/rust-lang/rust/issues/100013) and
99+
/// [rust-lang/rust#102211](https://github.com/rust-lang/rust/issues/102211). We implemented a workaround for it.
100+
/// This test is just there to prevent regressions. It doesn't actually test anything, we just need to make sure this compiles
101+
#[allow(dead_code)]
102+
async fn rustc_bug_workaround_inner() {
103+
let client: Client<FhirR4B> = Client::builder().build().unwrap();
104+
105+
fn assert_send<T: Send>(v: T) -> T {
106+
v
107+
}
108+
109+
// We don't actually test anything here, we just need to make sure this compiles
110+
let _ = assert_send(client.search::<Observation>().send());
111+
}
112+
}

crates/fhir-sdk/src/client/r5/search/mod.rs

+39-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Client search implementation.
22
33
use crate::client::search::NextPageCursor;
4-
use crate::client::{search::SearchExecutor, Client, Error, FhirR5, SearchParameters};
4+
use crate::client::{
5+
search::PagedSearchExecutor, search::UnpagedSearchExecutor, Client, Error, FhirR5,
6+
SearchParameters,
7+
};
58
use async_trait::async_trait;
69
use fhir_model::r5::codes::LinkRelationTypes;
710
use fhir_model::r5::resources::{Bundle, DomainResource, NamedResource, Resource};
@@ -16,16 +19,18 @@ mod paging;
1619
mod params;
1720

1821
#[async_trait]
19-
impl<R> SearchExecutor<R> for Client<FhirR5>
22+
impl<R> PagedSearchExecutor<R> for Client<FhirR5>
2023
where
2124
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
2225
{
26+
type Stream = Page<R>;
27+
2328
#[allow(refining_impl_trait)]
2429
async fn search_paged(
2530
self,
2631
params: SearchParameters,
2732
page_size: Option<u32>,
28-
) -> Result<(Page<R>, Option<NextPageCursor<Self, R>>), Error> {
33+
) -> Result<(Self::Stream, Option<NextPageCursor<Self, R>>), Error> {
2934
let mut url = self.url(&[R::TYPE.as_str()]);
3035
url.query_pairs_mut().extend_pairs(params.into_queries()).finish();
3136

@@ -40,7 +45,7 @@ where
4045
async fn fetch_next_page(
4146
self,
4247
url: Url,
43-
) -> Result<(Page<R>, Option<NextPageCursor<Self, R>>), Error> {
48+
) -> Result<(Self::Stream, Option<NextPageCursor<Self, R>>), Error> {
4449
let searchset: Bundle = self.fetch_resource(url).await?;
4550

4651
let cursor = match find_next_page_url(&searchset) {
@@ -57,6 +62,14 @@ where
5762

5863
Ok((page, cursor))
5964
}
65+
}
66+
67+
#[async_trait]
68+
impl<R> UnpagedSearchExecutor<R> for Client<FhirR5>
69+
where
70+
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
71+
{
72+
type Stream = Unpaged<R>;
6073

6174
#[allow(refining_impl_trait)]
6275
async fn search_unpaged(self, params: SearchParameters) -> Result<Unpaged<R>, Error> {
@@ -80,3 +93,25 @@ pub(self) fn find_next_page_url(bundle: &Bundle) -> Option<Result<Url, Error>> {
8093

8194
Some(Url::parse(url_str).map_err(|_| Error::UrlParse(url_str.to_string())))
8295
}
96+
97+
#[cfg(test)]
98+
mod tests {
99+
use fhir_model::r5::resources::Observation;
100+
101+
use crate::client::{Client, FhirR5};
102+
103+
/// The search code is prone to run into rustc bugs [rust-lang/rust#100013](https://github.com/rust-lang/rust/issues/100013) and
104+
/// [rust-lang/rust#102211](https://github.com/rust-lang/rust/issues/102211). We implemented a workaround for it.
105+
/// This test is just there to prevent regressions. It doesn't actually test anything, we just need to make sure this compiles
106+
#[allow(dead_code)]
107+
async fn rustc_bug_workaround_inner() {
108+
let client: Client<FhirR5> = Client::builder().build().unwrap();
109+
110+
fn assert_send<T: Send>(v: T) -> T {
111+
v
112+
}
113+
114+
// We don't actually test anything here, we just need to make sure this compiles
115+
let _ = assert_send(client.search::<Observation>().send());
116+
}
117+
}

crates/fhir-sdk/src/client/search.rs

+28-24
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub struct UnpagedSearch<E, R> {
2121

2222
impl<E, R> UnpagedSearch<E, R>
2323
where
24-
E: SearchExecutor<R> + 'static,
24+
E: UnpagedSearchExecutor<R>,
2525
{
2626
pub fn new(executor: E) -> Self {
2727
Self { executor, params: SearchParameters::empty(), resource_type: PhantomData }
@@ -65,18 +65,21 @@ where
6565
self.with_raw(key, value)
6666
}
6767

68-
/// Make this a paged search. Next pages can be fetched using
69-
/// [SearchResponse::next_page].
68+
/// Execute the search
69+
pub async fn send(self) -> Result<E::Stream, Error> {
70+
self.executor.search_unpaged(self.params).await
71+
}
72+
}
73+
74+
impl<E, R> UnpagedSearch<E, R>
75+
where
76+
E: UnpagedSearchExecutor<R> + PagedSearchExecutor<R>,
77+
{
7078
pub fn paged(self, page_size: Option<u32>) -> PagedSearch<E, R> {
7179
let Self { executor, params, resource_type } = self;
7280

7381
PagedSearch { executor, params, resource_type, page_size }
7482
}
75-
76-
/// Execute the search
77-
pub async fn send(self) -> Result<impl Stream<Item = Result<R, Error>>, Error> {
78-
self.executor.search_unpaged(self.params).await
79-
}
8083
}
8184

8285
#[derive(Debug)]
@@ -95,12 +98,10 @@ pub struct PagedSearch<E, R> {
9598

9699
impl<E, R> PagedSearch<E, R>
97100
where
98-
E: SearchExecutor<R> + 'static,
101+
E: PagedSearchExecutor<R>,
99102
{
100103
/// Execute the search
101-
pub async fn send(
102-
self,
103-
) -> Result<(impl Stream<Item = Result<R, Error>>, Option<NextPageCursor<E, R>>), Error> {
104+
pub async fn send(self) -> Result<(E::Stream, Option<NextPageCursor<E, R>>), Error> {
104105
self.executor.search_paged(self.params, self.page_size).await
105106
}
106107
}
@@ -121,36 +122,38 @@ pub struct NextPageCursor<E, R> {
121122

122123
impl<E, R> NextPageCursor<E, R>
123124
where
124-
E: SearchExecutor<R> + 'static,
125+
E: PagedSearchExecutor<R> + 'static,
125126
{
126127
pub fn new(executor: E, next_page_url: Url) -> Self {
127128
Self { executor, next_page_url, resource_type: PhantomData }
128129
}
129130

130-
pub async fn next_page(
131-
self,
132-
) -> Result<(impl Stream<Item = Result<R, Error>>, Option<Self>), Error> {
131+
pub async fn next_page(self) -> Result<(E::Stream, Option<Self>), Error> {
133132
self.executor.fetch_next_page(self.next_page_url).await
134133
}
135134
}
136135

137136
#[async_trait]
138-
pub trait SearchExecutor<R>: Sized {
139-
async fn search_unpaged(
140-
self,
141-
params: SearchParameters,
142-
) -> Result<impl Stream<Item = Result<R, Error>>, Error>;
137+
pub trait UnpagedSearchExecutor<R>: Sized {
138+
type Stream: Stream<Item = Result<R, Error>>;
139+
140+
async fn search_unpaged(self, params: SearchParameters) -> Result<Self::Stream, Error>;
141+
}
142+
143+
#[async_trait]
144+
pub trait PagedSearchExecutor<R>: Sized {
145+
type Stream: Stream<Item = Result<R, Error>>;
143146

144147
async fn search_paged(
145148
self,
146149
params: SearchParameters,
147150
page_size: Option<u32>,
148-
) -> Result<(impl Stream<Item = Result<R, Error>>, Option<NextPageCursor<Self, R>>), Error>;
151+
) -> Result<(Self::Stream, Option<NextPageCursor<Self, R>>), Error>;
149152

150153
async fn fetch_next_page(
151154
self,
152155
next_page_url: Url,
153-
) -> Result<(impl Stream<Item = Result<R, Error>>, Option<NextPageCursor<Self, R>>), Error>;
156+
) -> Result<(Self::Stream, Option<NextPageCursor<Self, R>>), Error>;
154157
}
155158

156159
impl<V: 'static> Client<V> {
@@ -159,7 +162,8 @@ impl<V: 'static> Client<V> {
159162
/// matching included resources.
160163
pub fn search<R>(&self) -> UnpagedSearch<Self, R>
161164
where
162-
Self: SearchExecutor<R>,
165+
Self: UnpagedSearchExecutor<R>,
166+
R: Send,
163167
{
164168
UnpagedSearch::new(self.clone())
165169
}

crates/fhir-sdk/src/client/stu3/search/mod.rs

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
//! Client search implementation.
22
33
use crate::client::search::NextPageCursor;
4-
use crate::client::{search::SearchExecutor, Client, Error, FhirStu3, SearchParameters};
4+
use crate::client::{
5+
search::PagedSearchExecutor, search::UnpagedSearchExecutor, Client, Error, FhirStu3,
6+
SearchParameters,
7+
};
58
use async_trait::async_trait;
69
use fhir_model::stu3::resources::{Bundle, DomainResource, NamedResource, Resource};
710
use paging::{Page, Unpaged};
@@ -15,10 +18,12 @@ mod paging;
1518
mod params;
1619

1720
#[async_trait]
18-
impl<R> SearchExecutor<R> for Client<FhirStu3>
21+
impl<R> PagedSearchExecutor<R> for Client<FhirStu3>
1922
where
2023
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
2124
{
25+
type Stream = Page<R>;
26+
2227
#[allow(refining_impl_trait)]
2328
async fn search_paged(
2429
self,
@@ -56,6 +61,14 @@ where
5661

5762
Ok((page, cursor))
5863
}
64+
}
65+
66+
#[async_trait]
67+
impl<R> UnpagedSearchExecutor<R> for Client<FhirStu3>
68+
where
69+
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
70+
{
71+
type Stream = Unpaged<R>;
5972

6073
#[allow(refining_impl_trait)]
6174
async fn search_unpaged(self, params: SearchParameters) -> Result<Unpaged<R>, Error> {
@@ -75,3 +88,25 @@ pub(self) fn find_next_page_url(bundle: &Bundle) -> Option<Result<Url, Error>> {
7588

7689
Some(Url::parse(url_str).map_err(|_| Error::UrlParse(url_str.to_string())))
7790
}
91+
92+
#[cfg(test)]
93+
mod tests {
94+
use fhir_model::stu3::resources::Observation;
95+
96+
use crate::client::{Client, FhirStu3};
97+
98+
/// The search code is prone to run into rustc bugs [rust-lang/rust#100013](https://github.com/rust-lang/rust/issues/100013) and
99+
/// [rust-lang/rust#102211](https://github.com/rust-lang/rust/issues/102211). We implemented a workaround for it.
100+
/// This test is just there to prevent regressions. It doesn't actually test anything, we just need to make sure this compiles
101+
#[allow(dead_code)]
102+
async fn rustc_bug_workaround_inner() {
103+
let client: Client<FhirStu3> = Client::builder().build().unwrap();
104+
105+
fn assert_send<T: Send>(v: T) -> T {
106+
v
107+
}
108+
109+
// We don't actually test anything here, we just need to make sure this compiles
110+
let _ = assert_send(client.search::<Observation>().send());
111+
}
112+
}

0 commit comments

Comments
 (0)