Skip to content

Commit

Permalink
chore(lua): now an optional feature
Browse files Browse the repository at this point in the history
  • Loading branch information
arcnmx committed Sep 17, 2024
1 parent 262d469 commit ef3407c
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 62 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ include = [
branches = ["main", "dev"]

[package.metadata.docs.rs]
features = ["v0_4_16", "experimental", "glib-signal", "libspa", "futures", "serde"]
features = ["v0_4_16", "lua", "experimental", "glib-signal", "libspa", "futures", "serde"]
rustc-args = ["--cfg", "docsrs"]
rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]

Expand Down Expand Up @@ -54,6 +54,7 @@ semver = "1.0"

[features]
default = []
lua = []
experimental = []
glib-signal = ["dep:glib-signal"]
futures = ["glib-signal?/futures", "dep:futures-channel"]
Expand Down
3 changes: 2 additions & 1 deletion examples/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ clap = { version = "4.0", features = ["derive"] }
anyhow = "1.0"

[features]
default = ["spa-json"]
default = ["spa-json", "lua"]
lua = ["wireplumber/lua"]
spa-json = ["wireplumber/v0_4_8", "serde"]
serde = ["dep:serde", "wireplumber/serde"]
serde_json = ["serde", "dep:serde_json"]
80 changes: 57 additions & 23 deletions examples/src/bin/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
//!
//! Roughly based on the original [wpexec.c](https://gitlab.freedesktop.org/pipewire/wireplumber/-/blob/master/src/tools/wpexec.c)
#[cfg(feature = "lua")]
use wireplumber::lua::LuaVariant;
#[cfg(feature = "spa-json")]
use wireplumber::spa::json::SpaJson;
use {
anyhow::{format_err, Context, Result},
clap::{Parser, ValueEnum},
glib::Variant,
std::{cell::RefCell, env, fs, path::Path, rc::Rc},
wireplumber::{
log::{info, warning},
lua::LuaVariant,
plugin::*,
prelude::*,
pw::{self, Properties},
Expand Down Expand Up @@ -41,17 +43,23 @@ enum ModuleType {
doc = "Command-line arguments parsed via [clap](https://docs.rs/clap/latest/clap/)"
)]
#[derive(Parser, Debug)]
#[clap(version)]
#[clap(version, disable_version_flag(true))]
struct Args {
#[clap(value_enum, short = 't', long = "type", default_value = "lua")]
module_type: ModuleType,

/// Arguments to pass to the loaded module
/// JSON arguments to pass to the loaded module
///
/// Lua scripts only support arrays and dictionary maps.
#[clap(short = 'J', long = "json")]
json_arg: Option<String>,

/// GLIB Variant argument to pass to the loaded module
///
/// https://docs.gtk.org/glib/gvariant-text-format.html
#[clap(short = 'V', long = "variant")]
variant_arg: Option<String>,

/// Associated plugins to load, provided by the module
#[clap(short, long = "plugin")]
plugins: Vec<String>,
Expand Down Expand Up @@ -80,24 +88,25 @@ async fn main_async(core: &Core, args: &Args) -> Result<()> {
}
.unwrap_or(path.into());

let variant_args = args.variant()?;
if let Some(module) = args.module_type.loader_module() {
core
.load_component(module, ComponentLoader::TYPE_WIREPLUMBER_MODULE, None)
.with_context(|| format!("failed to load the {:?} scripting module", args.module_type))?;
}
if args.module_type.is_lua() {
core
.load_lua_script(&path, variant_args)
.context("failed to load the lua script")?;
} else {
core
.load_component(
&path,
args.module_type.loader_type(),
variant_args.as_ref().map(|v| v.as_variant()),
)
.with_context(|| format!("failed to load {path} as a {}", args.module_type.loader_type()))?;
match args.module_type.is_lua() {
#[cfg(feature = "lua")]
true => {
let variant_args = args.lua_variant()?;
core
.load_lua_script(&path, variant_args)
.context("failed to load the lua script")?;
},
_ => {
let variant_args = args.args()?;
core
.load_component(&path, args.module_type.loader_type(), variant_args.as_ref())
.with_context(|| format!("failed to load {path} as a {}", args.module_type.loader_type()))?;
},
}

core.connect_future().await?;
Expand Down Expand Up @@ -143,7 +152,11 @@ fn main() -> Result<()> {
}

// bail out early if invalid args are provided
let _ = args.variant()?;
let _ = match args.module_type.is_lua() {
#[cfg(feature = "lua")]
true => args.lua_variant().map(drop)?,
_ => args.args().map(drop)?,
};

// initialize the wireplumber and pipewire libraries
Core::init();
Expand Down Expand Up @@ -264,18 +277,39 @@ impl Args {
}
}

/// A JSON-like blob of data to pass as an argument to the script to be loaded
#[cfg(feature = "lua")]
fn lua_variant(&self) -> Result<Option<LuaVariant>> {
#[allow(unreachable_patterns)]
match (&self.variant_arg, &self.json_arg) {
(None, None) => Ok(None),
(Some(v), _) => Variant::parse(None, v)
.map_err(Into::into)
.and_then(|v| LuaVariant::convert_from(&v).map_err(Into::into))
.map(Some),
#[cfg(feature = "spa-json")]
(None, Some(json)) => SpaJson::deserialize_from_string(json).map_err(Into::into).map(Some),
#[cfg(feature = "serde_json")]
(None, Some(json)) => serde_json::from_str(json).map_err(Into::into).map(Some),
(None, Some(..)) => panic!("spa-json or serde_json feature required to parse JSON arguments"),
}
}

/// A JSON-like blob of data to pass as an argument to the script or module to be loaded
///
/// In practice this must be a dictionary or array because lua scripts can't work with other
/// types of data as the top-level container, but more specific types may be usable by other
/// modules. See [wireplumber::lua] for a more detailed explanation.
fn variant(&self) -> Result<Option<LuaVariant>> {
match self.json_arg {
None => Ok(None),
fn args(&self) -> Result<Option<Variant>> {
#[allow(unreachable_patterns)]
match (&self.variant_arg, &self.json_arg) {
(None, None) => Ok(None),
(Some(v), _) => Variant::parse(None, v).map_err(Into::into).map(Some),
#[cfg(feature = "lua")]
(None, Some(..)) => self.lua_variant().map(|v| v.map(|v| v.into())),
#[cfg(feature = "spa-json")]
Some(ref json) => SpaJson::deserialize_from_string(json).map_err(Into::into).map(Some),
#[cfg(all(feature = "serde_json", not(feature = "spa-json")))]
Some(ref json) => serde_json::from_str(json).map_err(Into::into).map(Some),
(None, Some(json)) => SpaJson::from_string(json).parse_variant().map_err(Into::into).map(Some),
(None, Some(..)) => panic!("spa-json or lua feature is required to convert JSON to Variant"),
}
}
}
9 changes: 5 additions & 4 deletions examples/src/static-link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "lua")]
use wireplumber::lua::from_variant;
use {
futures::{channel::mpsc, future, FutureExt, StreamExt},
glib::{prelude::*, Error, SourceId, Variant},
Expand All @@ -16,7 +18,6 @@ use {
core::ObjectFeatures,
error,
log::{info, warning},
lua::from_variant,
plugin::{self, AsyncPluginImpl, SimplePlugin, SimplePluginObject, SourceHandlesCell},
prelude::*,
pw::{self, Link, Node, Port, Properties, ProxyFeatures},
Expand Down Expand Up @@ -294,18 +295,18 @@ impl SimplePlugin for StaticLink {
self.args.set(args).unwrap();
}

#[cfg(feature = "serde")]
#[cfg(all(feature = "serde", feature = "lua"))]
fn decode_args(args: Option<Variant>) -> Result<Self::Args, Error> {
args
.map(|args| from_variant(&args))
.unwrap_or(Ok(Default::default()))
.map_err(error::invalid_argument)
}

#[cfg(not(feature = "serde"))]
#[cfg(not(all(feature = "serde", feature = "lua")))]
fn decode_args(args: Option<Variant>) -> Result<Self::Args, Error> {
let args = args.map(|_args| {
warning!(domain: LOG_DOMAIN, "requires serde build feature");
warning!(domain: LOG_DOMAIN, "requires lua and serde build features");
Default::default()
});
Ok(args.unwrap_or_default())
Expand Down
6 changes: 5 additions & 1 deletion src/core/core.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#[cfg(any(feature = "v0_4_2"))]
use crate::plugin::LookupDirs;
#[cfg(feature = "lua")]
use crate::{lua::ToLuaTable, plugin::ComponentLoader};
use {
crate::{lua::ToLuaTable, plugin::ComponentLoader, prelude::*, Core, InitFlags, Properties},
crate::{prelude::*, Core, InitFlags, Properties},
glib::{MainContext, MainLoop},
pipewire_sys::{pw_context, pw_core},
};
Expand Down Expand Up @@ -99,6 +101,8 @@ impl Core {
unsafe { NonNull::new(ffi::wp_core_get_pw_context(self.to_glib_none().0)).expect("pw_context for WpCore") }
}

#[cfg(feature = "lua")]
#[cfg_attr(docsrs, doc(cfg(feature = "lua")))]
#[doc(alias = "wp_core_load_component")]
pub fn load_lua_script<A: ToLuaTable>(&self, script_path: &str, args: A) -> Result<(), Error> {
let args = args
Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ pub mod dbus;
pub mod error;
pub mod local;
pub mod log;
#[cfg(feature = "lua")]
#[cfg_attr(docsrs, doc(cfg(feature = "lua")))]
pub mod lua;
pub mod plugin;
pub mod prelude;
Expand Down
1 change: 1 addition & 0 deletions src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
//! ```
//! use wireplumber::plugin::{Plugin, PluginFeatures};
//!
//! # #[cfg(feature = "lua")]
//! # async fn load_lua(core: wireplumber::core::Core) -> wireplumber::Result<()> {
//! core.load_component("libwireplumber-module-lua-scripting", "module", None)?;
//! core.load_lua_script("create-item.lua", ())?;
Expand Down
Loading

0 comments on commit ef3407c

Please sign in to comment.