Skip to main content

refrain_adapters/
lib.rs

1//! refrain-adapters: pluggable output adapters for refrains.
2//!
3//! Defines the `RefrainAdapter` trait and provides built-in implementations
4//! for audio (Strudel JSON / OSC), visual (wgpu/skia), code-rewrite (text
5//! template), and text (n-gram). Built-ins are populated in Phases 6-9.
6
7pub mod audio;
8pub mod code;
9pub mod schedule;
10pub mod text;
11pub mod visual;
12
13pub use audio::{AudioAdapter, AudioFormat};
14pub use code::{CodeAdapter, CodeLang};
15pub use schedule::{schedule, Hap};
16pub use text::{TextAdapter, TextStyle};
17pub use visual::VisualAdapter;
18
19use refrain_core::Refrain;
20use thiserror::Error;
21
22#[derive(Debug, Error)]
23pub enum AdapterErr {
24    #[error("adapter not implemented yet: {0}")]
25    NotImplemented(String),
26    #[error("encoding error: {0}")]
27    Encoding(String),
28}
29
30#[derive(Debug, Clone, Default)]
31pub struct AdapterCaps {
32    pub realtime: bool,
33    pub differentiable: bool,
34}
35
36#[derive(Debug, Clone, Default)]
37pub struct EmitCtx {
38    pub sample_rate: Option<u32>,
39    pub frame_count: Option<u32>,
40}
41
42pub struct ExtractedRefrain<'a> {
43    pub refrain: &'a Refrain,
44}
45
46pub trait RefrainAdapter: Send + Sync {
47    fn name(&self) -> &str;
48    fn emit(&self, refrain: &ExtractedRefrain, ctx: &EmitCtx) -> Result<Vec<u8>, AdapterErr>;
49    fn capabilities(&self) -> AdapterCaps;
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    struct DummyAdapter;
57    impl RefrainAdapter for DummyAdapter {
58        fn name(&self) -> &str {
59            "dummy"
60        }
61        fn emit(&self, _r: &ExtractedRefrain, _ctx: &EmitCtx) -> Result<Vec<u8>, AdapterErr> {
62            Ok(vec![])
63        }
64        fn capabilities(&self) -> AdapterCaps {
65            AdapterCaps::default()
66        }
67    }
68
69    #[test]
70    fn dummy_adapter_emits_empty() {
71        let r = Refrain::new("dummy-host");
72        let ex = ExtractedRefrain { refrain: &r };
73        let bytes = DummyAdapter.emit(&ex, &EmitCtx::default()).unwrap();
74        assert!(bytes.is_empty());
75        assert_eq!(DummyAdapter.name(), "dummy");
76    }
77}