Skip to main content

aider/
lib.rs

1#![forbid(unsafe_code)]
2//! Async helper around the aider CLI headless `--message-format stream-json` surface.
3
4use std::{
5    future::Future,
6    pin::Pin,
7    sync::{
8        atomic::{AtomicBool, Ordering},
9        Arc,
10    },
11};
12
13use futures_core::Stream;
14use tokio::sync::Notify;
15
16mod builder;
17mod client;
18mod error;
19mod stream_json;
20pub mod wrapper_coverage_manifest;
21
22pub use builder::AiderCliClientBuilder;
23pub use client::AiderCliClient;
24pub use error::AiderCliError;
25pub use stream_json::{
26    parse_stream_json_lines, AiderStreamJsonError, AiderStreamJsonErrorCode, AiderStreamJsonEvent,
27    AiderStreamJsonLine, AiderStreamJsonLineOutcome, AiderStreamJsonParser,
28    AiderStreamJsonResultPayload, AiderStreamJsonRunRequest, AiderToolResultError,
29};
30
31pub type DynAiderStreamJsonEventStream =
32    Pin<Box<dyn Stream<Item = Result<AiderStreamJsonEvent, AiderStreamJsonError>> + Send>>;
33
34pub type DynAiderStreamJsonCompletion =
35    Pin<Box<dyn Future<Output = Result<AiderStreamJsonCompletion, AiderCliError>> + Send>>;
36
37#[derive(Clone)]
38pub struct AiderTerminationHandle {
39    inner: Arc<AiderTerminationInner>,
40}
41
42#[derive(Debug)]
43struct AiderTerminationInner {
44    requested: AtomicBool,
45    notify: Notify,
46}
47
48impl AiderTerminationHandle {
49    fn new() -> Self {
50        Self {
51            inner: Arc::new(AiderTerminationInner {
52                requested: AtomicBool::new(false),
53                notify: Notify::new(),
54            }),
55        }
56    }
57
58    pub fn request_termination(&self) {
59        if !self.inner.requested.swap(true, Ordering::SeqCst) {
60            self.inner.notify.notify_waiters();
61        }
62    }
63
64    fn is_requested(&self) -> bool {
65        self.inner.requested.load(Ordering::SeqCst)
66    }
67
68    async fn requested(&self) {
69        if self.is_requested() {
70            return;
71        }
72
73        let notified = self.inner.notify.notified();
74        if self.is_requested() {
75            return;
76        }
77
78        notified.await;
79    }
80}
81
82impl std::fmt::Debug for AiderTerminationHandle {
83    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        f.debug_struct("AiderTerminationHandle")
85            .field("requested", &self.is_requested())
86            .finish()
87    }
88}
89
90#[derive(Debug, Clone)]
91pub struct AiderStreamJsonCompletion {
92    pub status: std::process::ExitStatus,
93    pub final_text: Option<String>,
94    pub session_id: Option<String>,
95    pub model: Option<String>,
96    pub raw_result: Option<serde_json::Value>,
97}
98
99pub struct AiderStreamJsonHandle {
100    pub events: DynAiderStreamJsonEventStream,
101    pub completion: DynAiderStreamJsonCompletion,
102}
103
104impl std::fmt::Debug for AiderStreamJsonHandle {
105    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106        f.debug_struct("AiderStreamJsonHandle")
107            .field("events", &"<stream>")
108            .field("completion", &"<future>")
109            .finish()
110    }
111}
112
113pub struct AiderStreamJsonControlHandle {
114    pub events: DynAiderStreamJsonEventStream,
115    pub completion: DynAiderStreamJsonCompletion,
116    pub termination: AiderTerminationHandle,
117}
118
119impl std::fmt::Debug for AiderStreamJsonControlHandle {
120    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
121        f.debug_struct("AiderStreamJsonControlHandle")
122            .field("events", &"<stream>")
123            .field("completion", &"<future>")
124            .field("termination", &self.termination)
125            .finish()
126    }
127}