Skip to content

Log #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: develop
Choose a base branch
from
Open

Log #61

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
b0fce0c
init lv2-log crate
YruamaLairba Mar 25, 2020
79e0864
implement feature part of log
YruamaLairba Mar 26, 2020
101d936
impl Uribound and LogURIDcollection for type of log
YruamaLairba Mar 26, 2020
875153c
usable but unsafe draft
YruamaLairba Mar 26, 2020
3d3a31e
avoid '%' be interpreted, Format code
YruamaLairba Mar 29, 2020
862fa63
check for null string terminator for safety
YruamaLairba Mar 29, 2020
92691a4
create and use LogError enum
YruamaLairba Mar 29, 2020
eca5acd
Copy as supertrait for entry urid
YruamaLairba Apr 1, 2020
40ea7b7
add some doc
YruamaLairba Apr 1, 2020
36bf4fe
restrict log feature to the instantiation threading class
YruamaLairba Apr 2, 2020
fe6d8d7
WIP
YruamaLairba Apr 6, 2020
575a626
Merge branch 'develop' into log
YruamaLairba Aug 10, 2021
0eff4b0
fix the code after merge
YruamaLairba Aug 10, 2021
5eb55ab
apply prokopyl suggestion to refactor code
YruamaLairba Aug 10, 2021
d278053
forbid log feature only in the audio threading class
YruamaLairba Aug 10, 2021
a5c2d6f
change print to print_cstr
YruamaLairba Aug 17, 2021
5226867
remove println, it's not needed and almost useless
YruamaLairba Aug 17, 2021
97c4175
fix EntryType marker trait
YruamaLairba Aug 17, 2021
f6fd9ee
use a custom type to not have function pointer inside Option
YruamaLairba Aug 19, 2021
30656af
replace LogError by PrintError
YruamaLairba Aug 19, 2021
6e0ee4a
shorten the name and redocument URID marker
YruamaLairba Aug 19, 2021
826aeb0
ooops, log is not rt-safe but is valid in any threading class
YruamaLairba Aug 19, 2021
f0a47aa
oops, forgot to remove some _class suffix
YruamaLairba Aug 19, 2021
99e12cc
use a pointer replacement.
YruamaLairba Aug 20, 2021
ed9613b
complete and rework the log crate doc
YruamaLairba Aug 21, 2021
5134b89
fix example test (missing dev dependencie)
YruamaLairba Aug 21, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ members = [
"atom",
"core",
"core/derive",
"log",
"midi",
"state",
"sys",
Expand Down
15 changes: 15 additions & 0 deletions log/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "lv2-log"
version = "0.1.0"
authors = ["Yruama_Lairba <[email protected]>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lv2-sys = "2.0.0"
lv2-core = "3.0.0"
urid = "0.1.0"

[dev-dependencies]
lv2-urid = "2.1.0" # for doc example
236 changes: 236 additions & 0 deletions log/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
//! Logging library allowing LV2 plugins to print log message through host.
//!
//! This crate allow plugins to print log message through the host. Different log levels are
//! defined by URI and passed as a lv2 URID. The core of this crate is the [Log
//! Feature](struct.Log.html).
//!
//! # Example
//!
//! This following plugin log an error message each time the input control is switched on.
//!
//! Note: Logging an error is not real-time safe. This following plugin should not ask
//! `hardRTCapable` in its .ttl files.
//!
//! ```
//! use core::any::Any;
//! use lv2_core::feature::*;
//! use lv2_core::prelude::*;
//! use lv2_log::*;
//! use lv2_urid::*;
//! use std::ffi::CStr;
//! use urid::*;
//!
//! #[derive(URIDCollection)]
//! struct LoggerUrids {
//! error: URID<Error>,
//! }
//!
//! #[derive(PortCollection)]
//! struct Ports {
//! toggle: InputPort<Control>,
//! }
//! #[derive(FeatureCollection)]
//! pub struct InitFeatures<'a> {
//! map: LV2Map<'a>,
//! }
//!
//! #[derive(FeatureCollection)]
//! pub struct AudioFeatures<'a> {
//! log: Log<'a>,
//! }
//!
//! #[repr(C)]
//! #[uri("urn:rust-lv2-test:logger")]
//! struct logger {
//! urids: LoggerUrids,
//! last_toggle: bool,
//! }
//!
//! impl Plugin for logger {
//! type Ports = Ports;
//! type InitFeatures = InitFeatures<'static>;
//! type AudioFeatures = AudioFeatures<'static>;
//!
//! fn new(_plugin_info: &PluginInfo, features: &mut Self::InitFeatures) -> Option<Self> {
//! let urids: LoggerUrids = features.map.populate_collection()?;
//! Some(Self {
//! urids,
//! last_toggle: false,
//! })
//! }
//!
//! fn run(&mut self, ports: &mut Ports, features: &mut Self::AudioFeatures, _: u32) {
//! let log = &features.log;
//! let toggle = *ports.toggle > 0.0;
//! if self.last_toggle != toggle && toggle {
//! let message = CStr::from_bytes_with_nul(b"error run message\n\0").unwrap();
//! let _ = log.print_cstr(self.urids.error, message);
//! }
//! self.last_toggle = toggle;
//! }
//!
//! fn extension_data(_uri: &Uri) -> Option<&'static dyn Any> {
//! None
//! //match_extensions![uri, WorkerDescriptor<Self>]
//! }
//! }
//!
//! lv2_descriptors!(logger);
//! ```

use lv2_core::feature::{Feature, ThreadingClass};
use std::error;
use std::ffi::CStr;
use std::fmt;
use std::os::raw::*; //get all common c_type
use urid::*;

/// URID marker. The corresponding URID is used to log an error message on the host.
pub struct Error;
unsafe impl UriBound for Error {
const URI: &'static [u8] = lv2_sys::LV2_LOG__Error;
}

/// URID marker. The corresponding URID is used to log an informative message on the host.
pub struct Note;
unsafe impl UriBound for Note {
const URI: &'static [u8] = lv2_sys::LV2_LOG__Note;
}

/// URID marker. The corresponding URID is used to log a debugging trace on the host.
pub struct Trace;
unsafe impl UriBound for Trace {
const URI: &'static [u8] = lv2_sys::LV2_LOG__Trace;
}

/// URID marker. The corresponding URID is used to log a warning message on the host.
pub struct Warning;
unsafe impl UriBound for Warning {
const URI: &'static [u8] = lv2_sys::LV2_LOG__Warning;
}

/// Trait for URID marker. URID with a marker implementing this can be used to indicate the logging
/// level.
///
/// Plugin implementers can implement this on custom URID marker to define and use additional
/// logging level.
//
pub trait Entry: UriBound {}

impl Entry for Error {}
impl Entry for Note {}
impl Entry for Trace {}
impl Entry for Warning {}

/// Returned if an error occurred when sending a log to the host.
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub struct PrintError;

impl fmt::Display for PrintError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "An error occurred when sending a message to the host")
}
}
impl error::Error for PrintError {}

