Skip to main content

context69_sdk/
client.rs

1mod facade;
2mod transport;
3
4use std::{sync::Arc, time::Duration};
5
6use reqwest::{Url, header::USER_AGENT};
7use tokio::sync::RwLock;
8
9use crate::Error;
10
11pub use facade::{
12    AuthMeResponse, BatchGetDocumentsRequest, BatchGetDocumentsResponse, CompactSearchHit,
13    CompactSearchResponse, CreateMetadataIndexRequest, DeleteBatchRequest, DocumentChunkResponse,
14    DocumentKey, DocumentResponse, EnsureScopeResponse, FileBatchItem, FileBatchRequest,
15    FileMetadata, GenericTaskRequest, GroupKind, GroupResponse, HealthResponse, MetadataDataType,
16    MetadataFilter, MetadataFilterOperator, MetadataValueKind, ScopeMetadataIndex, ScopeSpec,
17    SearchRequest, TaskItemResponse, TaskItemStatus, TaskItemsResponse, TaskKind, TaskListQuery,
18    TaskPageResponse, TaskProgress, TaskRef, TaskResponse, TaskRetryResponse, TaskStatus,
19    TextBatchItem, TextBatchRequest, TextContentFormat, TranslationDirective, TranslationStatus,
20    UrlBatchItem, UrlBatchRequest, Visibility, WaitOptions,
21};
22
23pub(crate) const PERSONAL_ACCESS_TOKEN_PREFIX: &str = "ctx_pat_";
24
25#[derive(Debug, Clone, Default)]
26struct SessionState {
27    personal_access_token: Option<String>,
28}
29
30#[derive(Clone)]
31pub struct Context69Client {
32    client: reqwest::Client,
33    base_url: Url,
34    session: Arc<RwLock<SessionState>>,
35}
36
37#[derive(Debug)]
38pub struct Context69ClientBuilder {
39    base_url: Option<Url>,
40    user_agent: Option<String>,
41    timeout: Option<Duration>,
42    personal_access_token: Option<String>,
43}
44
45impl Context69Client {
46    pub fn builder() -> Context69ClientBuilder {
47        Context69ClientBuilder {
48            base_url: None,
49            user_agent: None,
50            timeout: None,
51            personal_access_token: None,
52        }
53    }
54
55    pub fn with_personal_access_token(
56        &self,
57        token: impl Into<String>,
58    ) -> Result<Context69Client, Error> {
59        Ok(Context69Client {
60            client: self.client.clone(),
61            base_url: self.base_url.clone(),
62            session: Arc::new(RwLock::new(SessionState {
63                personal_access_token: Some(transport::validate_personal_access_token(
64                    token.into(),
65                )?),
66            })),
67        })
68    }
69}
70
71impl Context69ClientBuilder {
72    pub fn base_url(mut self, base_url: &str) -> Result<Self, Error> {
73        let mut url =
74            Url::parse(base_url).map_err(|_| Error::InvalidBaseUrl(base_url.to_string()))?;
75        if !url.path().ends_with('/') {
76            url.set_path(&format!("{}/", url.path()));
77        }
78        self.base_url = Some(url);
79        Ok(self)
80    }
81
82    pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
83        self.user_agent = Some(user_agent.into());
84        self
85    }
86
87    pub fn timeout(mut self, timeout: Duration) -> Result<Self, Error> {
88        if timeout.is_zero() {
89            return Err(Error::InvalidTimeout(timeout));
90        }
91        self.timeout = Some(timeout);
92        Ok(self)
93    }
94
95    pub fn with_personal_access_token(mut self, token: impl Into<String>) -> Result<Self, Error> {
96        self.personal_access_token = Some(transport::validate_personal_access_token(token.into())?);
97        Ok(self)
98    }
99
100    pub fn build(self) -> Result<Context69Client, Error> {
101        let base_url = self
102            .base_url
103            .ok_or_else(|| Error::InvalidBaseUrl("missing base_url".to_string()))?;
104        let mut builder = reqwest::Client::builder();
105        if let Some(user_agent) = self.user_agent {
106            builder = builder.default_headers({
107                let mut headers = reqwest::header::HeaderMap::new();
108                headers.insert(
109                    USER_AGENT,
110                    user_agent
111                        .parse()
112                        .map_err(|_| Error::InvalidHeader(user_agent.clone()))?,
113                );
114                headers
115            });
116        }
117        if let Some(timeout) = self.timeout {
118            builder = builder.timeout(timeout);
119        }
120        Ok(Context69Client {
121            client: builder.build()?,
122            base_url,
123            session: Arc::new(RwLock::new(SessionState {
124                personal_access_token: self.personal_access_token,
125            })),
126        })
127    }
128}