[vlc-commits] [Git][videolan/vlc][master] 2 commits: vlcrs-core: add tracer capability bindings
Alexandre Janniaux (@alexandre-janniaux)
gitlab at videolan.org
Thu Jan 30 18:05:35 UTC 2025
Alexandre Janniaux pushed to branch master at VideoLAN / VLC
Commits:
3a3b5d67 by Alexandre Janniaux at 2025-01-30T17:15:44+00:00
vlcrs-core: add tracer capability bindings
This commit allows rust code to use the tracer API, through the trace!()
macro, and also exposes a TracerCapability and TracerModuleLoader to
create new tracer modules in Rust.
- - - - -
e8e46b0d by Alexandre Janniaux at 2025-01-30T17:15:44+00:00
logger: add telegraf tracer
The tracer is designed to send the metrics towards a telegraf server
using this kind of configuration:
[[inputs.socket_listener]]
service_address = "tcp://localhost:8094"
The telegraf server can then forward the metrics towards an influxdb
server for monitoring or directly to grafana live server[^1] for
introspection.
The influxdb database can also be used to query the metrics after
they've been indexed.
The application using the tracer can use the VLC_TELEGRAF_ENDPOINT
environment variable (eg. VLC_TELEGRAF_ENDPOINT=tcp://127.0.0.1:8094)
to set where the tracer will output the traces to.
A bunch of notes and improvement left for later:
- Unsafe code is still used for accessing the fields data since an
union is used and union access is unsafe. It could probably be
wrapped from the binding implementation.
- There is no way to specify the address using the configuration
since vlc_variable is not bound to the Rust bindings and no
unsafe extern "C" code is done from the plugin.
- The tracer currently panic as the telegraf server dies because it
doesn't handle reconnection. Proper reconnection logic with maybe
size-limited temporary storing might be a proper workaround for that.
- The tracer doesn't use the timestamp provided by VLC right now,
proper time conversion is required for it to work.
- There's no proper tags for the metrics being sent, which is an
issue given that tags are providing indexing on the data. The
tags should be generated from the string values of the tracer.
Maybe an additional "Role" should be added in the trace entry for
that purpose.
[^1]: https://grafana.com/blog/2021/08/16/streaming-real-time-telegraf-metrics-using-grafana-live/
- - - - -
8 changed files:
- Cargo.toml
- modules/logger/Makefile.am
- + modules/logger/telegraf-rs/Cargo.toml
- + modules/logger/telegraf-rs/src/lib.rs
- src/rust/vlcrs-core/Cargo.toml
- src/rust/vlcrs-core/src/lib.rs
- + src/rust/vlcrs-core/src/tracer/mod.rs
- + src/rust/vlcrs-core/src/tracer/sys.rs
Changes:
=====================================
Cargo.toml
=====================================
@@ -4,6 +4,7 @@ members = [
"src/rust/vlcrs-macros",
"src/rust/vlcrs-messages",
"src/rust/vlcrs-utils",
+ "modules/logger/telegraf-rs/"
]
resolver = "2"
@@ -12,5 +13,5 @@ version = "4.0.0"
license = "LGPL-2.1-or-later"
[workspace.dependencies]
-vlcrs-core = { path = "./vlcrs-core" }
-vlcrs-macros = { path = "./vlcrs-macros" }
+vlcrs-core = { path = "src/rust/vlcrs-core" }
+vlcrs-macros = { path = "src/rust/vlcrs-macros" }
=====================================
modules/logger/Makefile.am
=====================================
@@ -30,3 +30,21 @@ libemscripten_logger_plugin_la_SOURCES = logger/emscripten.c
if HAVE_EMSCRIPTEN
logger_LTLIBRARIES += libemscripten_logger_plugin.la
endif
+
+AM_V_LTCARGO = $(AM_V_LTCARGO_$(V))
+AM_V_LTCARGO_ = $(AM_V_LTCARGO_$(AM_DEFAULT_VERBOSITY))
+AM_V_LTCARGO__0 = $(AM_V_LTCARGO_0)
+AM_V_LTCARGO_0 = @echo " CARGO $(@)";
+
+libtelegraf_rs.la: $(libtelegraf_rs_plugin_la_SOURCES)
+ $(AM_V_LTCARGO)$(LIBTOOL_CARGO) $(abs_srcdir)/logger/telegraf-rs $@
+CLEANFILES += libtelegraf_rs.la
+
+libtelegraf_rs_plugin_la_SOURCES = \
+ logger/telegraf-rs/Cargo.toml \
+ logger/telegraf-rs/src/lib.rs
+libtelegraf_rs_plugin_la_LIBADD = libtelegraf_rs.la
+
+if HAVE_RUST
+logger_LTLIBRARIES += libtelegraf_rs_plugin.la
+endif
=====================================
modules/logger/telegraf-rs/Cargo.toml
=====================================
@@ -0,0 +1,13 @@
+[package]
+name = "telegraf-rs"
+version.workspace = true
+edition = "2021"
+license = "LGPL-2.1-or-later"
+
+[dependencies]
+vlcrs-core.workspace = true
+vlcrs-macros.workspace = true
+telegraf = "=0.6.0"
+
+[lints.rust]
+unexpected_cfgs = { level = "warn", check-cfg = ['cfg(vlc_static_plugins)'] }
=====================================
modules/logger/telegraf-rs/src/lib.rs
=====================================
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// Copyright (C) 2024-2025 Alexandre Janniaux <ajanni at videolabs.io>
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+
+use std::{cell::UnsafeCell, ffi::CStr, sync::Mutex};
+use telegraf::{Client, IntoFieldData, Point};
+use vlcrs_core::tracer::{sys::vlc_tracer_value_type, TracerCapability, TracerModuleLoader};
+use vlcrs_macros::module;
+
+struct TraceField(vlcrs_core::tracer::TraceField);
+
+impl IntoFieldData for TraceField {
+ fn field_data(&self) -> telegraf::FieldData {
+ match self.0.kind() {
+ vlc_tracer_value_type::String => unsafe {
+ let value = CStr::from_ptr(self.0.value().string);
+ telegraf::FieldData::Str(value.to_str().unwrap().to_string())
+ },
+ vlc_tracer_value_type::Integer => unsafe {
+ telegraf::FieldData::Number(self.0.value().integer)
+ },
+ vlc_tracer_value_type::Double => unsafe {
+ telegraf::FieldData::Float(self.0.value().double)
+ },
+ _ => unreachable!(),
+ }
+ }
+}
+
+struct TelegrafTracer {
+ endpoint: Mutex<UnsafeCell<telegraf::Client>>,
+}
+
+impl TracerCapability for TelegrafTracer {
+ fn open(_obj: &mut vlcrs_core::object::Object) -> Option<impl TracerCapability>
+ where
+ Self: Sized,
+ {
+ let endpoint_address =
+ std::env::var("VLC_TELEGRAF_ENDPOINT").unwrap_or(String::from("tcp://localhost:8094"));
+ let endpoint = Client::new(&endpoint_address)
+ .map(UnsafeCell::new)
+ .map(Mutex::new)
+ .unwrap();
+ Some(Self { endpoint })
+ }
+
+ fn trace(&self, _tick: vlcrs_core::tracer::Tick, entries: &'_ vlcrs_core::tracer::Trace) {
+ if !entries
+ .into_iter()
+ .any(|e| e.kind() != vlcrs_core::tracer::sys::vlc_tracer_value_type::String)
+ {
+ /* We cannot support events for now. */
+ return;
+ }
+ let tags: Vec<(String, String)> = entries
+ .into_iter()
+ .filter(|e| e.kind() == vlcrs_core::tracer::sys::vlc_tracer_value_type::String)
+ .map(|entry| unsafe {
+ let value = CStr::from_ptr(entry.value().string);
+ (
+ String::from(entry.key()),
+ String::from(value.to_str().unwrap()),
+ )
+ })
+ .collect();
+
+ let record: Vec<(String, Box<dyn IntoFieldData + 'static>)> = entries
+ .into_iter()
+ .filter(|e| e.kind() != vlcrs_core::tracer::sys::vlc_tracer_value_type::String)
+ .map(|entry| {
+ (
+ String::from(entry.key()),
+ Box::new(TraceField(entry)) as Box<dyn IntoFieldData>,
+ )
+ })
+ .collect();
+
+ let p = Point::new(
+ String::from("measurement"),
+ tags,
+ record,
+ None, //Some(tick.0 as u64),
+ );
+
+ let mut endpoint = self.endpoint.lock().unwrap();
+ if let Err(err) = endpoint.get_mut().write_point(&p) {
+ match err {
+ telegraf::TelegrafError::IoError(e) => eprintln!("TelegrafTracer: IO Error: {}", e),
+ telegraf::TelegrafError::ConnectionError(s) => {
+ eprintln!("telegraf tracer: connection error: {}", s)
+ }
+ telegraf::TelegrafError::BadProtocol(_) => todo!(),
+ }
+ }
+ }
+}
+
+module! {
+ type: TelegrafTracer (TracerModuleLoader),
+ capability: "tracer" @ 0,
+ category: ADVANCED_MISC,
+ description: "Tracer module forwarding the traces to a Telegraf endpoint",
+ shortname: "Telegraf tracer",
+ shortcuts: ["telegraf"],
+}
=====================================
src/rust/vlcrs-core/Cargo.toml
=====================================
@@ -6,3 +6,6 @@ license.workspace = true
[dependencies]
vlcrs-messages = { path = "../vlcrs-messages" }
+
+[dev-dependencies]
+vlcrs-macros.workspace = true
=====================================
src/rust/vlcrs-core/src/lib.rs
=====================================
@@ -1,6 +1,7 @@
#![deny(unsafe_op_in_unsafe_fn)]
#![feature(extern_types)]
#![feature(associated_type_defaults)]
+#![feature(allocator_api)]
#![feature(c_size_t)]
//! The `vlcrs-core` crate.
@@ -11,6 +12,25 @@
//! If you need a vlc core C API that is not ported or wrapped yet here,
//! then do so first instead of bypassing this crate.
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// Copyright (C) 2024 Alexandre Janniaux <ajanni at videolabs.io>
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation; either version 2.1 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with this program; if not, write to the Free Software Foundation,
+// Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.#![deny(unsafe_op_in_unsafe_fn)]
+
pub mod plugin;
pub mod object;
+
+pub mod tracer;
=====================================
src/rust/vlcrs-core/src/tracer/mod.rs
=====================================
@@ -0,0 +1,215 @@
+/// SPDX-License-Identifier: LGPL-2.1-or-later
+/// Copyright (C) 2024-2025 Alexandre Janniaux <ajanni at videolabs.io>
+///
+/// This program is free software; you can redistribute it and/or modify it
+/// under the terms of the GNU Lesser General Public License as published by
+/// the Free Software Foundation; either version 2.1 of the License, or
+/// (at your option) any later version.
+///
+/// This program is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Lesser General Public License for more details.
+///
+/// You should have received a copy of the GNU Lesser General Public License
+/// along with this program; if not, write to the Free Software Foundation,
+/// Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+use std::{
+ cell::UnsafeCell,
+ ffi::{c_char, c_void, CStr},
+ mem::MaybeUninit,
+ ptr::NonNull,
+};
+
+use crate::{object::Object, plugin::ModuleProtocol};
+
+pub mod sys;
+pub use sys::{Trace, TraceField};
+
+pub struct Tick(pub i64);
+
+///
+/// Trait representing the module capability for implementing tracers.
+///
+/// A type implementing the [TracerCapability] trait can be exposed to
+/// a [vlcrs_macros::module!] manifest using the
+/// [TracerModuleLoader] type loader, which will expose it as a "tracer"
+/// capability for VLC core.
+///
+/// Note that this trait *requires* [Sync] since the tracer is used
+/// from the C code in multiple threads at the same time.
+///
+/// [Sync] is automatically implemented by types using only [Sync]
+/// types, and is unsafe to implement. It means that the following
+/// implementation would not be possible without unsafe:
+///
+/// ```compile_fail
+/// use std::cell::Cell;
+/// use vlcrs_core::object::Object;
+/// use vlcrs_core::tracer::{Tick, TracerCapability, Trace};
+/// struct Module { last_trace_tick: Cell<Tick>, }
+/// impl TracerCapability for Module {
+/// fn open(obj: &mut Object) -> Option<impl TracerCapability> {
+/// Some(Self{ last_trace_tick: Cell::from(Tick(0)) })
+/// }
+///
+/// fn trace(&self, tick: Tick, entries: &Trace) {
+/// let mut state = self.last_trace_tick.get_mut();
+/// *state = tick;
+/// }
+/// }
+/// ````
+///
+/// As it will fail with the following error:
+///
+/// ```no_build
+/// test: vlcrs-core/src/tracer/mod.rs - tracer::TracerCapability (line 31)
+/// error[E0277]: `Cell<Tick>` cannot be shared between threads safely
+/// --> vlcrs-core/src/tracer/mod.rs:36:27
+/// |
+/// 8 | impl TracerCapability for Module {
+/// | ^^^^^^ `Cell<Tick>` cannot be shared between threads safely
+/// |
+/// = help: within `Module`, the trait `Sync` is not implemented for `Cell<Tick>`, which is required by `Module: Sync`
+/// = note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
+/// note: required because it appears within the type `Module`
+/// ````
+///
+/// Instead, proper concurrency handling must be implemented inside the
+/// object so that it becomes Sync and can be used in the multiples threads.
+///
+pub trait TracerCapability: Sync {
+ fn open(obj: &mut Object) -> Option<impl TracerCapability>
+ where
+ Self: Sized;
+
+ fn trace(&self, tick: Tick, entries: &Trace);
+}
+
+#[allow(non_camel_case_types)]
+pub type TracerCapabilityActivate =
+ unsafe extern "C" fn(
+ obj: &mut Object,
+ opaque: &mut MaybeUninit<*mut c_void>,
+ ) -> Option<&'static sys::vlc_tracer_operations>;
+
+extern "C" fn tracer_trace(opaque: *const c_void, tick: sys::vlc_tick, entries: sys::Trace) {
+ {
+ let tracer: &dyn TracerCapability =
+ unsafe { &**(opaque as *const Box<dyn TracerCapability>) };
+ tracer.trace(Tick(tick), &entries);
+ }
+}
+
+extern "C" fn tracer_destroy(opaque: *mut c_void) {
+ let tracer: *mut Box<dyn TracerCapability> = opaque as *mut _;
+ let _ = unsafe { Box::from_raw(tracer) };
+}
+
+const TRACER_OPERATIONS: sys::vlc_tracer_operations = sys::vlc_tracer_operations {
+ trace: tracer_trace,
+ destroy: tracer_destroy,
+};
+
+extern "C" fn activate_tracer<T: TracerCapability>(
+ obj: &mut Object,
+ opaque: &mut MaybeUninit<*mut c_void>,
+) -> Option<&'static sys::vlc_tracer_operations> {
+ if let Some(instance) = T::open(obj) {
+ let wrapper: Box<dyn TracerCapability> = Box::try_new(instance).ok()?;
+ let sys = Box::into_raw(Box::try_new(wrapper).ok()?);
+ opaque.write(sys as *mut _);
+ return Some(&TRACER_OPERATIONS);
+ }
+ None
+}
+
+///
+/// Loader type for the "tracer" capability, using the
+/// [TracerCapability] trait.
+///
+/// This is an implementation of the [ModuleProtocol] type for the
+/// [TracerCapability] trait, exposing modules from the declaration
+/// ([`vlcrs_macros::module!`]) of the manifest as a "tracer"
+/// capability for VLC core.
+///
+/// ```
+/// use vlcrs_core::tracer::{Tick, TracerCapability, TracerModuleLoader, Trace};
+/// use vlcrs_core::object::Object;
+/// use vlcrs_macros::module;
+/// struct CustomTracer {}
+///
+/// // Empty implementation
+/// impl TracerCapability for CustomTracer {
+/// fn open(obj: &mut Object) -> Option<impl TracerCapability> {
+/// Some(Self{})
+/// }
+/// fn trace(&self, _tick: Tick, _entries: &Trace) {}
+/// }
+///
+/// module!{
+/// type: CustomTracer (TracerModuleLoader),
+/// capability: "tracer" @ 0,
+/// category: ADVANCED_MISC,
+/// description: "Custom tracer implementation",
+/// shortname: "Custom tracer",
+/// shortcuts: ["customtracer"],
+///}
+/// ````
+pub struct TracerModuleLoader;
+
+impl<T> ModuleProtocol<T> for TracerModuleLoader
+where
+ T: TracerCapability,
+{
+ type Activate = TracerCapabilityActivate;
+ fn activate_function() -> Self::Activate {
+ activate_tracer::<T>
+ }
+}
+
+///
+/// Wrapper around a vlc_tracer implementation from VLC core.
+///
+/// Exposes the public API for using tracers from VLC core.
+///
+pub struct Tracer {
+ tracer: UnsafeCell<NonNull<sys::vlc_tracer>>,
+}
+
+impl Tracer {
+ ///
+ /// Request a new instance of a tracer from LibVLC.
+ ///
+ pub fn create(obj: &Object, name: &CStr) -> Option<Tracer> {
+ let name = name.as_ptr() as *const c_char;
+
+ // SAFETY: sys::vlc_tracer_Create() is safe as long as name is
+ // null-terminated (given by CStr) and Object is valid,
+ // which is given by requiring a reference.
+ let tracer: NonNull<sys::vlc_tracer> = unsafe { sys::vlc_tracer_Create(obj, name)? };
+
+ Some(Self {
+ tracer: UnsafeCell::new(tracer),
+ })
+ }
+
+ ///
+ /// Register the new point at time [tick] with the metadata [entries].
+ ///
+ pub fn trace(&self, tick: Tick, entries: Trace) {
+ unsafe {
+ // SAFETY: TODO
+ let tracer = *self.tracer.get();
+
+ // SAFETY: the pointer `tracer` is guaranteed to be non-null and
+ // nobody else has reference to it.
+ sys::vlc_tracer_TraceWithTs(tracer, tick.0, entries);
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! trace {
+ ($tracer: ident, $ts: expr, $($key:ident = $value:expr)*) => {};
+}
=====================================
src/rust/vlcrs-core/src/tracer/sys.rs
=====================================
@@ -0,0 +1,193 @@
+#![allow(rustdoc::bare_urls)]
+#![allow(rustdoc::broken_intra_doc_links)]
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+/// SPDX-License-Identifier: LGPL-2.1-or-later
+/// Copyright (C) 2024-2025 Alexandre Janniaux <ajanni at videolabs.io>
+///
+/// This program is free software; you can redistribute it and/or modify it
+/// under the terms of the GNU Lesser General Public License as published by
+/// the Free Software Foundation; either version 2.1 of the License, or
+/// (at your option) any later version.
+///
+/// This program is distributed in the hope that it will be useful,
+/// but WITHOUT ANY WARRANTY; without even the implied warranty of
+/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+/// GNU Lesser General Public License for more details.
+///
+/// You should have received a copy of the GNU Lesser General Public License
+/// along with this program; if not, write to the Free Software Foundation,
+/// Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+use std::{
+ ffi::{c_char, c_double, c_void, CStr},
+ ptr::NonNull,
+};
+
+use crate::object::Object;
+
+pub type vlc_tick = i64;
+
+#[repr(C)]
+#[non_exhaustive]
+#[doc = "Tracer message values"]
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum vlc_tracer_value_type {
+ Integer = 0,
+ Double = 1,
+ String = 2,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union vlc_tracer_value {
+ pub integer: i64,
+ pub double: c_double,
+ pub string: *const c_char,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct vlc_tracer_entry {
+ pub key: *const c_char,
+ pub value: vlc_tracer_value,
+ pub kind: vlc_tracer_value_type,
+}
+
+#[repr(C)]
+pub struct vlc_tracer_trace {
+ pub entries: NonNull<vlc_tracer_entry>,
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct vlc_tracer {
+ _unused: [u8; 0],
+}
+
+#[derive(PartialEq, Copy, Clone)]
+#[repr(transparent)]
+pub struct Trace {
+ pub entries: NonNull<vlc_tracer_trace>,
+}
+
+#[derive(PartialEq, Copy, Clone, Debug)]
+#[repr(transparent)]
+pub struct TraceField {
+ pub entry: NonNull<vlc_tracer_entry>,
+}
+
+impl TraceField {
+ pub fn key(&self) -> &str {
+ unsafe {
+ let key = CStr::from_ptr(self.entry.read().key);
+ key.to_str().unwrap()
+ }
+ }
+
+ pub fn kind(&self) -> vlc_tracer_value_type {
+ unsafe { self.entry.read().kind }
+ }
+
+ pub fn value(&self) -> vlc_tracer_value {
+ unsafe { self.entry.read().value }
+ }
+}
+
+pub struct TraceIterator {
+ current_field: NonNull<vlc_tracer_entry>,
+}
+
+impl IntoIterator for Trace {
+ type Item = TraceField;
+ type IntoIter = TraceIterator;
+ fn into_iter(self) -> Self::IntoIter {
+ TraceIterator {
+ current_field: unsafe { self.entries.read().entries },
+ }
+ }
+}
+
+impl Iterator for TraceIterator {
+ type Item = TraceField;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe {
+ if self.current_field.read().key.is_null() {
+ return None;
+ }
+ let output = Some(TraceField {
+ entry: self.current_field,
+ });
+ self.current_field = self.current_field.add(1);
+ output
+ }
+ }
+}
+
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct vlc_tracer_operations {
+ pub trace: extern "C" fn(opaque: *const c_void, ts: vlc_tick, entries: Trace),
+ pub destroy: extern "C" fn(*mut c_void),
+}
+
+extern "C" {
+ pub fn vlc_tracer_Create(parent: &Object, name: *const c_char) -> Option<NonNull<vlc_tracer>>;
+
+ pub fn vlc_tracer_TraceWithTs(tracer: NonNull<vlc_tracer>, tick: vlc_tick, entries: Trace);
+}
+
+#[cfg(test)]
+mod test {
+ #[test]
+ fn test_trace_interop() {
+ use super::*;
+ let entries = [
+ vlc_tracer_entry {
+ kind: vlc_tracer_value_type::String,
+ value: vlc_tracer_value {
+ string: c"value1".as_ptr(),
+ },
+ key: c"test1".as_ptr(),
+ },
+ vlc_tracer_entry {
+ kind: vlc_tracer_value_type::Integer,
+ value: vlc_tracer_value { integer: 0 },
+ key: std::ptr::null(),
+ },
+ ];
+
+ let trace_field = TraceField {
+ entry: NonNull::from(&entries[0]),
+ };
+ assert_eq!(trace_field.kind(), vlc_tracer_value_type::String);
+ assert_eq!(trace_field.key(), "test1");
+
+ let trace_field = TraceField {
+ entry: NonNull::from(&entries[1]),
+ };
+ assert_eq!(trace_field.kind(), vlc_tracer_value_type::Integer);
+
+ let trace = vlc_tracer_trace {
+ entries: NonNull::from(&entries[0]),
+ };
+
+ let trace = Trace {
+ entries: NonNull::from(&trace),
+ };
+
+ let mut iterator = trace.into_iter();
+
+ let first = iterator.next().expect("First field must be valid");
+ assert_eq!(first.kind(), vlc_tracer_value_type::String);
+ assert_eq!(first.key(), "test1");
+ unsafe {
+ assert_eq!(CStr::from_ptr(first.value().string), c"value1");
+ }
+
+ let second = iterator.next();
+ assert_eq!(second, None);
+ }
+}
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fd7e1136bd1026db36434a116079a2500cdefbf3...e8e46b0d915d153a58d002c9d6f19a7dbdfeeca9
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/fd7e1136bd1026db36434a116079a2500cdefbf3...e8e46b0d915d153a58d002c9d6f19a7dbdfeeca9
You're receiving this email because of your account on code.videolan.org.
VideoLAN code repository instance
More information about the vlc-commits
mailing list