[refactor](trx-rs): adopt shared app infra modules
Phase 4: route config loading and plugin discovery through trx-app, remove duplicated local plugin loaders, and align binary dependencies. Co-authored-by: Codex <codex@openai.com> Signed-off-by: Stanislaw Grams <stanislawgrams@gmail.com>
This commit is contained in:
Generated
+3
-4
@@ -2223,13 +2223,11 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"dirs",
|
"dirs",
|
||||||
"libloading",
|
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
|
||||||
"trx-app",
|
"trx-app",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
"trx-frontend",
|
"trx-frontend",
|
||||||
@@ -2253,6 +2251,7 @@ dependencies = [
|
|||||||
name = "trx-frontend"
|
name = "trx-frontend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
"tokio",
|
"tokio",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
]
|
]
|
||||||
@@ -2272,6 +2271,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
"trx-frontend",
|
"trx-frontend",
|
||||||
|
"trx-protocol",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2294,6 +2294,7 @@ dependencies = [
|
|||||||
"tracing",
|
"tracing",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
"trx-frontend",
|
"trx-frontend",
|
||||||
|
"trx-protocol",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2321,7 +2322,6 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"cpal",
|
"cpal",
|
||||||
"dirs",
|
"dirs",
|
||||||
"libloading",
|
|
||||||
"opus",
|
"opus",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -2329,7 +2329,6 @@ dependencies = [
|
|||||||
"tokio-serial",
|
"tokio-serial",
|
||||||
"toml",
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
|
||||||
"trx-app",
|
"trx-app",
|
||||||
"trx-backend",
|
"trx-backend",
|
||||||
"trx-core",
|
"trx-core",
|
||||||
|
|||||||
@@ -13,10 +13,8 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
libloading = "0.8"
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
trx-app = { path = "../trx-app" }
|
trx-app = { path = "../trx-app" }
|
||||||
trx-core = { path = "../trx-core" }
|
trx-core = { path = "../trx-core" }
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use std::net::IpAddr;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use trx_app::{ConfigError, ConfigFile};
|
||||||
|
|
||||||
/// Top-level client configuration structure.
|
/// Top-level client configuration structure.
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||||
@@ -189,44 +190,13 @@ pub struct HttpJsonAuthConfig {
|
|||||||
impl ClientConfig {
|
impl ClientConfig {
|
||||||
/// Load configuration from a specific file path.
|
/// Load configuration from a specific file path.
|
||||||
pub fn load_from_file(path: &Path) -> Result<Self, ConfigError> {
|
pub fn load_from_file(path: &Path) -> Result<Self, ConfigError> {
|
||||||
let contents = std::fs::read_to_string(path)
|
<Self as ConfigFile>::load_from_file(path)
|
||||||
.map_err(|e| ConfigError::ReadError(path.to_path_buf(), e.to_string()))?;
|
|
||||||
|
|
||||||
toml::from_str(&contents)
|
|
||||||
.map_err(|e| ConfigError::ParseError(path.to_path_buf(), e.to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load configuration from the default search paths.
|
/// Load configuration from the default search paths.
|
||||||
/// Returns default config if no config file is found.
|
/// Returns default config if no config file is found.
|
||||||
pub fn load_from_default_paths() -> Result<(Self, Option<PathBuf>), ConfigError> {
|
pub fn load_from_default_paths() -> Result<(Self, Option<PathBuf>), ConfigError> {
|
||||||
let search_paths = Self::default_search_paths();
|
<Self as ConfigFile>::load_from_default_paths()
|
||||||
|
|
||||||
for path in search_paths {
|
|
||||||
if path.exists() {
|
|
||||||
let config = Self::load_from_file(&path)?;
|
|
||||||
return Ok((config, Some(path)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((Self::default(), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the default search paths for config files.
|
|
||||||
pub fn default_search_paths() -> Vec<PathBuf> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
|
|
||||||
// Current directory
|
|
||||||
paths.push(PathBuf::from("trx-client.toml"));
|
|
||||||
|
|
||||||
// XDG config directory
|
|
||||||
if let Some(config_dir) = dirs::config_dir() {
|
|
||||||
paths.push(config_dir.join("trx-rs").join("client.toml"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// System-wide config
|
|
||||||
paths.push(PathBuf::from("/etc/trx-rs/client.toml"));
|
|
||||||
|
|
||||||
paths
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an example configuration as a TOML string.
|
/// Generate an example configuration as a TOML string.
|
||||||
@@ -263,40 +233,22 @@ impl ClientConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when loading configuration.
|
impl ConfigFile for ClientConfig {
|
||||||
#[derive(Debug)]
|
fn config_filename() -> &'static str {
|
||||||
pub enum ConfigError {
|
"client.toml"
|
||||||
/// Failed to read the config file
|
}
|
||||||
ReadError(PathBuf, String),
|
|
||||||
/// Failed to parse the config file
|
|
||||||
ParseError(PathBuf, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ConfigError {
|
fn default_search_paths() -> Vec<PathBuf> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
let mut paths = Vec::new();
|
||||||
match self {
|
paths.push(PathBuf::from("trx-client.toml"));
|
||||||
Self::ReadError(path, err) => {
|
if let Some(config_dir) = dirs::config_dir() {
|
||||||
write!(
|
paths.push(config_dir.join("trx-rs").join("client.toml"));
|
||||||
f,
|
|
||||||
"failed to read config file '{}': {}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::ParseError(path, err) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"failed to parse config file '{}': {}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
paths.push(PathBuf::from("/etc/trx-rs/client.toml"));
|
||||||
|
paths
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for ConfigError {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2025 Stanislaw Grams <stanislawgrams@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use libloading::{Library, Symbol};
|
|
||||||
use tracing::{info, warn};
|
|
||||||
|
|
||||||
const PLUGIN_ENV: &str = "TRX_PLUGIN_DIRS";
|
|
||||||
const PLUGIN_ENTRYPOINT: &str = "trx_register";
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const PATH_SEPARATOR: char = ';';
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
const PATH_SEPARATOR: char = ':';
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["dll"];
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["dylib"];
|
|
||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["so"];
|
|
||||||
|
|
||||||
pub fn load_plugins() -> Vec<Library> {
|
|
||||||
let mut libraries = Vec::new();
|
|
||||||
let search_paths = plugin_search_paths();
|
|
||||||
|
|
||||||
if search_paths.is_empty() {
|
|
||||||
return libraries;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Plugin search paths: {:?}", search_paths);
|
|
||||||
|
|
||||||
for path in search_paths {
|
|
||||||
if let Err(err) = load_plugins_from_dir(&path, &mut libraries) {
|
|
||||||
warn!("Plugin scan failed for {:?}: {}", path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_plugins_from_dir(path: &Path, libraries: &mut Vec<Library>) -> std::io::Result<()> {
|
|
||||||
if !path.exists() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry in std::fs::read_dir(path)? {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
if !path.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !is_plugin_file(&path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
match Library::new(&path) {
|
|
||||||
Ok(lib) => {
|
|
||||||
if let Err(err) = register_library(&lib, &path) {
|
|
||||||
warn!("Plugin {:?} failed to register: {}", path, err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
info!("Loaded plugin {:?}", path);
|
|
||||||
libraries.push(lib);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!("Failed to load plugin {:?}: {}", path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn register_library(lib: &Library, path: &Path) -> Result<(), String> {
|
|
||||||
let entry: Symbol<unsafe extern "C" fn()> = lib
|
|
||||||
.get(PLUGIN_ENTRYPOINT.as_bytes())
|
|
||||||
.map_err(|e| format!("missing entrypoint {}: {}", PLUGIN_ENTRYPOINT, e))?;
|
|
||||||
entry();
|
|
||||||
info!("Registered plugin {:?}", path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn plugin_search_paths() -> Vec<PathBuf> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
|
|
||||||
if let Ok(env_paths) = std::env::var(PLUGIN_ENV) {
|
|
||||||
for raw in env_paths.split(PATH_SEPARATOR) {
|
|
||||||
if raw.trim().is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
paths.push(PathBuf::from(raw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paths.push(PathBuf::from("plugins"));
|
|
||||||
|
|
||||||
if let Some(config_dir) = dirs::config_dir() {
|
|
||||||
paths.push(config_dir.join("trx-rs").join("plugins"));
|
|
||||||
}
|
|
||||||
|
|
||||||
paths
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_plugin_file(path: &Path) -> bool {
|
|
||||||
path.extension()
|
|
||||||
.and_then(OsStr::to_str)
|
|
||||||
.map(|ext| PLUGIN_EXTENSIONS.iter().any(|e| ext.eq_ignore_ascii_case(e)))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
@@ -14,10 +14,8 @@ serde = { workspace = true, features = ["derive"] }
|
|||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
toml = { workspace = true }
|
toml = { workspace = true }
|
||||||
tracing = { workspace = true }
|
tracing = { workspace = true }
|
||||||
tracing-subscriber = { workspace = true }
|
|
||||||
clap = { workspace = true, features = ["derive"] }
|
clap = { workspace = true, features = ["derive"] }
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
libloading = "0.8"
|
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
cpal = "0.15"
|
cpal = "0.15"
|
||||||
opus = "0.3"
|
opus = "0.3"
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use std::net::IpAddr;
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use trx_app::{ConfigError, ConfigFile};
|
||||||
|
|
||||||
use trx_core::rig::state::RigMode;
|
use trx_core::rig::state::RigMode;
|
||||||
|
|
||||||
@@ -204,44 +205,13 @@ impl Default for AudioConfig {
|
|||||||
impl ServerConfig {
|
impl ServerConfig {
|
||||||
/// Load configuration from a specific file path.
|
/// Load configuration from a specific file path.
|
||||||
pub fn load_from_file(path: &Path) -> Result<Self, ConfigError> {
|
pub fn load_from_file(path: &Path) -> Result<Self, ConfigError> {
|
||||||
let contents = std::fs::read_to_string(path)
|
<Self as ConfigFile>::load_from_file(path)
|
||||||
.map_err(|e| ConfigError::ReadError(path.to_path_buf(), e.to_string()))?;
|
|
||||||
|
|
||||||
toml::from_str(&contents)
|
|
||||||
.map_err(|e| ConfigError::ParseError(path.to_path_buf(), e.to_string()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load configuration from the default search paths.
|
/// Load configuration from the default search paths.
|
||||||
/// Returns default config if no config file is found.
|
/// Returns default config if no config file is found.
|
||||||
pub fn load_from_default_paths() -> Result<(Self, Option<PathBuf>), ConfigError> {
|
pub fn load_from_default_paths() -> Result<(Self, Option<PathBuf>), ConfigError> {
|
||||||
let search_paths = Self::default_search_paths();
|
<Self as ConfigFile>::load_from_default_paths()
|
||||||
|
|
||||||
for path in search_paths {
|
|
||||||
if path.exists() {
|
|
||||||
let config = Self::load_from_file(&path)?;
|
|
||||||
return Ok((config, Some(path)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok((Self::default(), None))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get the default search paths for config files.
|
|
||||||
pub fn default_search_paths() -> Vec<PathBuf> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
|
|
||||||
// Current directory
|
|
||||||
paths.push(PathBuf::from("trx-server.toml"));
|
|
||||||
|
|
||||||
// XDG config directory
|
|
||||||
if let Some(config_dir) = dirs::config_dir() {
|
|
||||||
paths.push(config_dir.join("trx-rs").join("server.toml"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// System-wide config
|
|
||||||
paths.push(PathBuf::from("/etc/trx-rs/server.toml"));
|
|
||||||
|
|
||||||
paths
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an example configuration as a TOML string.
|
/// Generate an example configuration as a TOML string.
|
||||||
@@ -274,40 +244,22 @@ impl ServerConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Errors that can occur when loading configuration.
|
impl ConfigFile for ServerConfig {
|
||||||
#[derive(Debug)]
|
fn config_filename() -> &'static str {
|
||||||
pub enum ConfigError {
|
"server.toml"
|
||||||
/// Failed to read the config file
|
}
|
||||||
ReadError(PathBuf, String),
|
|
||||||
/// Failed to parse the config file
|
|
||||||
ParseError(PathBuf, String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for ConfigError {
|
fn default_search_paths() -> Vec<PathBuf> {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
let mut paths = Vec::new();
|
||||||
match self {
|
paths.push(PathBuf::from("trx-server.toml"));
|
||||||
Self::ReadError(path, err) => {
|
if let Some(config_dir) = dirs::config_dir() {
|
||||||
write!(
|
paths.push(config_dir.join("trx-rs").join("server.toml"));
|
||||||
f,
|
|
||||||
"failed to read config file '{}': {}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Self::ParseError(path, err) => {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"failed to parse config file '{}': {}",
|
|
||||||
path.display(),
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
paths.push(PathBuf::from("/etc/trx-rs/server.toml"));
|
||||||
|
paths
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for ConfigError {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|||||||
@@ -1,115 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2025 Stanislaw Grams <stanislawgrams@gmail.com>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
use std::ffi::OsStr;
|
|
||||||
use std::path::{Path, PathBuf};
|
|
||||||
|
|
||||||
use libloading::{Library, Symbol};
|
|
||||||
use tracing::{info, warn};
|
|
||||||
|
|
||||||
const PLUGIN_ENV: &str = "TRX_PLUGIN_DIRS";
|
|
||||||
const PLUGIN_ENTRYPOINT: &str = "trx_register";
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const PATH_SEPARATOR: char = ';';
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
const PATH_SEPARATOR: char = ':';
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["dll"];
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["dylib"];
|
|
||||||
#[cfg(all(unix, not(target_os = "macos")))]
|
|
||||||
const PLUGIN_EXTENSIONS: &[&str] = &["so"];
|
|
||||||
|
|
||||||
pub fn load_plugins() -> Vec<Library> {
|
|
||||||
let mut libraries = Vec::new();
|
|
||||||
let search_paths = plugin_search_paths();
|
|
||||||
|
|
||||||
if search_paths.is_empty() {
|
|
||||||
return libraries;
|
|
||||||
}
|
|
||||||
|
|
||||||
info!("Plugin search paths: {:?}", search_paths);
|
|
||||||
|
|
||||||
for path in search_paths {
|
|
||||||
if let Err(err) = load_plugins_from_dir(&path, &mut libraries) {
|
|
||||||
warn!("Plugin scan failed for {:?}: {}", path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
libraries
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_plugins_from_dir(path: &Path, libraries: &mut Vec<Library>) -> std::io::Result<()> {
|
|
||||||
if !path.exists() {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry in std::fs::read_dir(path)? {
|
|
||||||
let entry = entry?;
|
|
||||||
let path = entry.path();
|
|
||||||
if !path.is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if !is_plugin_file(&path) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
match Library::new(&path) {
|
|
||||||
Ok(lib) => {
|
|
||||||
if let Err(err) = register_library(&lib, &path) {
|
|
||||||
warn!("Plugin {:?} failed to register: {}", path, err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
info!("Loaded plugin {:?}", path);
|
|
||||||
libraries.push(lib);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
warn!("Failed to load plugin {:?}: {}", path, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn register_library(lib: &Library, path: &Path) -> Result<(), String> {
|
|
||||||
let entry: Symbol<unsafe extern "C" fn()> = lib
|
|
||||||
.get(PLUGIN_ENTRYPOINT.as_bytes())
|
|
||||||
.map_err(|e| format!("missing entrypoint {}: {}", PLUGIN_ENTRYPOINT, e))?;
|
|
||||||
entry();
|
|
||||||
info!("Registered plugin {:?}", path);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn plugin_search_paths() -> Vec<PathBuf> {
|
|
||||||
let mut paths = Vec::new();
|
|
||||||
|
|
||||||
if let Ok(env_paths) = std::env::var(PLUGIN_ENV) {
|
|
||||||
for raw in env_paths.split(PATH_SEPARATOR) {
|
|
||||||
if raw.trim().is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
paths.push(PathBuf::from(raw));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
paths.push(PathBuf::from("plugins"));
|
|
||||||
|
|
||||||
if let Some(config_dir) = dirs::config_dir() {
|
|
||||||
paths.push(config_dir.join("trx-rs").join("plugins"));
|
|
||||||
}
|
|
||||||
|
|
||||||
paths
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_plugin_file(path: &Path) -> bool {
|
|
||||||
path.extension()
|
|
||||||
.and_then(OsStr::to_str)
|
|
||||||
.map(|ext| PLUGIN_EXTENSIONS.iter().any(|e| ext.eq_ignore_ascii_case(e)))
|
|
||||||
.unwrap_or(false)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user