Skip to main content

kagi_sdk/
routing.rs

1use std::fmt;
2
3use reqwest::Method;
4
5use crate::auth::CredentialKind;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum ProtocolSurface {
9    OfficialApi,
10    SessionWeb,
11}
12
13impl fmt::Display for ProtocolSurface {
14    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
15        match self {
16            Self::OfficialApi => formatter.write_str("OfficialApi"),
17            Self::SessionWeb => formatter.write_str("SessionWeb"),
18        }
19    }
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum ApiVersion {
24    V0,
25    V1,
26    NotApplicable,
27}
28
29impl fmt::Display for ApiVersion {
30    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
31        match self {
32            Self::V0 => formatter.write_str("v0"),
33            Self::V1 => formatter.write_str("v1"),
34            Self::NotApplicable => formatter.write_str("n/a"),
35        }
36    }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
40pub enum ParserShape {
41    JsonEnvelope,
42    Html,
43    Stream,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
47pub enum HttpMethod {
48    Get,
49    Post,
50}
51
52impl HttpMethod {
53    pub(crate) fn as_reqwest(self) -> Method {
54        match self {
55            Self::Get => Method::GET,
56            Self::Post => Method::POST,
57        }
58    }
59}
60
61#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
62pub enum EndpointId {
63    OfficialSearch,
64    OfficialEnrichWeb,
65    OfficialEnrichNews,
66    OfficialSummarizeGet,
67    OfficialSummarizePost,
68    OfficialFastGpt,
69    OfficialSmallwebFeed,
70    SessionHtmlSearch,
71    SessionSummaryLabsGet,
72    SessionSummaryLabsPost,
73}
74
75impl fmt::Display for EndpointId {
76    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77        formatter.write_str(self.spec().name)
78    }
79}
80
81#[derive(Debug, Clone, Copy)]
82pub struct EndpointSpec {
83    pub name: &'static str,
84    pub surface: ProtocolSurface,
85    pub method: HttpMethod,
86    pub route: &'static str,
87    pub version: ApiVersion,
88    pub parser: ParserShape,
89    pub allowed_credential: CredentialKind,
90}
91
92impl EndpointId {
93    pub fn spec(self) -> EndpointSpec {
94        match self {
95            Self::OfficialSearch => EndpointSpec {
96                name: "official.search",
97                surface: ProtocolSurface::OfficialApi,
98                method: HttpMethod::Get,
99                route: "/api/v0/search",
100                version: ApiVersion::V0,
101                parser: ParserShape::JsonEnvelope,
102                allowed_credential: CredentialKind::BotToken,
103            },
104            Self::OfficialEnrichWeb => EndpointSpec {
105                name: "official.enrich_web",
106                surface: ProtocolSurface::OfficialApi,
107                method: HttpMethod::Get,
108                route: "/api/v0/enrich/web",
109                version: ApiVersion::V0,
110                parser: ParserShape::JsonEnvelope,
111                allowed_credential: CredentialKind::BotToken,
112            },
113            Self::OfficialEnrichNews => EndpointSpec {
114                name: "official.enrich_news",
115                surface: ProtocolSurface::OfficialApi,
116                method: HttpMethod::Get,
117                route: "/api/v0/enrich/news",
118                version: ApiVersion::V0,
119                parser: ParserShape::JsonEnvelope,
120                allowed_credential: CredentialKind::BotToken,
121            },
122            Self::OfficialSummarizeGet => EndpointSpec {
123                name: "official.summarize_get",
124                surface: ProtocolSurface::OfficialApi,
125                method: HttpMethod::Get,
126                route: "/api/v0/summarize",
127                version: ApiVersion::V0,
128                parser: ParserShape::JsonEnvelope,
129                allowed_credential: CredentialKind::BotToken,
130            },
131            Self::OfficialSummarizePost => EndpointSpec {
132                name: "official.summarize_post",
133                surface: ProtocolSurface::OfficialApi,
134                method: HttpMethod::Post,
135                route: "/api/v0/summarize",
136                version: ApiVersion::V0,
137                parser: ParserShape::JsonEnvelope,
138                allowed_credential: CredentialKind::BotToken,
139            },
140            Self::OfficialFastGpt => EndpointSpec {
141                name: "official.fastgpt",
142                surface: ProtocolSurface::OfficialApi,
143                method: HttpMethod::Post,
144                route: "/api/v0/fastgpt",
145                version: ApiVersion::V0,
146                parser: ParserShape::JsonEnvelope,
147                allowed_credential: CredentialKind::BotToken,
148            },
149            Self::OfficialSmallwebFeed => EndpointSpec {
150                name: "official.smallweb_feed",
151                surface: ProtocolSurface::OfficialApi,
152                method: HttpMethod::Get,
153                route: "/api/v1/smallweb/feed",
154                version: ApiVersion::V1,
155                parser: ParserShape::JsonEnvelope,
156                allowed_credential: CredentialKind::BotToken,
157            },
158            Self::SessionHtmlSearch => EndpointSpec {
159                name: "session.html_search",
160                surface: ProtocolSurface::SessionWeb,
161                method: HttpMethod::Get,
162                route: "/html/search",
163                version: ApiVersion::NotApplicable,
164                parser: ParserShape::Html,
165                allowed_credential: CredentialKind::SessionToken,
166            },
167            Self::SessionSummaryLabsGet => EndpointSpec {
168                name: "session.summary_labs_get",
169                surface: ProtocolSurface::SessionWeb,
170                method: HttpMethod::Get,
171                route: "/mother/summary_labs",
172                version: ApiVersion::NotApplicable,
173                parser: ParserShape::Stream,
174                allowed_credential: CredentialKind::SessionToken,
175            },
176            Self::SessionSummaryLabsPost => EndpointSpec {
177                name: "session.summary_labs_post",
178                surface: ProtocolSurface::SessionWeb,
179                method: HttpMethod::Post,
180                route: "/mother/summary_labs/",
181                version: ApiVersion::NotApplicable,
182                parser: ParserShape::Stream,
183                allowed_credential: CredentialKind::SessionToken,
184            },
185        }
186    }
187}
188
189#[cfg(test)]
190mod tests {
191    use super::{ApiVersion, EndpointId};
192
193    #[test]
194    fn official_endpoints_cover_both_v0_and_v1_routes() {
195        let v0 = EndpointId::OfficialSearch.spec();
196        let v1 = EndpointId::OfficialSmallwebFeed.spec();
197
198        assert_eq!(v0.version, ApiVersion::V0);
199        assert_eq!(v0.route, "/api/v0/search");
200        assert_eq!(v1.version, ApiVersion::V1);
201        assert_eq!(v1.route, "/api/v1/smallweb/feed");
202    }
203}