|
| 1 | +//! Runtime infrastructure for sqlc plugin execution. |
| 2 | +//! |
| 3 | +//! This module provides the core runtime functions for sqlc plugins, handling |
| 4 | +//! the communication protocol between sqlc and the plugin. It reads protobuf-encoded |
| 5 | +//! requests from stdin, processes them through a user-provided handler, and writes |
| 6 | +//! protobuf-encoded responses to stdout. |
| 7 | +//! |
| 8 | +//! # Overview |
| 9 | +//! |
| 10 | +//! The runtime handles: |
| 11 | +//! - Reading and decoding protobuf messages from stdin |
| 12 | +//! - Invoking user-defined code generation logic |
| 13 | +//! - Encoding and writing responses back to stdout |
| 14 | +//! - Error propagation and handling |
| 15 | +//! |
| 16 | +//! # Example |
| 17 | +//! |
| 18 | +//! ```no_run |
| 19 | +//! use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File}; |
| 20 | +//! use sqlc_gen_core::runtime::run; |
| 21 | +//! |
| 22 | +//! fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 23 | +//! run(|request| { |
| 24 | +//! // Your code generation logic here |
| 25 | +//! let files = vec![File { |
| 26 | +//! name: "output.rs".to_string(), |
| 27 | +//! contents: b"// Generated code".to_vec(), |
| 28 | +//! }]; |
| 29 | +//! |
| 30 | +//! Ok(GenerateResponse { files }) |
| 31 | +//! }) |
| 32 | +//! } |
| 33 | +//! ``` |
| 34 | +
|
1 | 35 | use crate::plugin::{GenerateRequest, GenerateResponse}; |
2 | 36 | use prost::Message; |
3 | 37 | use std::error::Error; |
4 | 38 | use std::io::{Read, Write}; |
5 | 39 |
|
6 | | -pub fn run<TFunc>(process: TFunc) -> Result<(), Box<dyn Error>> |
| 40 | +/// Runs a sqlc plugin with the standard stdin/stdout communication protocol. |
| 41 | +/// |
| 42 | +/// This is the main entry point for sqlc plugins. It reads a protobuf-encoded |
| 43 | +/// [`GenerateRequest`] from stdin, passes it to your processing function, and |
| 44 | +/// writes the resulting [`GenerateResponse`] back to stdout. |
| 45 | +/// |
| 46 | +/// # Arguments |
| 47 | +/// |
| 48 | +/// * `process` - A function that takes a [`GenerateRequest`] and returns a |
| 49 | +/// [`GenerateResponse`]. This is where your code generation logic should live. |
| 50 | +/// |
| 51 | +/// # Errors |
| 52 | +/// |
| 53 | +/// Returns an error if: |
| 54 | +/// - Reading from stdin fails |
| 55 | +/// - Decoding the protobuf request fails |
| 56 | +/// - The process function returns an error |
| 57 | +/// - Encoding the response fails |
| 58 | +/// - Writing to stdout fails |
| 59 | +/// |
| 60 | +/// # Example |
| 61 | +/// |
| 62 | +/// ```no_run |
| 63 | +/// use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File}; |
| 64 | +/// use sqlc_gen_core::runtime::run; |
| 65 | +/// |
| 66 | +/// fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 67 | +/// run(|request| { |
| 68 | +/// // Access request data |
| 69 | +/// let version = &request.sqlc_version; |
| 70 | +/// let queries = &request.queries; |
| 71 | +/// |
| 72 | +/// // Generate files based on the request |
| 73 | +/// let files = vec![File { |
| 74 | +/// name: "generated.rs".to_string(), |
| 75 | +/// contents: b"// Generated code".to_vec(), |
| 76 | +/// }]; |
| 77 | +/// |
| 78 | +/// Ok(GenerateResponse { files }) |
| 79 | +/// }) |
| 80 | +/// } |
| 81 | +/// ``` |
| 82 | +pub fn run<F>(process: F) -> Result<(), Box<dyn Error>> |
7 | 83 | where |
8 | | - TFunc: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>, |
| 84 | + F: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>, |
9 | 85 | { |
10 | 86 | let stdin = std::io::stdin(); |
11 | 87 | let stdout = std::io::stdout(); |
12 | 88 | run_with_io(stdin.lock(), stdout.lock(), process) |
13 | 89 | } |
14 | 90 |
|
15 | | -pub fn run_with_io<TReader, TWriter, TFunc>( |
16 | | - mut reader: TReader, |
17 | | - mut writer: TWriter, |
18 | | - process: TFunc, |
19 | | -) -> Result<(), Box<dyn Error>> |
| 91 | +/// Runs a sqlc plugin with custom I/O streams. |
| 92 | +/// |
| 93 | +/// This is a more flexible version of [`run`] that allows you to provide custom |
| 94 | +/// readers and writers instead of using stdin/stdout. This is particularly useful |
| 95 | +/// for testing, as it allows you to pass in-memory buffers instead of actual I/O streams. |
| 96 | +/// |
| 97 | +/// # Arguments |
| 98 | +/// |
| 99 | +/// * `reader` - An input stream containing the protobuf-encoded [`GenerateRequest`] |
| 100 | +/// * `writer` - An output stream where the protobuf-encoded [`GenerateResponse`] will be written |
| 101 | +/// * `process` - A function that takes a [`GenerateRequest`] and returns a [`GenerateResponse`] |
| 102 | +/// |
| 103 | +/// # Type Parameters |
| 104 | +/// |
| 105 | +/// * `R` - Any type that implements [`Read`] |
| 106 | +/// * `W` - Any type that implements [`Write`] |
| 107 | +/// * `F` - A closure that processes the request and returns a response |
| 108 | +/// |
| 109 | +/// # Errors |
| 110 | +/// |
| 111 | +/// Returns an error if: |
| 112 | +/// - Reading from the input stream fails |
| 113 | +/// - Decoding the protobuf request fails |
| 114 | +/// - The process function returns an error |
| 115 | +/// - Encoding the response fails |
| 116 | +/// - Writing to the output stream fails |
| 117 | +/// |
| 118 | +/// # Example |
| 119 | +/// |
| 120 | +/// ``` |
| 121 | +/// use sqlc_gen_core::plugin::{GenerateRequest, GenerateResponse, File}; |
| 122 | +/// use sqlc_gen_core::runtime::run_with_io; |
| 123 | +/// use prost::Message; |
| 124 | +/// |
| 125 | +/// // Create a test request |
| 126 | +/// let request = GenerateRequest { |
| 127 | +/// sqlc_version: "1.0.0".to_string(), |
| 128 | +/// settings: None, |
| 129 | +/// catalog: None, |
| 130 | +/// queries: vec![], |
| 131 | +/// plugin_options: vec![], |
| 132 | +/// global_options: vec![], |
| 133 | +/// }; |
| 134 | +/// |
| 135 | +/// // Encode it |
| 136 | +/// let mut input = Vec::new(); |
| 137 | +/// request.encode(&mut input).unwrap(); |
| 138 | +/// |
| 139 | +/// // Process it |
| 140 | +/// let mut output = Vec::new(); |
| 141 | +/// run_with_io(&input[..], &mut output, |req| { |
| 142 | +/// assert_eq!(req.sqlc_version, "1.0.0"); |
| 143 | +/// Ok(GenerateResponse { files: vec![] }) |
| 144 | +/// }).unwrap(); |
| 145 | +/// |
| 146 | +/// // Decode the response |
| 147 | +/// let response = GenerateResponse::decode(&output[..]).unwrap(); |
| 148 | +/// assert_eq!(response.files.len(), 0); |
| 149 | +/// ``` |
| 150 | +pub fn run_with_io<R, W, F>(mut reader: R, mut writer: W, process: F) -> Result<(), Box<dyn Error>> |
20 | 151 | where |
21 | | - TReader: Read, |
22 | | - TWriter: Write, |
23 | | - TFunc: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>, |
| 152 | + R: Read, |
| 153 | + W: Write, |
| 154 | + F: FnOnce(GenerateRequest) -> Result<GenerateResponse, Box<dyn Error>>, |
24 | 155 | { |
25 | 156 | let mut input = Vec::new(); |
26 | 157 | reader.read_to_end(&mut input)?; |
|
0 commit comments