//replace the lv2_sys::Log_Log to avoid checking if function pointers are null
#[repr(C)]
struct LogInternal {
handle: lv2_sys::LV2_Log_Handle,
printf: unsafe extern "C" fn(
handle: lv2_sys::LV2_Log_Handle,
type_: lv2_sys::LV2_URID,
fmt: *const c_char,
...
) -> c_int,
//placeholder, not useable yet
vprintf: unsafe extern "C" fn(
handle: lv2_sys::LV2_Log_Handle,
type_: lv2_sys::LV2_URID,
fmt: *const c_char,
ap: *mut core::ffi::c_void, //should be *mut ffi::VaList but it's not yet stable
) -> c_int,
}

/// Feature allowing to print log message through the host.
///
///
#[repr(transparent)]
pub struct Log<'a> {
internal: &'a LogInternal,
}

unsafe impl<'a> UriBound for Log<'a> {
const URI: &'static [u8] = lv2_sys::LV2_LOG__log;
}

unsafe impl<'a> Feature for Log<'a> {
unsafe fn from_feature_ptr(feature: *const c_void, _class: ThreadingClass) -> Option<Self> {
(feature as *const LogInternal)
.as_ref()
.map(|internal| Self { internal })
}
}

impl<'a> Log<'a> {
/// Send a log message to the host.
///
/// The `entry` parameter is an URID representing the kind of log message. There are four
/// standard kind of message:
/// * **note:** an informative message.
/// * **warning:** a warning message.
/// * **error:** an error message.
/// * **trace:** a debugging trace. These entries should not be displayed during normal
/// operation, but the host may implement an option to display them for debugging purposes.
/// This entry type is special in that it may be written to in a real-time thread. It is
/// assumed that if debug tracing is enabled, real-time considerations are not a concern.
///
/// # Real-Time safety
///
/// This function is not real-time safe. Except for logging debugging trace, it should not be
/// used in real-time context. That means `HardRTCapable` plugins should not call this function in
/// their `run()` context.
pub fn print_cstr(&self, entry: URID<impl Entry>, message: &CStr) -> Result<(), PrintError> {
let res = unsafe {
(self.internal.printf)(
self.internal.handle,
entry.get(),
"%s\0" as *const _ as *const c_char,
message.as_ptr(),
)
};
if res > 0 {
Ok(())
} else {
Err(PrintError)
}
}
}

/// A URID cache containing all useful log properties.
#[derive(URIDCollection, Debug)]
pub struct LogURIDCollection {
pub error: URID<Error>,
pub note: URID<Note>,
pub trace: URID<Trace>,
pub warning: URID<Warning>,
}

#[cfg(test)]
mod tests {
use super::*;
fn _should_compile() {
let fake_logger = unsafe {
Log {
internal: &*(0xDEFEC8 as *const LogInternal),
}
};
let urid = unsafe { URID::<Error>::new_unchecked(42) };
let message = CStr::from_bytes_with_nul(b"message\0").unwrap();

let _ = fake_logger.print_cstr(urid, message);
}

#[test]
fn it_works() {}
}