use std::ptr::NonNull;
use crate::error::Error;
use crate::value::{Value, value_text};
use crate::{Format, ffi};
#[derive(Clone, Copy, Debug)]
pub enum Segment<'a> {
Key(&'a str),
Index(usize),
}
impl<'a> From<&'a str> for Segment<'a> {
fn from(key: &'a str) -> Self {
Segment::Key(key)
}
}
impl From<usize> for Segment<'_> {
fn from(index: usize) -> Self {
Segment::Index(index)
}
}
pub(crate) fn to_ffi_path(path: &[Segment]) -> Vec<ffi::FigPathSegment> {
path.iter()
.map(|seg| match seg {
Segment::Key(k) => ffi::FigPathSegment {
kind: 0,
key_ptr: k.as_ptr(),
key_len: k.len(),
index: 0,
},
Segment::Index(i) => ffi::FigPathSegment {
kind: 1,
key_ptr: std::ptr::null(),
key_len: 0,
index: *i,
},
})
.collect()
}
pub(crate) fn to_ffi_keys<S: AsRef<str>>(keys: &[S]) -> Vec<ffi::FigStr> {
keys.iter()
.map(|k| {
let s = k.as_ref();
ffi::FigStr {
ptr: s.as_ptr(),
len: s.len(),
}
})
.collect()
}
#[derive(Debug)]
pub struct Editor {
raw: NonNull<ffi::FigEditor>,
format: Format,
}
impl Editor {
pub fn open(input: &[u8], format: Format) -> Result<Self, Error> {
let mut raw = std::ptr::null_mut();
let ffi_format: ffi::FigFormat = format.into();
let status = unsafe {
ffi::fig_editor_create(input.as_ptr(), input.len(), ffi_format as i32, &mut raw)
};
Error::from_status(status)?;
let raw = NonNull::new(raw).ok_or(Error::Internal)?;
Ok(Self { raw, format })
}
fn ptr(&self) -> *mut ffi::FigEditor {
self.raw.as_ptr()
}
pub fn replace_value(&mut self, path: &[Segment], value: &Value) -> Result<(), Error> {
let repl = value_text(value, self.format)?;
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_replace_val(self.ptr(), p.as_ptr(), p.len(), repl.as_ptr(), repl.len())
};
Error::from_status(status)
}
pub fn replace_key(&mut self, path: &[Segment], key: &str) -> Result<(), Error> {
let repl = value_text(&Value::Str(key.to_string()), self.format)?;
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_replace_key(self.ptr(), p.as_ptr(), p.len(), repl.as_ptr(), repl.len())
};
Error::from_status(status)
}
pub fn insert_value(
&mut self,
path: &[Segment],
key: &str,
value: &Value,
) -> Result<(), Error> {
let key_text = value_text(&Value::Str(key.to_string()), self.format)?;
let val = value_text(value, self.format)?;
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_insert_key(
self.ptr(),
p.as_ptr(),
p.len(),
key_text.as_ptr(),
key_text.len(),
val.as_ptr(),
val.len(),
)
};
Error::from_status(status)
}
pub fn append_value(&mut self, path: &[Segment], value: &Value) -> Result<(), Error> {
let val = value_text(value, self.format)?;
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_append_seq(self.ptr(), p.as_ptr(), p.len(), val.as_ptr(), val.len())
};
Error::from_status(status)
}
pub fn prepend_value(&mut self, path: &[Segment], value: &Value) -> Result<(), Error> {
let val = value_text(value, self.format)?;
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_prepend_seq(self.ptr(), p.as_ptr(), p.len(), val.as_ptr(), val.len())
};
Error::from_status(status)
}
#[cfg(feature = "serde")]
pub fn replace<T: serde::Serialize + ?Sized>(
&mut self,
path: &[Segment],
value: &T,
) -> Result<(), Error> {
self.replace_value(path, &crate::ser::to_value(value)?)
}
#[cfg(feature = "serde")]
pub fn insert<T: serde::Serialize + ?Sized>(
&mut self,
path: &[Segment],
key: &str,
value: &T,
) -> Result<(), Error> {
self.insert_value(path, key, &crate::ser::to_value(value)?)
}
#[cfg(feature = "serde")]
pub fn append<T: serde::Serialize + ?Sized>(
&mut self,
path: &[Segment],
value: &T,
) -> Result<(), Error> {
self.append_value(path, &crate::ser::to_value(value)?)
}
#[cfg(feature = "serde")]
pub fn prepend<T: serde::Serialize + ?Sized>(
&mut self,
path: &[Segment],
value: &T,
) -> Result<(), Error> {
self.prepend_value(path, &crate::ser::to_value(value)?)
}
pub fn add_leading_comment(&mut self, path: &[Segment], text: &str) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_add_leading_comment(self.ptr(), p.as_ptr(), p.len(), text.as_ptr(), text.len())
};
Error::from_status(status)
}
pub fn set_trailing_comment(&mut self, path: &[Segment], text: &str) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_set_trailing_comment(self.ptr(), p.as_ptr(), p.len(), text.as_ptr(), text.len())
};
Error::from_status(status)
}
pub fn delete_leading_comments(&mut self, path: &[Segment]) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe { ffi::fig_editor_delete_leading_comments(self.ptr(), p.as_ptr(), p.len()) };
Error::from_status(status)
}
pub fn delete_trailing_comment(&mut self, path: &[Segment]) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe { ffi::fig_editor_delete_trailing_comment(self.ptr(), p.as_ptr(), p.len()) };
Error::from_status(status)
}
pub fn delete(&mut self, path: &[Segment]) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe { ffi::fig_editor_delete_key(self.ptr(), p.as_ptr(), p.len()) };
Error::from_status(status)
}
pub fn remove_item(&mut self, path: &[Segment], index: usize) -> Result<(), Error> {
let p = to_ffi_path(path);
let status =
unsafe { ffi::fig_editor_remove_seq_item(self.ptr(), p.as_ptr(), p.len(), index) };
Error::from_status(status)
}
pub fn move_key(&mut self, src_path: &[Segment], dest_path: &[Segment]) -> Result<(), Error> {
let s = to_ffi_path(src_path);
let d = to_ffi_path(dest_path);
let status = unsafe {
ffi::fig_editor_move_key(self.ptr(), s.as_ptr(), s.len(), d.as_ptr(), d.len())
};
Error::from_status(status)
}
pub fn reorder_keys<S: AsRef<str>>(
&mut self,
path: &[Segment],
keys: &[S],
) -> Result<(), Error> {
let p = to_ffi_path(path);
let k = to_ffi_keys(keys);
let status = unsafe {
ffi::fig_editor_reorder_keys(self.ptr(), p.as_ptr(), p.len(), k.as_ptr(), k.len())
};
Error::from_status(status)
}
pub fn move_item(&mut self, path: &[Segment], from: usize, to: usize) -> Result<(), Error> {
let p = to_ffi_path(path);
let status =
unsafe { ffi::fig_editor_move_item(self.ptr(), p.as_ptr(), p.len(), from, to) };
Error::from_status(status)
}
pub fn reorder_items(&mut self, path: &[Segment], indices: &[usize]) -> Result<(), Error> {
let p = to_ffi_path(path);
let status = unsafe {
ffi::fig_editor_reorder_items(
self.ptr(),
p.as_ptr(),
p.len(),
indices.as_ptr(),
indices.len(),
)
};
Error::from_status(status)
}
pub fn source(&self) -> Result<&str, Error> {
let mut ptr: *const u8 = std::ptr::null();
let mut len: usize = 0;
let status = unsafe { ffi::fig_editor_source(self.raw.as_ptr(), &mut ptr, &mut len) };
Error::from_status(status)?;
borrow_str(ptr, len)
}
}
impl Drop for Editor {
fn drop(&mut self) {
unsafe { ffi::fig_editor_destroy(self.raw.as_ptr()) };
}
}
pub(crate) fn borrow_str<'a>(ptr: *const u8, len: usize) -> Result<&'a str, Error> {
if len == 0 {
return Ok("");
}
let bytes = unsafe { std::slice::from_raw_parts(ptr, len) };
std::str::from_utf8(bytes).map_err(|_| Error::Utf8)
}