pinecone_sdk/pinecone/data.rs
1use crate::pinecone::PineconeClient;
2use crate::protos::vector_service_client::VectorServiceClient;
3use crate::utils::errors::PineconeError;
4use once_cell::sync::Lazy;
5use tonic::metadata::{Ascii, MetadataValue as TonicMetadataVal};
6use tonic::service::interceptor::InterceptedService;
7use tonic::service::Interceptor;
8use tonic::transport::Channel;
9use tonic::{Request, Status};
10
11use crate::models::{
12 DescribeIndexStatsResponse, FetchResponse, ListResponse, Metadata, Namespace, QueryResponse,
13 SparseValues, UpdateResponse, UpsertResponse, Vector,
14};
15use crate::protos;
16
17#[derive(Debug, Clone)]
18struct ApiKeyInterceptor {
19 api_token: TonicMetadataVal<Ascii>,
20}
21
22impl Interceptor for ApiKeyInterceptor {
23 fn call(&mut self, mut request: Request<()>) -> Result<Request<()>, Status> {
24 // TODO: replace `api_token` with an `Option`, and do a proper `if_some`.
25 if !self.api_token.is_empty() {
26 request
27 .metadata_mut()
28 .insert("api-key", self.api_token.clone());
29 }
30 Ok(request)
31 }
32}
33
34/// A client for interacting with a Pinecone index.
35#[derive(Debug)]
36pub struct Index {
37 /// The name of the index.
38 host: String,
39 connection: VectorServiceClient<InterceptedService<Channel, ApiKeyInterceptor>>,
40}
41
42impl Index {
43 /// The upsert operation writes vectors into a namespace.
44 /// If a new value is upserted for an existing vector id, it will overwrite the previous value.
45 ///
46 /// ### Arguments
47 /// * `vectors: &[Vector]` - A list of vectors to upsert.
48 /// * `namespace: &Namespace` - The namespace to upsert vectors into. Default is "".
49 ///
50 /// ### Return
51 /// * `Result<UpsertResponse, PineconeError>`
52 ///
53 /// ### Example
54 /// ```no_run
55 /// use pinecone_sdk::models::{Namespace, UpsertResponse, Vector};
56 /// # use pinecone_sdk::utils::errors::PineconeError;
57 ///
58 /// # #[tokio::main]
59 /// # async fn main() -> Result<(), PineconeError>{
60 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
61 ///
62 /// let mut index = pinecone.index("index-host").await?;
63 ///
64 /// let vectors = [Vector {
65 /// id: "vector-id".to_string(),
66 /// values: vec![1.0, 2.0, 3.0, 4.0],
67 /// sparse_values: None,
68 /// metadata: None,
69 /// }];
70 ///
71 /// // Upsert vectors into the namespace "namespace" in the index
72 /// let response: Result<UpsertResponse, PineconeError> = index.upsert(&vectors, &"namespace".into()).await;
73 /// # Ok(())
74 /// # }
75 /// ```
76 pub async fn upsert(
77 &mut self,
78 vectors: &[Vector],
79 namespace: &Namespace,
80 ) -> Result<UpsertResponse, PineconeError> {
81 let request = protos::UpsertRequest {
82 vectors: vectors.to_vec(),
83 namespace: namespace.name.clone(),
84 };
85
86 let response = self
87 .connection
88 .upsert(request)
89 .await
90 .map_err(|e| PineconeError::DataPlaneError { status: e })?
91 .into_inner();
92
93 Ok(response)
94 }
95
96 /// The list operation lists the IDs of vectors in a single namespace of a serverless index. An optional prefix can be passed to limit the results to IDs with a common prefix.
97 ///
98 /// ### Arguments
99 /// * `namespace: &Namespace` - The namespace to list vectors from. Default is "".
100 /// * `prefix: Option<&str>` - The vector IDs to list, will list all vectors with IDs that have a matching prefix. Default is empty string.
101 /// * `limit: Option<u32>` - The maximum number of vector ids to return. If unspecified, the default limit is 100.
102 /// * `pagination_token: Option<&str>` - The token for paginating through results.
103 ///
104 /// ### Return
105 /// * `Result<ListResponse, PineconeError>`
106 ///
107 /// ### Example
108 /// ```no_run
109 /// use pinecone_sdk::models::{Namespace, ListResponse};
110 /// # use pinecone_sdk::utils::errors::PineconeError;
111 ///
112 /// # #[tokio::main]
113 /// # async fn main() -> Result<(), PineconeError>{
114 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
115 ///
116 /// let mut index = pinecone.index("index-host").await?;
117 ///
118 /// // List all vectors in the namespace "namespace"
119 /// let response: Result<ListResponse, PineconeError> = index.list(&"namespace".into(), None, None, None).await;
120 /// # Ok(())
121 /// # }
122 /// ```
123 pub async fn list(
124 &mut self,
125 namespace: &Namespace,
126 prefix: Option<&str>,
127 limit: Option<u32>,
128 pagination_token: Option<&str>,
129 ) -> Result<ListResponse, PineconeError> {
130 let request = protos::ListRequest {
131 namespace: namespace.name.clone(),
132 prefix: prefix.map(|s| s.to_string()),
133 limit,
134 pagination_token: pagination_token.map(|s| s.to_string()),
135 };
136
137 let response = self
138 .connection
139 .list(request)
140 .await
141 .map_err(|e| PineconeError::DataPlaneError { status: e })?
142 .into_inner();
143
144 Ok(response)
145 }
146
147 /// The describe_index_stats operation returns statistics about the index.
148 ///
149 /// ### Arguments
150 /// * `filter: Option<Metadata>` - An optional filter to specify which vectors to return statistics for. None means no filter will be applied. Note that the filter is only supported by pod indexes.
151 ///
152 /// ### Return
153 /// * `Result<DescribeIndexStatsResponse, PineconeError>`
154 ///
155 /// ### Example
156 /// ```no_run
157 /// use std::collections::BTreeMap;
158 /// use pinecone_sdk::models::{DescribeIndexStatsResponse, Value, Kind, Metadata, Namespace};
159 /// # use pinecone_sdk::utils::errors::PineconeError;
160 ///
161 /// # #[tokio::main]
162 /// # async fn main() -> Result<(), PineconeError>{
163 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
164 ///
165 /// let mut index = pinecone.index("index-host").await?;
166 ///
167 /// // Construct a metadata filter
168 /// let mut fields = BTreeMap::new();
169 /// let kind = Some(Kind::StringValue("value".to_string()));
170 /// fields.insert("field".to_string(), Value { kind });
171 ///
172 /// // Describe the index statistics
173 /// let response: Result<DescribeIndexStatsResponse, PineconeError> = index.describe_index_stats(Some(Metadata { fields })).await;
174 /// # Ok(())
175 /// # }
176 /// ```
177 pub async fn describe_index_stats(
178 &mut self,
179 filter: Option<Metadata>,
180 ) -> Result<DescribeIndexStatsResponse, PineconeError> {
181 let request = protos::DescribeIndexStatsRequest { filter };
182
183 let response = self
184 .connection
185 .describe_index_stats(request)
186 .await
187 .map_err(|e| PineconeError::DataPlaneError { status: e })?
188 .into_inner();
189
190 Ok(response)
191 }
192
193 // Helper function to call query operation
194 async fn query(
195 &mut self,
196 request: protos::QueryRequest,
197 ) -> Result<QueryResponse, PineconeError> {
198 let response = self
199 .connection
200 .query(request)
201 .await
202 .map_err(|e| PineconeError::DataPlaneError { status: e })?
203 .into_inner();
204
205 Ok(response)
206 }
207
208 /// The update operation updates a vector in a namespace. If a value is included, it will overwrite the previous value.
209 /// If a `metadata` filter is included, the values of the fields specified in it will be added or overwrite the previous values.
210 ///
211 /// ### Arguments
212 /// * `id: &str` - The vector's unique ID.
213 /// * `values: Vec<f32>` - The vector data.
214 /// * `sparse_values: Option<SparseValues>` - The sparse vector data.
215 /// * `metadata: Option<MetadataFilter>` - The metadata to set for the vector.
216 /// * `namespace: &Namespace` - The namespace containing the vector to update. Default is "".
217 ///
218 /// ### Return
219 /// * `Result<UpsertResponse, PineconeError>`
220 ///
221 /// ### Example
222 /// ```no_run
223 /// use pinecone_sdk::models::{Namespace, SparseValues, Metadata, UpdateResponse};
224 /// # use pinecone_sdk::utils::errors::PineconeError;
225 ///
226 /// # #[tokio::main]
227 /// # async fn main() -> Result<(), PineconeError>{
228 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
229 ///
230 /// let mut index = pinecone.index("index-host").await?;
231 ///
232 /// // Update the vector with id "vector-id" in the namespace "namespace"
233 /// let response: Result<UpdateResponse, PineconeError> = index.update("vector-id", vec![1.0, 2.0, 3.0, 4.0], None, None, &"namespace".into()).await;
234 /// # Ok(())
235 /// # }
236 /// ```
237 pub async fn update(
238 &mut self,
239 id: &str,
240 values: Vec<f32>,
241 sparse_values: Option<SparseValues>,
242 metadata: Option<Metadata>,
243 namespace: &Namespace,
244 ) -> Result<UpdateResponse, PineconeError> {
245 let request = protos::UpdateRequest {
246 id: id.to_string(),
247 values,
248 sparse_values,
249 set_metadata: metadata,
250 namespace: namespace.name.clone(),
251 };
252
253 let response = self
254 .connection
255 .update(request)
256 .await
257 .map_err(|e| PineconeError::DataPlaneError { status: e })?
258 .into_inner();
259
260 Ok(response)
261 }
262
263 /// The query operation searches a namespace using a query vector. It retrieves the ids of the most similar items in a namespace, along with their similarity scores.
264 ///
265 /// ### Arguments
266 /// * `id: &str` - The id of the query vector.
267 /// * `top_k: u32` - The number of results to return.
268 /// * `namespace: &Namespace` - The namespace to query. Default is "".
269 /// * `filter: Option<Metadata>` - The filter to apply to limit your search by vector metadata.
270 /// * `include_values: Option<bool>` - Indicates whether to include the values of the vectors in the response. Default is false.
271 /// * `include_metadata: Option<bool>` - Indicates whether to include the metadata of the vectors in the response. Default is false.
272 ///
273 /// ### Return
274 /// * `Result<QueryResponse, PineconeError>`
275 ///
276 /// ### Example
277 /// ```no_run
278 /// use pinecone_sdk::models::{Namespace, QueryResponse};
279 /// # use pinecone_sdk::utils::errors::PineconeError;
280 ///
281 /// # #[tokio::main]
282 /// # async fn main() -> Result<(), PineconeError>{
283 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
284 ///
285 /// let mut index = pinecone.index("index-host").await?;
286 ///
287 /// // Query the vector with id "vector-id" in the namespace "namespace"
288 /// let response: Result<QueryResponse, PineconeError> = index.query_by_id("vector-id", 10, &Namespace::default(), None, None, None).await;
289 /// # Ok(())
290 /// # }
291 /// ```
292 pub async fn query_by_id(
293 &mut self,
294 id: &str,
295 top_k: u32,
296 namespace: &Namespace,
297 filter: Option<Metadata>,
298 include_values: Option<bool>,
299 include_metadata: Option<bool>,
300 ) -> Result<QueryResponse, PineconeError> {
301 let request = protos::QueryRequest {
302 id: id.to_string(),
303 top_k,
304 namespace: namespace.name.clone(),
305 filter,
306 include_values: include_values.unwrap_or(false),
307 include_metadata: include_metadata.unwrap_or(false),
308 queries: vec![],
309 vector: vec![],
310 sparse_vector: None,
311 };
312
313 self.query(request).await
314 }
315
316 /// The query operation searches a namespace using a query vector. It retrieves the ids of the most similar items in a namespace, along with their similarity scores.
317 ///
318 /// ### Arguments
319 /// * `vector: Vec<f32>` - The query vector.
320 /// * `sparse_vector: Option<SparseValues>` - Vector sparse data.
321 /// * `top_k: u32` - The number of results to return.
322 /// * `namespace: &Namespace` - The namespace to query. Default is "".
323 /// * `filter: Option<Metadata>` - The filter to apply to limit your search by vector metadata.
324 /// * `include_values: Option<bool>` - Indicates whether to include the values of the vectors in the response. Default is false.
325 /// * `include_metadata: Option<bool>` - Indicates whether to include the metadata of the vectors in the response. Default is false.
326 ///
327 /// ### Return
328 /// * `Result<QueryResponse, PineconeError>`
329 ///
330 /// ### Example
331 /// ```no_run
332 /// use pinecone_sdk::models::{Namespace, QueryResponse};
333 /// # use pinecone_sdk::utils::errors::PineconeError;
334 ///
335 /// # #[tokio::main]
336 /// # async fn main() -> Result<(), PineconeError>{
337 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
338 ///
339 /// let mut index = pinecone.index("index-host").await?;
340 ///
341 /// let vector = vec![1.0, 2.0, 3.0, 4.0];
342 ///
343 /// // Query the vector in the default namespace
344 /// let response: Result<QueryResponse, PineconeError> = index.query_by_value(vector, None, 10, &Namespace::default(), None, None, None).await;
345 /// # Ok(())
346 /// # }
347 /// ```
348 pub async fn query_by_value(
349 &mut self,
350 vector: Vec<f32>,
351 sparse_vector: Option<SparseValues>,
352 top_k: u32,
353 namespace: &Namespace,
354 filter: Option<Metadata>,
355 include_values: Option<bool>,
356 include_metadata: Option<bool>,
357 ) -> Result<QueryResponse, PineconeError> {
358 let request = protos::QueryRequest {
359 id: "".to_string(),
360 top_k,
361 namespace: namespace.name.clone(),
362 filter,
363 include_values: include_values.unwrap_or(false),
364 include_metadata: include_metadata.unwrap_or(false),
365 queries: vec![],
366 vector,
367 sparse_vector,
368 };
369
370 self.query(request).await
371 }
372
373 /// The delete_by_id operation deletes vectors by ID from a namespace.
374 ///
375 /// ### Arguments
376 /// * `ids: &[&str]` - List of IDs of vectors to be deleted.
377 /// * `namespace: &Namespace` - The namespace to delete vectors from. Default is "".
378 ///
379 /// ### Return
380 /// * `Result<(), PineconeError>`
381 ///
382 /// ### Example
383 /// ```no_run
384 /// use pinecone_sdk::models::Namespace;
385 /// # use pinecone_sdk::utils::errors::PineconeError;
386 ///
387 /// # #[tokio::main]
388 /// # async fn main() -> Result<(), PineconeError>{
389 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
390 ///
391 /// let mut index = pinecone.index("index-host").await?;
392 ///
393 /// let ids = ["vector-id"];
394 ///
395 /// // Delete vectors from the namespace "namespace" that have the ids in the list
396 /// let response: Result<(), PineconeError> = index.delete_by_id(&ids, &"namespace".into()).await;
397 /// # Ok(())
398 /// # }
399 /// ```
400 pub async fn delete_by_id(
401 &mut self,
402 ids: &[&str],
403 namespace: &Namespace,
404 ) -> Result<(), PineconeError> {
405 let ids = ids.iter().map(|id| id.to_string()).collect::<Vec<String>>();
406 let request = protos::DeleteRequest {
407 ids,
408 delete_all: false,
409 namespace: namespace.name.clone(),
410 filter: None,
411 };
412
413 self.delete(request).await
414 }
415
416 /// The delete_all operation deletes all vectors from a namespace.
417 ///
418 /// ### Arguments
419 /// * `namespace: &Namespace` - The namespace to delete vectors from. Default is "".
420 ///
421 /// ### Return
422 /// * `Result<(), PineconeError>`
423 ///
424 /// ### Example
425 /// ```no_run
426 /// use pinecone_sdk::models::Namespace;
427 /// # use pinecone_sdk::utils::errors::PineconeError;
428 ///
429 /// # #[tokio::main]
430 /// # async fn main() -> Result<(), PineconeError>{
431 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
432 ///
433 /// let mut index = pinecone.index("index-host").await?;
434 ///
435 /// // Delete all vectors from the namespace "namespace"
436 /// let response: Result<(), PineconeError> = index.delete_all(&"namespace".into()).await;
437 /// # Ok(())
438 /// # }
439 /// ```
440 pub async fn delete_all(&mut self, namespace: &Namespace) -> Result<(), PineconeError> {
441 let request = protos::DeleteRequest {
442 ids: vec![],
443 delete_all: true,
444 namespace: namespace.name.clone(),
445 filter: None,
446 };
447
448 self.delete(request).await
449 }
450
451 /// The delete_by_filter operation deletes the vectors from a namespace that satisfy the filter.
452 ///
453 /// ### Arguments
454 /// * `filter: Metadata` - The filter to specify which vectors to delete.
455 /// * `namespace: &Namespace` - The namespace to delete vectors from. Default is "".
456 ///
457 /// ### Return
458 /// * `Result<(), PineconeError>`
459 ///
460 /// ### Example
461 /// ```no_run
462 /// use std::collections::BTreeMap;
463 /// use pinecone_sdk::models::{Metadata, Value, Kind, Namespace};
464 /// # use pinecone_sdk::utils::errors::PineconeError;
465 ///
466 /// # #[tokio::main]
467 /// # async fn main() -> Result<(), PineconeError>{
468 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
469 ///
470 /// let mut index = pinecone.index("index-host").await?;
471 ///
472 /// // Construct a metadata filter
473 /// let mut fields = BTreeMap::new();
474 /// let kind = Some(Kind::StringValue("value".to_string()));
475 /// fields.insert("field".to_string(), Value { kind });
476 ///
477 /// // Delete vectors from the namespace "namespace" that satisfy the filter
478 /// let response: Result<(), PineconeError> = index.delete_by_filter(Metadata { fields }, &"namespace".into()).await;
479 /// # Ok(())
480 /// # }
481 /// ```
482 pub async fn delete_by_filter(
483 &mut self,
484 filter: Metadata,
485 namespace: &Namespace,
486 ) -> Result<(), PineconeError> {
487 let request = protos::DeleteRequest {
488 ids: vec![],
489 delete_all: false,
490 namespace: namespace.name.clone(),
491 filter: Some(filter),
492 };
493
494 self.delete(request).await
495 }
496
497 // Helper function to call delete operation
498 async fn delete(&mut self, request: protos::DeleteRequest) -> Result<(), PineconeError> {
499 let _ = self
500 .connection
501 .delete(request)
502 .await
503 .map_err(|e| PineconeError::DataPlaneError { status: e })?;
504
505 Ok(())
506 }
507
508 /// The fetch operation retrieves vectors by ID from a namespace.
509 ///
510 /// ### Arguments
511 /// * `ids: &[&str]` - The ids of vectors to fetch.
512 /// * `namespace: &Namespace` - The namespace to fetch vectors from. Default is "".
513 ///
514 /// ### Return
515 /// * `Result<FetchResponse, PineconeError>`
516 ///
517 /// ### Example
518 /// ```no_run
519 /// use std::collections::BTreeMap;
520 /// use pinecone_sdk::models::{FetchResponse, Metadata, Value, Kind};
521 /// # use pinecone_sdk::utils::errors::PineconeError;
522 ///
523 /// # #[tokio::main]
524 /// # async fn main() -> Result<(), PineconeError>{
525 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
526 ///
527 /// let mut index = pinecone.index("index-host").await?;
528 ///
529 /// let vectors = &["1", "2"];
530 ///
531 /// // Fetch vectors from the default namespace that have the ids in the list
532 /// let response: Result<FetchResponse, PineconeError> = index.fetch(vectors, &Default::default()).await;
533 /// Ok(())
534 /// }
535 /// ```
536 pub async fn fetch(
537 &mut self,
538 ids: &[&str],
539 namespace: &Namespace,
540 ) -> Result<FetchResponse, PineconeError> {
541 let ids = ids.iter().map(|id| id.to_string()).collect::<Vec<String>>();
542 let request = protos::FetchRequest {
543 ids,
544 namespace: namespace.name.clone(),
545 };
546
547 let response = self
548 .connection
549 .fetch(request)
550 .await
551 .map_err(|e| PineconeError::DataPlaneError { status: e })?
552 .into_inner();
553
554 Ok(response)
555 }
556}
557
558impl PineconeClient {
559 /// Match the scheme in a host string.
560 ///
561 /// ### Arguments
562 /// * `host: &str` - The host string to match.
563 ///
564 /// ### Return
565 /// * `bool` - True if the host string contains a scheme, false otherwise.
566 fn has_scheme(host: &str) -> bool {
567 static RE: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r"^[a-zA-Z]+://").unwrap());
568 RE.is_match(host)
569 }
570
571 /// Match the port in a host string.
572 ///
573 /// ### Arguments
574 /// * `host: &str` - The host string to match.
575 ///
576 /// ### Return
577 /// * `bool` - True if the host string contains a port, false otherwise.
578 fn has_port(host: &str) -> bool {
579 static RE: Lazy<regex::Regex> = Lazy::new(|| regex::Regex::new(r":\d+$").unwrap());
580 RE.is_match(host)
581 }
582
583 /// Target an index for data operations.
584 ///
585 /// ### Arguments
586 /// * `host: &str` - The host of the index to target. If the host does not contain a scheme, it will default to `https://`. If the host does not contain a port, it will default to `443`.
587 ///
588 /// ### Return
589 /// * `Result<Index, PineconeError>`
590 ///
591 /// ### Example
592 ///
593 /// ```no_run
594 /// # use pinecone_sdk::utils::errors::PineconeError;
595 ///
596 /// # #[tokio::main]
597 /// # async fn main() -> Result<(), PineconeError>{
598 /// let pinecone = pinecone_sdk::pinecone::default_client()?;
599 ///
600 /// let index = pinecone.index("index-host").await?;
601 /// # Ok(())
602 /// # }
603 /// ```
604 pub async fn index(&self, host: &str) -> Result<Index, PineconeError> {
605 let endpoint = host.to_string();
606
607 let endpoint = if PineconeClient::has_scheme(&endpoint) {
608 endpoint
609 } else {
610 format!("https://{}", endpoint)
611 };
612
613 let endpoint = if PineconeClient::has_port(&endpoint) {
614 endpoint
615 } else {
616 format!("{}:443", endpoint)
617 };
618
619 let index = Index {
620 host: endpoint.clone(),
621 connection: self.new_index_connection(endpoint).await?,
622 };
623
624 Ok(index)
625 }
626
627 // Helper function to create a new index connection
628 async fn new_index_connection(
629 &self,
630 host: String,
631 ) -> Result<VectorServiceClient<InterceptedService<Channel, ApiKeyInterceptor>>, PineconeError>
632 {
633 let tls_config = tonic::transport::ClientTlsConfig::default();
634
635 // connect to server
636 let endpoint = Channel::from_shared(host)
637 .map_err(|e| PineconeError::ConnectionError { source: e.into() })?
638 .tls_config(tls_config)
639 .map_err(|e| PineconeError::ConnectionError { source: e.into() })?;
640
641 let channel = endpoint
642 .connect()
643 .await
644 .map_err(|e| PineconeError::ConnectionError { source: e.into() })?;
645
646 // add api key in metadata through interceptor
647 let token: TonicMetadataVal<_> = self.api_key.parse().unwrap();
648 let add_api_key_interceptor = ApiKeyInterceptor { api_token: token };
649 let inner = VectorServiceClient::with_interceptor(channel, add_api_key_interceptor);
650
651 Ok(inner)
652 }
653}
654
655#[cfg(test)]
656mod tests {
657 use super::*;
658 use crate::pinecone::default_client;
659 use httpmock::prelude::*;
660
661 #[tokio::test]
662 async fn test_index_full_endpoint() {
663 let server = MockServer::start();
664
665 // server url contains scheme and port
666 let _mock = server.mock(|_when, then| {
667 then.status(200);
668 });
669
670 let pinecone = default_client().expect("Failed to create Pinecone instance");
671
672 let index = pinecone.index(server.base_url().as_str()).await.unwrap();
673
674 assert_eq!(index.host, server.base_url());
675 }
676
677 #[tokio::test]
678 async fn test_index_no_scheme() {
679 let server = MockServer::start();
680
681 // server url contains no scheme
682 let _mock = server.mock(|_when, then| {
683 then.status(200);
684 });
685
686 let pinecone = default_client().expect("Failed to create Pinecone instance");
687
688 let addr = server.address().to_string();
689
690 let _index = pinecone
691 .index(addr.as_str())
692 .await
693 .expect_err("Expected connection error");
694 }
695
696 #[tokio::test]
697 async fn test_index_no_port() {
698 let server = MockServer::start();
699
700 // server url contains no port
701 let _mock = server.mock(|_when, then| {
702 then.status(200);
703 });
704
705 let pinecone = default_client().expect("Failed to create Pinecone instance");
706
707 let scheme_host = format!("http://{}", server.host());
708
709 let _index = pinecone
710 .index(scheme_host.as_str())
711 .await
712 .expect_err("Expected connection error");
713 }
714
715 #[tokio::test]
716 async fn test_index_no_scheme_no_port() {
717 let server = MockServer::start();
718
719 // server url contains no scheme and no port
720 let _mock = server.mock(|_when, then| {
721 then.status(200);
722 });
723
724 let pinecone = default_client().expect("Failed to create Pinecone instance");
725
726 let host = server.host();
727
728 let _index = pinecone
729 .index(host.as_str())
730 .await
731 .expect_err("Expected connection error");
732 }
733}