mod annotation;
mod externals;
mod program_details;
use {
crate::{
endpoint::{EndpointHandle, EndpointInfo},
ffi::EnginePtr,
performer::{Endpoint, EndpointError, EndpointType, OutputEvent, Performer},
program::Program,
},
std::{
borrow::Cow,
collections::HashMap,
ffi::{CStr, CString},
slice::Split,
},
};
pub use {annotation::Annotation, externals::Externals, program_details::ProgramDetails};
pub struct EngineTypes<'a> {
engine_types: Split<'a, u8, fn(&u8) -> bool>,
}
impl<'a> EngineTypes<'a> {
pub(crate) fn new(engine_types: &'a CStr) -> Self {
Self {
engine_types: engine_types.to_bytes().split(|&b| b == b' '),
}
}
}
impl<'a> Iterator for EngineTypes<'a> {
type Item = EngineType;
fn next(&mut self) -> Option<Self::Item> {
self.engine_types
.next()
.map(String::from_utf8_lossy)
.map(Cow::into_owned)
.map(EngineType)
}
}
#[derive(Clone)]
pub struct EngineType(String);
impl EngineType {
pub(crate) fn to_str(&self) -> &str {
&self.0
}
pub(crate) fn default_engine_type() -> Self {
Self(String::new())
}
}
impl PartialEq<str> for EngineType {
fn eq(&self, other: &str) -> bool {
self.0 == other
}
}
impl std::fmt::Debug for EngineType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
pub struct EngineBuilder {
pub(crate) sample_rate: f64,
pub(crate) engine: Engine<Idle>,
}
impl EngineBuilder {
pub fn with_sample_rate(mut self, sample_rate: f64) -> Self {
self.sample_rate = sample_rate;
self
}
pub fn build(self) -> Engine {
let Self {
sample_rate,
engine,
} = self;
let build_settings = CString::new(
serde_json::json!(
{
"frequency": sample_rate
}
)
.to_string(),
)
.expect("failed to convert build settings to C string");
engine.inner.set_build_settings(build_settings.as_c_str());
engine
}
}
#[derive(Debug)]
pub struct Engine<State = Idle> {
inner: EnginePtr,
state: State,
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("Failed to load program: {:#?}", .0)]
FailedToLoad(Engine<Idle>, String),
#[error("Failed to link program: {:#?}", .0)]
FailedToLink(Engine<Loaded>, String),
}
#[doc(hidden)]
#[derive(Debug)]
pub struct Idle;
#[doc(hidden)]
#[derive(Debug)]
pub struct Loaded {
program_details: ProgramDetails,
endpoints: HashMap<EndpointHandle, EndpointInfo>,
console: Option<Endpoint<OutputEvent>>,
}
#[doc(hidden)]
#[derive(Debug)]
pub struct Linked {
endpoints: HashMap<EndpointHandle, EndpointInfo>,
console: Option<Endpoint<OutputEvent>>,
}
impl Engine<Idle> {
pub(crate) fn new(engine: EnginePtr) -> Self {
Self {
inner: engine,
state: Idle,
}
}
pub fn load(self, program: &Program) -> Result<Engine<Loaded>, Error> {
self.load_with_externals(program, Externals::default())
}
pub fn load_with_externals(
self,
program: &Program,
externals: Externals,
) -> Result<Engine<Loaded>, Error> {
match self.inner.load(&program.inner, externals) {
Ok(_) => {
let program_details = self
.inner
.program_details()
.expect("failed to get program details");
let program_details = program_details.to_string();
let program_details = serde_json::from_str(program_details.as_ref())
.expect("failed to parse program details");
let mut loaded = Engine {
inner: self.inner,
state: Loaded {
program_details,
endpoints: HashMap::default(),
console: None,
},
};
loaded.state.console = loaded.endpoint("console").ok();
Ok(loaded)
}
Err(error) => Err(Error::FailedToLoad(self, error.to_string().into_owned())),
}
}
}
impl Engine<Loaded> {
pub fn endpoint<T>(&mut self, id: impl AsRef<str>) -> Result<Endpoint<T>, EndpointError>
where
T: EndpointType,
{
let id = id.as_ref();
let info = self
.state
.program_details
.endpoints()
.find(|endpoint| endpoint.id() == id)
.ok_or(EndpointError::EndpointDoesNotExist)?;
let id = CString::new(id).expect("invalid endpoint id");
let handle = self
.inner
.get_endpoint_handle(id.as_c_str())
.ok_or(EndpointError::EndpointDoesNotExist)?;
self.state.endpoints.insert(handle, info.clone());
EndpointType::make(handle, info)
}
pub fn program_details(&self) -> &ProgramDetails {
&self.state.program_details
}
pub fn link(self) -> Result<Engine<Linked>, Error> {
match self.inner.link() {
Ok(_) => {
let linked = Linked {
endpoints: self.state.endpoints,
console: self.state.console,
};
Ok(Engine {
inner: self.inner,
state: linked,
})
}
Err(error) => Err(Error::FailedToLink(self, error.to_string().into_owned())),
}
}
}
impl Engine<Linked> {
pub fn performer(&self) -> Performer {
Performer::new(
self.inner.create_performer(),
self.state.endpoints.clone(),
self.state.console,
)
}
}
impl<T> Engine<T> {
pub fn unload(self) -> Engine<Idle> {
self.inner.unload();
Engine {
inner: self.inner,
state: Idle,
}
}
}