Skip to content

Commit

Permalink
Use reference counted type to simplify lifetime management
Browse files Browse the repository at this point in the history
Change the DataTree structure to contain a reference-counting pointer
instead of a regular reference to the associated YANG context. With
this change, the DataTree structure is no longer bound to any
lifetime, improving the API ergonomics and adding more flexibility
(e.g. possibility to send data trees through channels). The only
downside is that YANG contexts can't be modified once wrapped inside
an Arc, but that shouldn't be a limitation for most use cases. This
is a breaking API change that might be revisited in the future.

Signed-off-by: Renato Westphal <renato@opensourcerouting.org>
  • Loading branch information
rwestphal committed Dec 1, 2021
1 parent b6bff0b commit 945368c
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 29 deletions.
2 changes: 2 additions & 0 deletions examples/data_diff.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::sync::Arc;
use yang2::context::{Context, ContextFlags};
use yang2::data::{
Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree,
Expand Down Expand Up @@ -60,6 +61,7 @@ fn main() -> std::io::Result<()> {
ctx.load_module(module_name, None)
.expect("Failed to load module");
}
let ctx = Arc::new(ctx);

// Parse data trees from JSON strings.
let dtree1 = DataTree::parse_string(
Expand Down
2 changes: 2 additions & 0 deletions examples/data_edit.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs::File;
use std::sync::Arc;
use yang2::context::{Context, ContextFlags};
use yang2::data::{
Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree,
Expand All @@ -24,6 +25,7 @@ fn main() -> std::io::Result<()> {
ctx.load_module(module_name, None)
.expect("Failed to load module");
}
let ctx = Arc::new(ctx);

// Parse data tree from JSON file.
let mut dtree = DataTree::parse_file(
Expand Down
2 changes: 2 additions & 0 deletions examples/data_iteration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs::File;
use std::sync::Arc;
use yang2::context::{Context, ContextFlags};
use yang2::data::{
Data, DataFormat, DataParserFlags, DataTree, DataValidationFlags,
Expand All @@ -18,6 +19,7 @@ fn main() -> std::io::Result<()> {
ctx.load_module(module_name, None)
.expect("Failed to load module");
}
let ctx = Arc::new(ctx);

// Parse data tree in the JSON format.
let dtree = DataTree::parse_file(
Expand Down
2 changes: 2 additions & 0 deletions examples/data_json2xml.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::fs::File;
use std::sync::Arc;
use yang2::context::{Context, ContextFlags};
use yang2::data::{
Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree,
Expand All @@ -19,6 +20,7 @@ fn main() -> std::io::Result<()> {
ctx.load_module(module_name, None)
.expect("Failed to load module");
}
let ctx = Arc::new(ctx);

// Parse data tree in the JSON format.
let dtree = DataTree::parse_file(
Expand Down
59 changes: 33 additions & 26 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::ffi::CString;
use std::os::raw::{c_char, c_void};
use std::os::unix::io::AsRawFd;
use std::slice;
use std::sync::Arc;

use crate::context::Context;
use crate::error::{Error, Result};
Expand All @@ -23,15 +24,15 @@ use libyang2_sys as ffi;

/// YANG data tree.
#[derive(Debug)]
pub struct DataTree<'a> {
context: &'a Context,
pub struct DataTree {
context: Arc<Context>,
raw: *mut ffi::lyd_node,
}

/// YANG data node reference.
#[derive(Clone, Debug)]
pub struct DataNodeRef<'a> {
tree: &'a DataTree<'a>,
tree: &'a DataTree,
raw: *mut ffi::lyd_node,
}

Expand All @@ -49,8 +50,8 @@ pub struct Metadata<'a> {

/// YANG data tree diff.
#[derive(Debug)]
pub struct DataDiff<'a> {
tree: DataTree<'a>,
pub struct DataDiff {
tree: DataTree,
}

/// YANG data diff operation.
Expand Down Expand Up @@ -295,18 +296,18 @@ pub trait Data {

// ===== impl DataTree =====

impl<'a> DataTree<'a> {
impl DataTree {
/// Create new empty data tree.
pub fn new(context: &Context) -> DataTree {
pub fn new(context: &Arc<Context>) -> DataTree {
DataTree {
context: &context,
context: context.clone(),
raw: std::ptr::null_mut(),
}
}

/// Parse (and validate) input data as a YANG data tree.
pub fn parse_file<F: AsRawFd>(
context: &Context,
context: &Arc<Context>,
fd: F,
format: DataFormat,
parser_options: DataParserFlags,
Expand Down Expand Up @@ -334,12 +335,12 @@ impl<'a> DataTree<'a> {

/// Parse (and validate) input data as a YANG data tree.
pub fn parse_string(
context: &'a Context,
context: &Arc<Context>,
data: &str,
format: DataFormat,
parser_options: DataParserFlags,
validation_options: DataValidationFlags,
) -> Result<DataTree<'a>> {
) -> Result<DataTree> {
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
let data = CString::new(data).unwrap();
Expand Down Expand Up @@ -457,7 +458,7 @@ impl<'a> DataTree<'a> {
}

/// Create a copy of the data tree.
pub fn duplicate(&self) -> Result<DataTree<'a>> {
pub fn duplicate(&self) -> Result<DataTree> {
let mut dup = std::ptr::null_mut();
let dup_ptr = &mut dup;

Expand Down Expand Up @@ -485,7 +486,7 @@ impl<'a> DataTree<'a> {
/// Merge the source data tree into the target data tree. Merge may not be
/// complete until validation is called on the resulting data tree (data
/// from more cases may be present, default and non-default values).
pub fn merge(&mut self, source: &DataTree<'a>) -> Result<()> {
pub fn merge(&mut self, source: &DataTree) -> Result<()> {
// Special handling for empty data trees.
if self.raw.is_null() {
*self = source.duplicate()?;
Expand Down Expand Up @@ -533,7 +534,7 @@ impl<'a> DataTree<'a> {
/// metadata ('orig-default', 'value', 'orig-value', 'key', 'orig-key')
/// are used for storing more information about the value in the first
/// or the second tree.
pub fn diff(&self, dtree: &DataTree<'a>) -> Result<DataDiff<'a>> {
pub fn diff(&self, dtree: &DataTree) -> Result<DataDiff> {
let options = ffi::LYD_DIFF_DEFAULTS as u16;
let mut rnode = std::ptr::null_mut();
let rnode_ptr = &mut rnode;
Expand Down Expand Up @@ -563,31 +564,37 @@ impl<'a> DataTree<'a> {

/// Returns an iterator over all elements in the data tree and its sibling
/// trees (depth-first search algorithm).
pub fn traverse(&'a self) -> impl Iterator<Item = DataNodeRef<'a>> {
pub fn traverse<'a>(&'a self) -> impl Iterator<Item = DataNodeRef<'a>> {
let top = Siblings::new(self.reference());
top.flat_map(|dnode| dnode.traverse())
}
}

impl<'a> Data for DataTree<'a> {
impl Data for DataTree {
fn tree(&self) -> &DataTree {
&self
}
}

impl<'a> Binding<'a> for DataTree<'a> {
impl<'a> Binding<'a> for DataTree {
type CType = ffi::lyd_node;
type Container = Context;
type Container = Arc<Context>;

fn from_raw(context: &'a Context, raw: *mut ffi::lyd_node) -> DataTree {
DataTree { context, raw }
fn from_raw(
context: &'a Arc<Context>,
raw: *mut ffi::lyd_node,
) -> DataTree {
DataTree {
context: context.clone(),
raw,
}
}
}

unsafe impl Send for DataTree<'_> {}
unsafe impl Sync for DataTree<'_> {}
unsafe impl Send for DataTree {}
unsafe impl Sync for DataTree {}

impl<'a> Drop for DataTree<'a> {
impl Drop for DataTree {
fn drop(&mut self) {
unsafe { ffi::lyd_free_all(self.raw) };
}
Expand Down Expand Up @@ -726,7 +733,7 @@ impl<'a> Data for DataNodeRef<'a> {

impl<'a> Binding<'a> for DataNodeRef<'a> {
type CType = ffi::lyd_node;
type Container = DataTree<'a>;
type Container = DataTree;

fn from_raw(
tree: &'a DataTree,
Expand Down Expand Up @@ -827,7 +834,7 @@ unsafe impl Sync for Metadata<'_> {}

// ===== impl DataDiff =====

impl<'a> DataDiff<'a> {
impl DataDiff {
/// Returns an iterator over the data changes.
pub fn iter(&self) -> impl Iterator<Item = (DataDiffOp, DataNodeRef<'_>)> {
self.tree.traverse().filter_map(|dnode| {
Expand Down Expand Up @@ -863,7 +870,7 @@ impl<'a> DataDiff<'a> {
}
}

impl<'a> Data for DataDiff<'a> {
impl Data for DataDiff {
fn tree(&self) -> &DataTree {
&self.tree
}
Expand Down
7 changes: 4 additions & 3 deletions tests/data.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::collections::BTreeSet;
use std::sync::Arc;
use yang2::context::{Context, ContextFlags};
use yang2::data::{
Data, DataFormat, DataImplicitFlags, DataParserFlags, DataPrinterFlags,
Expand Down Expand Up @@ -161,7 +162,7 @@ macro_rules! assert_data_eq {
};
}

fn create_context() -> Context {
fn create_context() -> Arc<Context> {
// Initialize context.
let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY)
.expect("Failed to create context");
Expand All @@ -180,10 +181,10 @@ fn create_context() -> Context {
.expect("Failed to load module");
}

ctx
Arc::new(ctx)
}

fn parse_json_data<'a>(ctx: &'a Context, string: &str) -> DataTree<'a> {
fn parse_json_data(ctx: &Arc<Context>, string: &str) -> DataTree {
DataTree::parse_string(
&ctx,
string,
Expand Down

0 comments on commit 945368c

Please sign in to comment.