1use std::path::PathBuf;
2
3pub const RESERVED_NAMESPACES: [&str; 3] = ["state", "git", "openapi"];
4
5#[derive(Clone)]
6pub enum SpecSource {
7 Url(url::Url),
8 Path(PathBuf),
9}
10
11impl std::fmt::Debug for SpecSource {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 match self {
14 Self::Url(url) => f
15 .debug_tuple("Url")
16 .field(&crate::ssrf::redact_url(url.as_str()))
17 .finish(),
18 Self::Path(path) => f.debug_tuple("Path").field(path).finish(),
19 }
20 }
21}
22
23#[derive(Clone)]
24pub enum OpenApiCredential {
25 ApiKey { header: String, value: String },
26 BearerToken(String),
27}
28
29impl OpenApiCredential {
30 #[must_use]
31 pub fn api_key(header: impl Into<String>, value: impl Into<String>) -> Self {
32 Self::ApiKey {
33 header: header.into(),
34 value: value.into(),
35 }
36 }
37
38 #[must_use]
39 pub fn bearer_token(value: impl Into<String>) -> Self {
40 Self::BearerToken(value.into())
41 }
42}
43
44impl std::fmt::Debug for OpenApiCredential {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 Self::ApiKey { header, .. } => f
48 .debug_struct("ApiKey")
49 .field("header", header)
50 .field("value", &"<redacted>")
51 .finish(),
52 Self::BearerToken(_) => f.debug_tuple("BearerToken").field(&"<redacted>").finish(),
53 }
54 }
55}
56
57impl serde::Serialize for OpenApiCredential {
58 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
59 where
60 S: serde::Serializer,
61 {
62 use serde::ser::SerializeMap;
63
64 match self {
65 Self::ApiKey { header, .. } => {
66 let mut map = serializer.serialize_map(Some(3))?;
67 map.serialize_entry("type", "api_key")?;
68 map.serialize_entry("header", header)?;
69 map.serialize_entry("value", "<redacted>")?;
70 map.end()
71 }
72 Self::BearerToken(_) => {
73 let mut map = serializer.serialize_map(Some(2))?;
74 map.serialize_entry("type", "bearer_token")?;
75 map.serialize_entry("value", "<redacted>")?;
76 map.end()
77 }
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
83pub struct OpenApiSpecConfig {
84 pub label: String,
85 pub spec_source: SpecSource,
86 pub base_url: url::Url,
87 pub allowed_operations: Vec<String>,
88 pub credential: Option<OpenApiCredential>,
89}
90
91impl OpenApiSpecConfig {
92 #[must_use]
93 pub fn is_reserved_label(&self) -> bool {
94 RESERVED_NAMESPACES.contains(&self.label.as_str())
95 }
96}
97
98#[derive(Debug, Clone, Default)]
99pub struct OpenApiConfig {
100 pub specs: Vec<OpenApiSpecConfig>,
101}
102
103impl OpenApiConfig {
104 #[must_use]
105 pub fn empty() -> Self {
106 Self { specs: Vec::new() }
107 }
108}