hemmer_provider_sdk/lib.rs
1//! Hemmer Provider SDK
2//!
3//! This crate provides the gRPC protocol types and server helpers for building
4//! Hemmer providers.
5//!
6//! # Overview
7//!
8//! The SDK provides:
9//!
10//! - **Protocol Buffers types**: Pre-compiled Rust types from the canonical provider protocol
11//! - **Schema types**: Types for describing provider, resource, and data source schemas
12//! - **ProviderService trait**: A high-level trait that providers implement
13//! - **Server helpers**: Functions to start a gRPC server with the handshake protocol
14//! - **Error types**: Common error types for provider implementations
15//! - **Logging**: Integration with `tracing` for structured logging
16//!
17//! # Quick Start
18//!
19//! ```ignore
20//! use hemmer_provider_sdk::{
21//! serve, ProviderService, ProviderError, PlanResult,
22//! schema::{ProviderSchema, Schema, Attribute, Diagnostic},
23//! };
24//!
25//! struct MyProvider;
26//!
27//! #[async_trait::async_trait]
28//! impl ProviderService for MyProvider {
29//! fn schema(&self) -> ProviderSchema {
30//! ProviderSchema::new()
31//! .with_resource("example_resource", Schema::v0()
32//! .with_attribute("name", Attribute::required_string())
33//! .with_attribute("id", Attribute::computed_string()))
34//! }
35//!
36//! async fn configure(
37//! &self,
38//! config: serde_json::Value,
39//! ) -> Result<Vec<Diagnostic>, ProviderError> {
40//! Ok(vec![])
41//! }
42//!
43//! async fn plan(
44//! &self,
45//! resource_type: &str,
46//! prior_state: Option<serde_json::Value>,
47//! proposed_state: serde_json::Value,
48//! config: serde_json::Value,
49//! ) -> Result<PlanResult, ProviderError> {
50//! Ok(PlanResult::no_change(proposed_state))
51//! }
52//!
53//! async fn create(
54//! &self,
55//! resource_type: &str,
56//! planned_state: serde_json::Value,
57//! ) -> Result<serde_json::Value, ProviderError> {
58//! Ok(planned_state)
59//! }
60//!
61//! async fn read(
62//! &self,
63//! resource_type: &str,
64//! current_state: serde_json::Value,
65//! ) -> Result<serde_json::Value, ProviderError> {
66//! Ok(current_state)
67//! }
68//!
69//! async fn update(
70//! &self,
71//! resource_type: &str,
72//! prior_state: serde_json::Value,
73//! planned_state: serde_json::Value,
74//! ) -> Result<serde_json::Value, ProviderError> {
75//! Ok(planned_state)
76//! }
77//!
78//! async fn delete(
79//! &self,
80//! resource_type: &str,
81//! current_state: serde_json::Value,
82//! ) -> Result<(), ProviderError> {
83//! Ok(())
84//! }
85//! }
86//!
87//! #[tokio::main]
88//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
89//! let provider = MyProvider;
90//! serve(provider).await
91//! }
92//! ```
93//!
94//! # Handshake Protocol
95//!
96//! When a provider starts via [`serve`], it outputs a handshake string to stdout:
97//!
98//! ```text
99//! HEMMER_PROVIDER|1|127.0.0.1:50051
100//! ```
101//!
102//! Format: `HEMMER_PROVIDER|<protocol_version>|<address>`
103//!
104//! This allows Hemmer to spawn the provider as a subprocess and connect via gRPC.
105//!
106//! # Provider Protocol
107//!
108//! The SDK implements a complete provider protocol with the following RPCs:
109//!
110//! - **GetMetadata**: Returns provider capabilities and resource/data source names
111//! - **GetSchema**: Returns full schema for provider config, resources, and data sources
112//! - **ValidateProviderConfig**: Validates provider configuration
113//! - **Configure**: Configures the provider with credentials
114//! - **Stop**: Gracefully shuts down the provider
115//! - **ValidateResourceConfig**: Validates resource configuration
116//! - **UpgradeResourceState**: Migrates state from older schema versions
117//! - **Plan**: Calculates required changes
118//! - **Create/Read/Update/Delete**: CRUD operations for resources
119//! - **ImportResourceState**: Imports existing infrastructure
120//! - **ValidateDataSourceConfig**: Validates data source configuration
121//! - **ReadDataSource**: Reads data from external sources
122
123#![warn(missing_docs)]
124#![warn(clippy::all)]
125
126pub mod error;
127pub mod logging;
128pub mod schema;
129pub mod server;
130pub mod testing;
131pub mod types;
132pub mod validation;
133
134#[allow(missing_docs)]
135#[allow(clippy::all)]
136pub mod generated;
137
138// Re-export main types at crate root
139pub use error::ProviderError;
140pub use logging::{init_logging, init_logging_with_default, try_init_logging};
141pub use schema::ProviderSchema;
142pub use server::{
143 serve, serve_on, serve_on_with_options, serve_with_options, ProviderService, ServeOptions,
144};
145pub use types::{
146 check_protocol_version, AttributeChange, ImportedResource, PlanResult, ProviderMetadata,
147 ServerCapabilities, HANDSHAKE_PREFIX, MIN_PROTOCOL_VERSION, PROTOCOL_VERSION,
148};
149pub use validation::{is_valid, validate, validate_result};
150
151/// A convenience type alias for Results with [`ProviderError`].
152///
153/// This allows provider implementations to use `Result<T>` instead of
154/// `Result<T, ProviderError>` for brevity.
155///
156/// # Examples
157///
158/// ```
159/// use hemmer_provider_sdk::{Result, ProviderError};
160///
161/// fn do_something() -> Result<String> {
162/// Ok("success".to_string())
163/// }
164///
165/// fn do_something_else() -> Result<i32> {
166/// Err(ProviderError::NotFound("not found".to_string()))
167/// }
168/// ```
169pub type Result<T> = std::result::Result<T, ProviderError>;
170
171// Re-export testing utilities
172pub use testing::{ProviderTester, TestError};
173
174// Re-export async_trait for convenience
175pub use async_trait::async_trait;
176
177// Re-export commonly used external types
178pub use serde_json;
179pub use tonic;
180pub use tracing;