greentic_secrets_api/
lib.rs1#![cfg_attr(not(feature = "std"), no_std)]
2
3#[cfg(not(feature = "std"))]
4extern crate alloc;
5
6#[cfg(not(feature = "std"))]
7use alloc::{string::String, vec::Vec};
8#[cfg(feature = "std")]
9use std::{string::String, vec::Vec};
10
11pub mod value {
12 use super::String;
13 #[cfg(not(feature = "std"))]
14 use core::cmp;
15 #[cfg(feature = "std")]
16 use std::cmp;
17
18 #[derive(Clone, Debug, PartialEq, Eq)]
19 pub struct SecretValue(pub String);
20
21 impl SecretValue {
22 pub fn into_string(self) -> String {
23 self.0
24 }
25
26 pub fn redact_preview(&self) -> String {
27 let n = self.0.len();
28 match n {
29 0..=4 => "****".into(),
30 _ => {
31 let prefix = cmp::min(4, n);
32 format!("{}****", &self.0[..prefix])
33 }
34 }
35 }
36 }
37}
38
39pub mod error {
40 use super::String;
41 use thiserror::Error;
42
43 #[derive(Error, Debug)]
44 pub enum SecretsError {
45 #[error("secret not found: {0}")]
46 NotFound(String),
47 #[error("backend error: {0}")]
48 Backend(String),
49 #[error("invalid secret value for {0}: {1}")]
50 Invalid(String, String),
51 }
52
53 pub type Result<T> = core::result::Result<T, SecretsError>;
54}
55
56pub mod spec {
57 use super::{
58 error::{Result, SecretsError},
59 String, Vec,
60 };
61
62 pub struct SecretSpec {
63 pub name: String,
64 pub description: Option<String>,
65 pub required: bool,
66 validator: Option<fn(&str) -> bool>,
67 }
68
69 impl SecretSpec {
70 pub fn new<N: Into<String>>(name: N) -> Self {
71 Self {
72 name: name.into(),
73 description: None,
74 required: false,
75 validator: None,
76 }
77 }
78
79 pub fn description<D: Into<String>>(mut self, description: D) -> Self {
80 self.description = Some(description.into());
81 self
82 }
83
84 pub fn required(mut self) -> Self {
85 self.required = true;
86 self
87 }
88
89 pub fn validator(mut self, f: fn(&str) -> bool) -> Self {
90 self.validator = Some(f);
91 self
92 }
93
94 pub fn validate(&self, value: &str) -> Result<()> {
95 if let Some(f) = self.validator {
96 if !f(value) {
97 return Err(SecretsError::Invalid(
98 self.name.clone(),
99 "validator failed".into(),
100 ));
101 }
102 }
103 Ok(())
104 }
105 }
106
107 #[derive(Default)]
108 pub struct SecretSpecRegistry {
109 specs: Vec<SecretSpec>,
110 }
111
112 impl SecretSpecRegistry {
113 pub fn register(&mut self, spec: SecretSpec) {
114 self.specs.push(spec);
115 }
116
117 pub fn validate_value(&self, name: &str, value: &str) -> Result<()> {
118 if let Some(spec) = self.specs.iter().find(|s| s.name == name) {
119 spec.validate(value)
120 } else {
121 Ok(())
122 }
123 }
124
125 pub fn to_markdown(&self) -> String {
126 let mut md = String::from("| Name | Description | Required |\n|---|---|---|\n");
127 for spec in &self.specs {
128 md.push_str(&format!(
129 "| {} | {} | {} |\n",
130 spec.name,
131 spec.description.clone().unwrap_or_default(),
132 if spec.required { "yes" } else { "no" }
133 ));
134 }
135 md
136 }
137 }
138}
139
140pub mod backend {
141 use super::{error::Result, value::SecretValue};
142
143 pub trait SecretsBackend: Send + Sync + 'static {
145 fn get(&self, key: &str) -> Result<SecretValue>;
147 }
148}
149
150pub use backend::SecretsBackend;
151pub use error::{Result, SecretsError};
152pub use spec::{SecretSpec, SecretSpecRegistry};
153pub use value::SecretValue;