From f4d743260efee3450a043d5168c9737399b056e1 Mon Sep 17 00:00:00 2001 From: Kai Schmidt Date: Thu, 21 Dec 2023 12:37:19 -0800 Subject: [PATCH] make way more features optional --- Cargo.toml | 29 +++-- features.ua | 11 +- site/Cargo.toml | 2 +- src/lib.rs | 8 +- src/lsp.rs | 4 +- src/primitive/mod.rs | 3 +- src/run.rs | 7 +- src/sys.rs | 300 +++++++++++++++++++++++++------------------ src/sys_native.rs | 6 +- 9 files changed, 223 insertions(+), 147 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b479aabeb..35eb181b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,9 +19,6 @@ crossbeam-channel = "0.5.9" dashmap = {version = "5", features = ["serde"]} ecow = {version = "0.2.0", features = ["serde"]} enum-iterator = "1.4.1" -gif = "0.12.0" -hound = "3" -image = {version = "0.24.5", features = ["bmp", "gif", "ico", "jpeg", "png"]} instant = "0.1.12" num_cpus = "1.16.0" once_cell = "1" @@ -69,16 +66,21 @@ indexmap = {version = "2", optional = true, features = ["serde"]} serde_tuple = "0.5.0" serde_yaml = {version = "0.9.27", optional = true} +# Feature dependencies +gif = {version = "0.12.0", optional = true} +hound = {version = "3", optional = true} +image = {version = "0.24.5", optional = true, default-features = false, features = ["bmp", "gif", "ico", "jpeg", "png"]} + [features] -audio = ["hodaun", "lockfree"] +audio = ["hodaun", "lockfree", "audio_encode"] +audio_encode = ["hound"] binary = [ "ctrlc", "notify", "clap", "color-backtrace", - "lsp", "rustyline", - "stand", + "native_sys", ] bytes = [] debug = [] @@ -89,15 +91,22 @@ default = [ "https", "invoke", "trash", + "lsp", "raw_mode", + "gif", + "stand", + "image", + "native_sys", ] +gif = ["dep:gif", "image"] https = ["httparse", "rustls", "webpki-roots"] invoke = ["open"] -lsp = ["tower-lsp", "tokio"] +lsp = ["tower-lsp", "tokio", "native_sys"] +native_sys = [] profile = ["serde_yaml", "indexmap"] -raw_mode = ["crossterm"] -stand = [] -terminal_image = ["viuer"] +raw_mode = ["crossterm", "native_sys"] +stand = ["native_sys"] +terminal_image = ["viuer", "image"] [[bin]] name = "uiua" diff --git a/features.ua b/features.ua index 4ecea0273..3106e66ec 100644 --- a/features.ua +++ b/features.ua @@ -1,13 +1,18 @@ # Test combinations of features Command ← {"cargo" "c" "--lib" "--no-default-features"} +Features ← ( + {"audio" "bytes" "https"} + ⊂( + {"gif,image,terminal_image" "lsp,raw_mode"} + | {"audio_encode" "gif" "image" "terminal_image" "lsp" "native_sys" "raw_mode"} + ) ∊□"all" &args +) &p "Checking with no features" ⍤⊃⋅∘≍ 0 &runi Command -# Features to try -{"bytes" "lsp" "terminal_image" "raw_mode" "audio"} -↘1⋯⇡ⁿ:2⧻. +↘1⋯⇡ⁿ:2⧻. Features ≡( ⊐/$"_,_" ▽ &p $"Checking with features: _". diff --git a/site/Cargo.toml b/site/Cargo.toml index 19e3b8d26..c71b8dee1 100644 --- a/site/Cargo.toml +++ b/site/Cargo.toml @@ -16,7 +16,7 @@ leptos = "0.5.0" leptos_meta = {version = "0.5.4", features = ["csr"]} leptos_router = {version = "0.5.4", features = ["csr"]} pathdiff = "0.2.1" -uiua = {path = "..", default-features = false, features = ["bytes"]} +uiua = {path = "..", default-features = false, features = ["bytes", "image", "gif", "audio_encode"]} urlencoding = "2" wasm-bindgen = "0.2.89" wasm-bindgen-futures = "0.4.39" diff --git a/src/lib.rs b/src/lib.rs index 1cf811069..9ed4e9b2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -123,6 +123,7 @@ mod run; #[doc(hidden)] pub mod stand; mod sys; +#[cfg(feature = "native_sys")] mod sys_native; mod value; @@ -141,10 +142,12 @@ pub use self::{ primitive::*, run::*, sys::*, - sys_native::*, value::*, }; +#[cfg(feature = "native_sys")] +pub use self::sys_native::*; + pub use complex::*; use ecow::EcoString; @@ -156,9 +159,10 @@ pub type Ident = EcoString; #[cfg(test)] mod tests { - use super::*; #[test] + #[cfg(feature = "native_sys")] fn suite() { + use super::*; for entry in std::fs::read_dir("tests").unwrap() { let entry = entry.unwrap(); let path = entry.path(); diff --git a/src/lsp.rs b/src/lsp.rs index 467d302c1..8d17fe222 100644 --- a/src/lsp.rs +++ b/src/lsp.rs @@ -194,7 +194,7 @@ mod server { format::{format_str, FormatConfig}, lex::Loc, primitive::{PrimClass, PrimDocFragment}, - Assembly, BindingInfo, Compiler, NativeSys, PrimDocLine, Uiua, + Assembly, BindingInfo, Compiler, PrimDocLine, Uiua, }; pub struct LspDoc { @@ -465,7 +465,7 @@ mod server { match format_str( &doc.input, &FormatConfig { - backend: Arc::new(NativeSys), + backend: Arc::new(crate::NativeSys), ..FormatConfig::find().unwrap_or_default() }, ) { diff --git a/src/primitive/mod.rs b/src/primitive/mod.rs index 40a6da0cd..7600f572c 100644 --- a/src/primitive/mod.rs +++ b/src/primitive/mod.rs @@ -1171,7 +1171,7 @@ impl PrimExample { /// Get the example's output pub fn output(&self) -> &Result, String> { self.output.get_or_init(|| { - let env = &mut Uiua::with_native_sys(); + let mut env = Uiua::with_safe_sys(); match env.run_str(&self.input) { Ok(_) => Ok(env.take_stack().into_iter().map(|val| val.show()).collect()), Err(e) => Err(e @@ -1333,6 +1333,7 @@ mod tests { } #[test] + #[cfg(feature = "native_sys")] fn prim_docs() { for prim in Primitive::non_deprecated() { for line in &prim.doc().lines { diff --git a/src/run.rs b/src/run.rs index 01133e482..3cd2ed52a 100644 --- a/src/run.rs +++ b/src/run.rs @@ -18,8 +18,8 @@ use thread_local::ThreadLocal; use crate::{ algorithm, array::Array, boxed::Boxed, check::instrs_temp_signatures, function::*, lex::Span, - value::Value, Assembly, Compiler, Complex, Global, Ident, Inputs, IntoSysBackend, NativeSys, - Primitive, SafeSys, SysBackend, SysOp, TraceFrame, UiuaError, UiuaResult, + value::Value, Assembly, Compiler, Complex, Global, Ident, Inputs, IntoSysBackend, Primitive, + SafeSys, SysBackend, SysOp, TraceFrame, UiuaError, UiuaResult, }; /// The Uiua interpreter @@ -209,8 +209,9 @@ impl Default for Runtime { impl Uiua { /// Create a new Uiua runtime with the standard IO backend + #[cfg(feature = "native_sys")] pub fn with_native_sys() -> Self { - Self::with_backend(NativeSys) + Self::with_backend(crate::NativeSys) } /// Create a new Uiua runtime with no IO capabilities pub fn with_safe_sys() -> Self { diff --git a/src/sys.rs b/src/sys.rs index 698fc98d4..759e4b200 100644 --- a/src/sys.rs +++ b/src/sys.rs @@ -1,30 +1,23 @@ use std::{ any::Any, - collections::{HashMap, HashSet}, fmt, - io::{stderr, stdin, Cursor, Read, Write}, + io::{stderr, stdin, Read, Write}, path::Path, sync::{Arc, OnceLock}, time::Duration, }; -use ecow::EcoVec; use enum_iterator::{all, Sequence}; +#[cfg(feature = "audio_encode")] use hound::{SampleFormat, WavReader, WavSpec, WavWriter}; +#[cfg(feature = "image")] use image::{DynamicImage, ImageOutputFormat}; use once_cell::sync::Lazy; use parking_lot::Mutex; use serde::*; -use tinyvec::tiny_vec; use crate::{ - array::{Array, Shape}, - boxed::Boxed, - cowslice::{cowslice, CowSlice}, - function::Signature, - primitive::PrimDoc, - value::Value, - Uiua, UiuaResult, + cowslice::cowslice, primitive::PrimDoc, Array, Boxed, Signature, Uiua, UiuaResult, Value, }; /// Access the built-in `example.ua` file @@ -604,6 +597,7 @@ pub trait SysBackend: Any + Send + Sync + 'static { Err("Sleeping is not supported in this environment".into()) } /// Show an image + #[cfg(feature = "image")] fn show_image(&self, image: DynamicImage) -> Result<(), String> { Err("Showing images not supported in this environment".into()) } @@ -1053,127 +1047,178 @@ impl SysOp { env.rt.backend.invoke(&path).map_err(|e| env.error(e))?; } SysOp::ImDecode => { - let bytes: CowSlice = match env.pop(1)? { - #[cfg(feature = "bytes")] - Value::Byte(arr) => { - if arr.rank() != 1 { - return Err(env.error(format!( - "Image bytes array must be rank 1, but is rank {}", - arr.rank() - ))); + #[cfg(feature = "image")] + { + let bytes: crate::cowslice::CowSlice = match env.pop(1)? { + #[cfg(feature = "bytes")] + Value::Byte(arr) => { + if arr.rank() != 1 { + return Err(env.error(format!( + "Image bytes array must be rank 1, but is rank {}", + arr.rank() + ))); + } + arr.data } - arr.data - } - Value::Num(arr) => { - if arr.rank() != 1 { - return Err(env.error(format!( - "Image bytes array must be rank 1, but is rank {}", - arr.rank() - ))); + Value::Num(arr) => { + if arr.rank() != 1 { + return Err(env.error(format!( + "Image bytes array must be rank 1, but is rank {}", + arr.rank() + ))); + } + arr.data.iter().map(|&x| x as u8).collect() } - arr.data.iter().map(|&x| x as u8).collect() - } - _ => return Err(env.error("Image bytes must be a numeric array")), - }; - let image = image::load_from_memory(&bytes) - .map_err(|e| env.error(format!("Failed to read image: {}", e)))? - .into_rgba8(); - let shape = tiny_vec![image.height() as usize, image.width() as usize, 4]; - let array = Array::::new( - shape, - image - .into_raw() - .into_iter() - .map(|b| b as f64 / 255.0) - .collect::>(), - ); - env.push(array); + _ => return Err(env.error("Image bytes must be a numeric array")), + }; + let image = image::load_from_memory(&bytes) + .map_err(|e| env.error(format!("Failed to read image: {}", e)))? + .into_rgba8(); + let shape = + tinyvec::tiny_vec![image.height() as usize, image.width() as usize, 4]; + let array = Array::::new( + shape, + image + .into_raw() + .into_iter() + .map(|b| b as f64 / 255.0) + .collect::>(), + ); + env.push(array); + } + #[cfg(not(feature = "image"))] + return Err(env.error("Image decoding is not supported in this environment")); } SysOp::ImEncode => { - let format = env - .pop(1)? - .as_string(env, "Image format must be a string")?; - let value = env.pop(2)?; - let output_format = match format.as_str() { - "jpg" | "jpeg" => ImageOutputFormat::Jpeg(100), - "png" => ImageOutputFormat::Png, - "bmp" => ImageOutputFormat::Bmp, - "gif" => ImageOutputFormat::Gif, - "ico" => ImageOutputFormat::Ico, - format => return Err(env.error(format!("Invalid image format: {}", format))), - }; - let bytes = - value_to_image_bytes(&value, output_format).map_err(|e| env.error(e))?; - env.push(Array::::from(bytes.as_slice())); + #[cfg(feature = "image")] + { + let format = env + .pop(1)? + .as_string(env, "Image format must be a string")?; + let value = env.pop(2)?; + let output_format = match format.as_str() { + "jpg" | "jpeg" => ImageOutputFormat::Jpeg(100), + "png" => ImageOutputFormat::Png, + "bmp" => ImageOutputFormat::Bmp, + "gif" => ImageOutputFormat::Gif, + "ico" => ImageOutputFormat::Ico, + format => { + return Err(env.error(format!("Invalid image format: {}", format))) + } + }; + let bytes = + value_to_image_bytes(&value, output_format).map_err(|e| env.error(e))?; + env.push(Array::::from(bytes.as_slice())); + } + #[cfg(not(feature = "image"))] + return Err(env.error("Image encoding is not supported in this environment")); } SysOp::ImShow => { - let value = env.pop(1)?; - let image = value_to_image(&value).map_err(|e| env.error(e))?; - env.rt.backend.show_image(image).map_err(|e| env.error(e))?; + #[cfg(feature = "image")] + { + let value = env.pop(1)?; + let image = value_to_image(&value).map_err(|e| env.error(e))?; + env.rt.backend.show_image(image).map_err(|e| env.error(e))?; + } + #[cfg(not(feature = "image"))] + return Err(env.error("Image encoding is not supported in this environment")); } SysOp::GifDecode => { - let bytes = env - .pop(1)? - .as_bytes(env, "Gif bytes must be a byte array")?; - let (frame_rate, value) = gif_bytes_to_value(&bytes).map_err(|e| env.error(e))?; - env.push(value); - env.push(frame_rate); + #[cfg(feature = "gif")] + { + let bytes = env + .pop(1)? + .as_bytes(env, "Gif bytes must be a byte array")?; + let (frame_rate, value) = + gif_bytes_to_value(&bytes).map_err(|e| env.error(e))?; + env.push(value); + env.push(frame_rate); + } + #[cfg(not(feature = "gif"))] + return Err(env.error("GIF encoding is not supported in this environment")); } SysOp::GifEncode => { - let delay = env.pop(1)?.as_num(env, "Delay must be a number")?; - let value = env.pop(2)?; - let bytes = value_to_gif_bytes(&value, delay).map_err(|e| env.error(e))?; - env.push(Array::::from(bytes.as_slice())); + #[cfg(feature = "gif")] + { + let delay = env.pop(1)?.as_num(env, "Delay must be a number")?; + let value = env.pop(2)?; + let bytes = value_to_gif_bytes(&value, delay).map_err(|e| env.error(e))?; + env.push(Array::::from(bytes.as_slice())); + } + #[cfg(not(feature = "gif"))] + return Err(env.error("GIF encoding is not supported in this environment")); } SysOp::GifShow => { - let delay = env.pop(1)?.as_num(env, "Delay must be a number")?; - let value = env.pop(2)?; - let bytes = value_to_gif_bytes(&value, delay).map_err(|e| env.error(e))?; - env.rt.backend.show_gif(bytes).map_err(|e| env.error(e))?; + #[cfg(feature = "gif")] + { + let delay = env.pop(1)?.as_num(env, "Delay must be a number")?; + let value = env.pop(2)?; + let bytes = value_to_gif_bytes(&value, delay).map_err(|e| env.error(e))?; + env.rt.backend.show_gif(bytes).map_err(|e| env.error(e))?; + } + #[cfg(not(feature = "gif"))] + return Err(env.error("GIF encoding is not supported in this environment")); } SysOp::AudioDecode => { - let bytes: CowSlice = match env.pop(1)? { - #[cfg(feature = "bytes")] - Value::Byte(arr) => { - if arr.rank() != 1 { - return Err(env.error(format!( - "Audio bytes array must be rank 1, but is rank {}", - arr.rank() - ))); + #[cfg(feature = "audio_encode")] + { + let bytes: crate::cowslice::CowSlice = match env.pop(1)? { + #[cfg(feature = "bytes")] + Value::Byte(arr) => { + if arr.rank() != 1 { + return Err(env.error(format!( + "Audio bytes array must be rank 1, but is rank {}", + arr.rank() + ))); + } + arr.data } - arr.data - } - Value::Num(arr) => { - if arr.rank() != 1 { - return Err(env.error(format!( - "Audio bytes array must be rank 1, but is rank {}", - arr.rank() - ))); + Value::Num(arr) => { + if arr.rank() != 1 { + return Err(env.error(format!( + "Audio bytes array must be rank 1, but is rank {}", + arr.rank() + ))); + } + arr.data.iter().map(|&x| x as u8).collect() } - arr.data.iter().map(|&x| x as u8).collect() - } - _ => return Err(env.error("Audio bytes be a numeric array")), - }; - let array = array_from_wav_bytes(&bytes, env).map_err(|e| env.error(e))?; - env.push(array); + _ => return Err(env.error("Audio bytes be a numeric array")), + }; + let array = array_from_wav_bytes(&bytes, env).map_err(|e| env.error(e))?; + env.push(array); + } + #[cfg(not(feature = "audio_encode"))] + return Err(env.error("Audio decoding is not supported in this environment")); } SysOp::AudioEncode => { - let format = env - .pop(1)? - .as_string(env, "Audio format must be a string")?; - let value = env.pop(2)?; - let bytes = match format.as_str() { - "wav" => value_to_wav_bytes(&value, env.rt.backend.audio_sample_rate()) - .map_err(|e| env.error(e))?, - format => return Err(env.error(format!("Invalid audio format: {}", format))), - }; - env.push(Array::::from(bytes.as_slice())); + #[cfg(feature = "audio_encode")] + { + let format = env + .pop(1)? + .as_string(env, "Audio format must be a string")?; + let value = env.pop(2)?; + let bytes = match format.as_str() { + "wav" => value_to_wav_bytes(&value, env.rt.backend.audio_sample_rate()) + .map_err(|e| env.error(e))?, + format => { + return Err(env.error(format!("Invalid audio format: {}", format))) + } + }; + env.push(Array::::from(bytes.as_slice())); + } + #[cfg(not(feature = "audio_encode"))] + return Err(env.error("Audio encoding is not supported in this environment")); } SysOp::AudioPlay => { - let value = env.pop(1)?; - let bytes = value_to_wav_bytes(&value, env.rt.backend.audio_sample_rate()) - .map_err(|e| env.error(e))?; - env.rt.backend.play_audio(bytes).map_err(|e| env.error(e))?; + #[cfg(feature = "audio_encode")] + { + let value = env.pop(1)?; + let bytes = value_to_wav_bytes(&value, env.rt.backend.audio_sample_rate()) + .map_err(|e| env.error(e))?; + env.rt.backend.play_audio(bytes).map_err(|e| env.error(e))?; + } + #[cfg(not(feature = "audio_encode"))] + return Err(env.error("Audio encoding is not supported in this environment")); } SysOp::AudioSampleRate => { let sample_rate = env.rt.backend.audio_sample_rate(); @@ -1412,13 +1457,15 @@ fn value_to_command(value: &Value, env: &Uiua) -> UiuaResult<(String, Vec Result, String> { image_to_bytes(&value_to_image(value)?, format) } #[doc(hidden)] +#[cfg(feature = "image")] pub fn image_to_bytes(image: &DynamicImage, format: ImageOutputFormat) -> Result, String> { - let mut bytes = Cursor::new(Vec::new()); + let mut bytes = std::io::Cursor::new(Vec::new()); image .write_to(&mut bytes, format) .map_err(|e| format!("Failed to write image: {e}"))?; @@ -1426,6 +1473,7 @@ pub fn image_to_bytes(image: &DynamicImage, format: ImageOutputFormat) -> Result } #[doc(hidden)] +#[cfg(feature = "image")] pub fn value_to_image(value: &Value) -> Result { if ![2, 3].contains(&value.rank()) { return Err(format!( @@ -1549,6 +1597,7 @@ pub fn value_to_audio_channels(audio: &Value) -> Result>, String> { } #[doc(hidden)] +#[cfg(feature = "audio_encode")] pub fn value_to_wav_bytes(audio: &Value, sample_rate: u32) -> Result, String> { #[cfg(not(feature = "audio"))] { @@ -1566,6 +1615,7 @@ pub fn value_to_wav_bytes(audio: &Value, sample_rate: u32) -> Result, St } } +#[cfg(feature = "audio_encode")] fn value_to_wav_bytes_impl( audio: &Value, convert_samples: impl Fn(f64) -> T + Copy, @@ -1585,7 +1635,7 @@ fn value_to_wav_bytes_impl( bits_per_sample, sample_format, }; - let mut bytes = Cursor::new(Vec::new()); + let mut bytes = std::io::Cursor::new(Vec::new()); let mut writer = WavWriter::new(&mut bytes, spec).map_err(|e| e.to_string())?; for i in 0..channels[0].len() { for channel in &channels { @@ -1601,6 +1651,7 @@ fn value_to_wav_bytes_impl( } #[doc(hidden)] +#[cfg(feature = "audio_encode")] pub fn stereo_to_wave_bytes( samples: &[[f64; 2]], convert_samples: impl Fn(f64) -> T + Copy, @@ -1614,7 +1665,7 @@ pub fn stereo_to_wave_bytes( bits_per_sample, sample_format, }; - let mut bytes = Cursor::new(Vec::new()); + let mut bytes = std::io::Cursor::new(Vec::new()); let mut writer = WavWriter::new(&mut bytes, spec).map_err(|e| e.to_string())?; for frame in samples { for sample in frame { @@ -1629,9 +1680,10 @@ pub fn stereo_to_wave_bytes( Ok(bytes.into_inner()) } +#[cfg(feature = "audio_encode")] fn array_from_wav_bytes(bytes: &[u8], env: &Uiua) -> UiuaResult> { - let mut reader: WavReader> = - WavReader::new(Cursor::new(bytes)).map_err(|e| env.error(e.to_string()))?; + let mut reader: WavReader> = + WavReader::new(std::io::Cursor::new(bytes)).map_err(|e| env.error(e.to_string()))?; let spec = reader.spec(); match (spec.sample_format, spec.bits_per_sample) { (SampleFormat::Int, 16) => { @@ -1650,13 +1702,14 @@ fn array_from_wav_bytes(bytes: &[u8], env: &Uiua) -> UiuaResult> { } } +#[cfg(feature = "audio_encode")] fn array_from_wav_bytes_impl( - reader: &mut WavReader>, + reader: &mut WavReader>, sample_to_f64: impl Fn(T) -> f64, env: &Uiua, ) -> UiuaResult> { let channel_count = reader.spec().channels as usize; - let mut channels = vec![EcoVec::new(); channel_count]; + let mut channels = vec![ecow::EcoVec::new(); channel_count]; let mut curr_channel = 0; for sample in reader.samples::() { let sample = sample.map_err(|e| env.error(e.to_string()))?; @@ -1672,7 +1725,9 @@ fn array_from_wav_bytes_impl( } #[doc(hidden)] +#[cfg(feature = "gif")] pub fn value_to_gif_bytes(value: &Value, frame_rate: f64) -> Result, String> { + use std::collections::{HashMap, HashSet}; if value.row_count() == 0 { return Err("Cannot convert empty array into GIF".into()); } @@ -1695,7 +1750,7 @@ pub fn value_to_gif_bytes(value: &Value, frame_rate: f64) -> Result, Str )); } let mut reduction = 1; - let mut bytes = Cursor::new(Vec::new()); + let mut bytes = std::io::Cursor::new(Vec::new()); let mut all_colors = HashSet::new(); for frame in &frames { for pixel in frame.pixels() { @@ -1738,6 +1793,7 @@ pub fn value_to_gif_bytes(value: &Value, frame_rate: f64) -> Result, Str } #[doc(hidden)] +#[cfg(feature = "gif")] pub fn gif_bytes_to_value(bytes: &[u8]) -> Result<(f64, Value), gif::DecodingError> { let mut decoder = gif::DecodeOptions::new(); decoder.set_color_output(gif::ColorOutput::RGBA); @@ -1745,7 +1801,7 @@ pub fn gif_bytes_to_value(bytes: &[u8]) -> Result<(f64, Value), gif::DecodingErr let first_frame = decoder.read_next_frame()?.unwrap(); let width = first_frame.width; let height = first_frame.height; - let mut data: CowSlice = CowSlice::new(); + let mut data: crate::cowslice::CowSlice = Default::default(); let mut frame_count = 1; let mut delay_sum = first_frame.delay as f64 / 100.0; data.extend(first_frame.buffer.iter().map(|&b| b as f64 / 255.0)); @@ -1756,7 +1812,7 @@ pub fn gif_bytes_to_value(bytes: &[u8]) -> Result<(f64, Value), gif::DecodingErr } let avg_delay = delay_sum / frame_count as f64; let frame_rate = 1.0 / avg_delay; - let shape = Shape::from_iter([frame_count, height as usize, width as usize, 4]); + let shape = crate::Shape::from_iter([frame_count, height as usize, width as usize, 4]); let mut num = Value::Num(Array::new(shape, data)); num.compress(); Ok((frame_rate, num)) diff --git a/src/sys_native.rs b/src/sys_native.rs index 52ca47cc5..bdeae1245 100644 --- a/src/sys_native.rs +++ b/src/sys_native.rs @@ -87,13 +87,13 @@ impl GlobalNativeSys { static NATIVE_SYS: Lazy = Lazy::new(Default::default); -#[cfg(feature = "audio")] +#[cfg(all(feature = "audio", feature = "binary"))] #[doc(hidden)] pub fn set_audio_stream_time(time: f64) { *NATIVE_SYS.audio_stream_time.lock() = Some(time); } -#[cfg(feature = "audio")] +#[cfg(all(feature = "audio", feature = "binary"))] #[doc(hidden)] pub fn set_audio_stream_time_port(port: u16) -> std::io::Result<()> { let socket = std::net::UdpSocket::bind(("127.0.0.1", 0))?; @@ -252,7 +252,7 @@ impl SysBackend for NativeSys { sleep(Duration::from_secs_f64(seconds)); Ok(()) } - #[cfg(feature = "terminal_image")] + #[cfg(all(feature = "terminal_image", feature = "image"))] fn show_image(&self, image: image::DynamicImage) -> Result<(), String> { let (width, height) = if let Some((w, h)) = term_size::dimensions() { let (tw, th) = (w as u32, h.saturating_sub(1) as u32);