1#![forbid(unsafe_code)]
2use std::{
8 future::Future,
9 pin::Pin,
10 sync::{
11 atomic::{AtomicBool, Ordering},
12 Arc,
13 },
14};
15
16use futures_core::Stream;
17use tokio::sync::Notify;
18
19mod builder;
20mod client;
21mod error;
22mod stream_json;
23
24pub use builder::GeminiCliClientBuilder;
25pub use client::GeminiCliClient;
26pub use error::GeminiCliError;
27pub use stream_json::{
28 parse_stream_json_lines, GeminiStreamJsonError, GeminiStreamJsonErrorCode,
29 GeminiStreamJsonEvent, GeminiStreamJsonLine, GeminiStreamJsonLineOutcome,
30 GeminiStreamJsonParser, GeminiStreamJsonResultPayload, GeminiStreamJsonRunRequest,
31 GeminiToolResultError,
32};
33
34pub type DynGeminiStreamJsonEventStream =
35 Pin<Box<dyn Stream<Item = Result<GeminiStreamJsonEvent, GeminiStreamJsonError>> + Send>>;
36
37pub type DynGeminiStreamJsonCompletion =
38 Pin<Box<dyn Future<Output = Result<GeminiStreamJsonCompletion, GeminiCliError>> + Send>>;
39
40#[derive(Clone)]
41pub struct GeminiTerminationHandle {
42 inner: Arc<GeminiTerminationInner>,
43}
44
45#[derive(Debug)]
46struct GeminiTerminationInner {
47 requested: AtomicBool,
48 notify: Notify,
49}
50
51impl GeminiTerminationHandle {
52 fn new() -> Self {
53 Self {
54 inner: Arc::new(GeminiTerminationInner {
55 requested: AtomicBool::new(false),
56 notify: Notify::new(),
57 }),
58 }
59 }
60
61 pub fn request_termination(&self) {
62 if !self.inner.requested.swap(true, Ordering::SeqCst) {
63 self.inner.notify.notify_waiters();
64 }
65 }
66
67 fn is_requested(&self) -> bool {
68 self.inner.requested.load(Ordering::SeqCst)
69 }
70
71 async fn requested(&self) {
72 if self.is_requested() {
73 return;
74 }
75
76 let notified = self.inner.notify.notified();
77 if self.is_requested() {
78 return;
79 }
80
81 notified.await;
82 }
83}
84
85impl std::fmt::Debug for GeminiTerminationHandle {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 f.debug_struct("GeminiTerminationHandle")
88 .field("requested", &self.is_requested())
89 .finish()
90 }
91}
92
93#[derive(Debug, Clone)]
94pub struct GeminiStreamJsonCompletion {
95 pub status: std::process::ExitStatus,
96 pub final_text: Option<String>,
97 pub session_id: Option<String>,
98 pub model: Option<String>,
99 pub raw_result: Option<serde_json::Value>,
100}
101
102pub struct GeminiStreamJsonHandle {
103 pub events: DynGeminiStreamJsonEventStream,
104 pub completion: DynGeminiStreamJsonCompletion,
105}
106
107impl std::fmt::Debug for GeminiStreamJsonHandle {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.debug_struct("GeminiStreamJsonHandle")
110 .field("events", &"<stream>")
111 .field("completion", &"<future>")
112 .finish()
113 }
114}
115
116pub struct GeminiStreamJsonControlHandle {
117 pub events: DynGeminiStreamJsonEventStream,
118 pub completion: DynGeminiStreamJsonCompletion,
119 pub termination: GeminiTerminationHandle,
120}
121
122impl std::fmt::Debug for GeminiStreamJsonControlHandle {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 f.debug_struct("GeminiStreamJsonControlHandle")
125 .field("events", &"<stream>")
126 .field("completion", &"<future>")
127 .field("termination", &self.termination)
128 .finish()
129 }
130}