use core::{
cmp::Ordering,
fmt::{self, Debug},
iter::FusedIterator,
marker::PhantomData,
ops::Index,
};
use evil_janet::{JanetKV, JanetStructHead};
use super::{DeepEq, Janet, JanetTable};
#[derive(Debug)]
#[must_use = "builder cannot be utilized as a proper JanetStruct, use the `finish` method"]
pub struct JanetStructBuilder<'data> {
raw: *mut JanetKV,
phantom: PhantomData<&'data ()>,
}
impl<'data> JanetStructBuilder<'data> {
#[inline]
pub fn put(self, key: impl Into<Janet>, value: impl Into<Janet>) -> Self {
let (key, value) = (key.into(), value.into());
unsafe { evil_janet::janet_struct_put(self.raw, key.into(), value.into()) }
self
}
#[inline]
#[must_use = "function finishes building process and returns JanetStruct"]
pub fn finalize(self) -> JanetStruct<'data> {
JanetStruct {
raw: unsafe { evil_janet::janet_struct_end(self.raw) },
phantom: PhantomData,
}
}
}
#[repr(transparent)]
pub struct JanetStruct<'data> {
pub(crate) raw: *const JanetKV,
phantom: PhantomData<&'data ()>,
}
impl<'data> JanetStruct<'data> {
#[inline]
pub fn builder(len: usize) -> JanetStructBuilder<'data> {
let len = i32::try_from(len).unwrap_or(i32::MAX);
JanetStructBuilder {
raw: unsafe { evil_janet::janet_struct_begin(len) },
phantom: PhantomData,
}
}
#[inline]
#[must_use = "function is a constructor associated function"]
pub const unsafe fn from_raw(raw: *const JanetKV) -> Self {
Self {
raw,
phantom: PhantomData,
}
}
#[inline]
fn head(&self) -> &JanetStructHead {
unsafe { &*evil_janet::janet_struct_head(self.raw) }
}
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn capacity(&self) -> usize {
self.head().capacity as usize
}
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn len(&self) -> usize {
self.head().length as usize
}
#[inline]
#[must_use = "this returns the result of the operation, without modifying the original"]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn get(&self, key: impl Into<Janet>) -> Option<&Janet> {
self.get_key_value(key).map(|(_, v)| v)
}
#[inline]
pub fn get_key_value(&self, key: impl Into<Janet>) -> Option<(&Janet, &Janet)> {
let key = key.into();
if key.is_nil() {
None
} else {
let kv = unsafe { evil_janet::janet_struct_find(self.raw, key.into()) };
if kv.is_null() {
None
} else {
unsafe {
let kv: *const (Janet, Janet) = kv as *const _;
if (*kv).1.is_nil() {
None
} else {
Some((&(*kv).0, &(*kv).1))
}
}
}
}
}
#[inline]
pub(crate) unsafe fn get_unchecked(&self, key: impl Into<Janet>) -> &'data Janet {
self.get_key_value_unchecked(key).1
}
#[inline]
pub(crate) unsafe fn get_key_value_unchecked(
&self, key: impl Into<Janet>,
) -> (&Janet, &'data mut Janet) {
let key = key.into();
let kv: *mut (Janet, Janet) = evil_janet::janet_struct_find(self.raw, key.inner) as *mut _;
(&(*kv).0, &mut (*kv).1)
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get_owned(&self, key: impl Into<Janet>) -> Option<Janet> {
let key = key.into();
if key.is_nil() {
None
} else {
let val: Janet = unsafe { evil_janet::janet_struct_get(self.raw, key.inner).into() };
if val.is_nil() { None } else { Some(val) }
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn get_owned_key_value(&self, key: impl Into<Janet>) -> Option<(Janet, Janet)> {
let key = key.into();
self.get_owned(key).map(|v| (key, v))
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn find(&self, key: impl Into<Janet>) -> Option<(&Janet, &Janet)> {
let key = key.into();
if key.is_nil() {
None
} else {
let kv = unsafe { evil_janet::janet_struct_find(self.raw, key.into()) };
if kv.is_null() {
None
} else {
unsafe {
let kv: *const (Janet, Janet) = kv as *const _;
if kv.is_null() {
None
} else {
Some((&(*kv).0, &(*kv).1))
}
}
}
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn contains_key(&self, key: impl Into<Janet>) -> bool {
self.get(key).is_some()
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn contains(&self, value: impl Into<Janet>) -> bool {
let value = value.into();
self.values().any(|&v| v == value)
}
#[inline]
pub fn keys(&self) -> Keys<'_, '_> {
Keys { inner: self.iter() }
}
#[inline]
pub fn values(&self) -> Values<'_, '_> {
Values { inner: self.iter() }
}
#[inline]
pub fn iter(&self) -> Iter<'_, '_> {
let cap = self.capacity() as isize;
Iter {
st: self,
kv: self.raw,
end: unsafe { self.raw.offset(cap) },
}
}
#[inline]
#[must_use]
pub const fn as_raw(&self) -> *const JanetKV {
self.raw
}
}
impl Debug for JanetStruct<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_map().entries(self.iter()).finish()
}
}
impl Clone for JanetStruct<'_> {
#[cfg_attr(feature = "inline-more", inline)]
fn clone(&self) -> Self {
let len = self.len();
let mut clone = JanetStruct::builder(len);
for (k, v) in self {
clone = clone.put(k, v);
}
clone.finalize()
}
}
impl Default for JanetStruct<'_> {
#[inline]
fn default() -> Self {
crate::structs! {}
}
}
impl PartialEq for JanetStruct<'_> {
#[inline]
fn eq(&self, other: &Self) -> bool {
if self.raw.eq(&other.raw) {
return true;
}
if self.head().hash.eq(&other.head().hash) {
return true;
}
if self.len().eq(&other.len()) {
return self.iter().zip(other.iter()).all(|(s, o)| s.eq(&o));
}
false
}
}
impl Eq for JanetStruct<'_> {}
impl PartialOrd for JanetStruct<'_> {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for JanetStruct<'_> {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
use core::cmp::Ordering::{Equal, Greater, Less};
match self.len().cmp(&other.len()) {
x @ (Less | Greater) => x,
Equal => match self.head().hash.cmp(&other.head().hash) {
x @ (Less | Greater) => x,
Equal => {
for (s, ref o) in self.iter().zip(other.iter()) {
match s.cmp(o) {
x @ (Less | Greater) => return x,
Equal => continue,
}
}
Equal
},
},
}
}
}
impl DeepEq for JanetStruct<'_> {
#[inline]
fn deep_eq(&self, other: &Self) -> bool {
if self.len() == other.len() {
return self.iter().all(|(s_key, s_val)| {
if let Some(o_val) = other.get(s_key) {
s_val.deep_eq(o_val)
} else {
false
}
});
}
false
}
}
impl DeepEq<JanetTable<'_>> for JanetStruct<'_> {
#[inline]
fn deep_eq(&self, other: &JanetTable<'_>) -> bool {
if self.len() == other.len() {
return self.iter().all(|(s_key, s_val)| {
if let Some(o_val) = other.get(s_key) {
s_val.deep_eq(o_val)
} else {
false
}
});
}
false
}
}
impl From<JanetTable<'_>> for JanetStruct<'_> {
#[inline]
fn from(table: JanetTable<'_>) -> Self {
From::<&JanetTable<'_>>::from(&table)
}
}
impl From<&JanetTable<'_>> for JanetStruct<'_> {
#[cfg_attr(feature = "inline-more", inline)]
fn from(table: &JanetTable<'_>) -> Self {
let mut st = Self::builder(table.len());
for (k, v) in table {
st = st.put(k, v);
}
st.finalize()
}
}
impl<T: Into<Janet>> Index<T> for JanetStruct<'_> {
type Output = Janet;
#[inline]
fn index(&self, key: T) -> &Self::Output {
self.get(key)
.unwrap_or_else(|| crate::jpanic!("no entry found for key"))
}
}
impl<'data> IntoIterator for JanetStruct<'data> {
type IntoIter = IntoIter<'data>;
type Item = (Janet, Janet);
#[inline]
fn into_iter(self) -> Self::IntoIter {
let cap = self.capacity() as isize;
let kv = self.raw;
let end = unsafe { self.raw.offset(cap) };
IntoIter { st: self, kv, end }
}
}
impl<'a, 'data> IntoIterator for &'a JanetStruct<'data> {
type IntoIter = Iter<'a, 'data>;
type Item = (&'a Janet, &'a Janet);
#[inline]
fn into_iter(self) -> Self::IntoIter {
let cap = self.capacity() as isize;
Iter {
st: self,
kv: self.raw,
end: unsafe { self.raw.offset(cap) },
}
}
}
impl<U, J> FromIterator<(U, J)> for JanetStruct<'_>
where
U: Into<Janet>,
J: Into<Janet>,
{
#[cfg_attr(feature = "inline-more", inline)]
fn from_iter<T: IntoIterator<Item = (U, J)>>(iter: T) -> Self {
let iter = iter.into_iter().collect::<JanetTable>().into_iter();
let (lower, upper) = iter.size_hint();
let mut new = if let Some(upper) = upper {
Self::builder(upper)
} else {
Self::builder(lower)
};
for (k, v) in iter {
new = new.put(k, v);
}
new.finalize()
}
}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Iter<'a, 'data> {
st: &'a JanetStruct<'data>,
kv: *const JanetKV,
end: *const JanetKV,
}
impl Debug for Iter<'_, '_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.st.iter()).finish()
}
}
impl<'a> Iterator for Iter<'a, '_> {
type Item = (&'a Janet, &'a Janet);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
while self.kv < self.end {
let ptr = self.kv as *const (Janet, Janet);
if !(*ptr).0.is_nil() {
self.kv = self.kv.add(1);
return Some((&(*ptr).0, &(*ptr).1));
}
self.kv = self.kv.add(1);
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let exact = self.end as usize - self.kv as usize;
(exact, Some(exact))
}
}
impl ExactSizeIterator for Iter<'_, '_> {}
impl FusedIterator for Iter<'_, '_> {}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Keys<'a, 'data> {
inner: Iter<'a, 'data>,
}
impl Debug for Keys<'_, '_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.inner.st.keys()).finish()
}
}
impl<'a> Iterator for Keys<'a, '_> {
type Item = &'a Janet;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(k, _)| k)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for Keys<'_, '_> {}
impl FusedIterator for Keys<'_, '_> {}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Values<'a, 'data> {
inner: Iter<'a, 'data>,
}
impl Debug for Values<'_, '_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.inner.st.values()).finish()
}
}
impl<'a> Iterator for Values<'a, '_> {
type Item = &'a Janet;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|(_, v)| v)
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for Values<'_, '_> {}
impl FusedIterator for Values<'_, '_> {}
#[derive(Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IntoIter<'data> {
st: JanetStruct<'data>,
kv: *const JanetKV,
end: *const JanetKV,
}
impl Debug for IntoIter<'_> {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.st.iter()).finish()
}
}
impl Iterator for IntoIter<'_> {
type Item = (Janet, Janet);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
unsafe {
while self.kv < self.end {
let ptr = self.kv as *const (Janet, Janet);
if !(*ptr).0.is_nil() {
self.kv = self.kv.add(1);
return Some(((*ptr).0, (*ptr).1));
}
self.kv = self.kv.add(1);
}
}
None
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
let exact = self.end as usize - self.kv as usize;
(exact, Some(exact))
}
}
impl ExactSizeIterator for IntoIter<'_> {}
impl FusedIterator for IntoIter<'_> {}
#[cfg(all(test, any(feature = "amalgation", feature = "link-system")))]
mod tests {
use super::*;
use crate::{client::JanetClient, structs};
#[test]
fn creation_and_get() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let value1 = Janet::integer(10);
let value2 = Janet::boolean(true);
let st = JanetStruct::builder(0).finalize();
assert!(st.is_empty());
let st = JanetStruct::builder(2)
.put(10.0, value1)
.put(11.0, value2)
.finalize();
assert_eq!(2, st.len());
assert_eq!(Some(&value1), st.get(10.0));
assert_eq!(Some(&value2), st.get(11.0));
assert_eq!(None, st.get(12.0));
Ok(())
}
#[test]
fn get_owned() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let value1 = Janet::integer(10);
let value2 = Janet::boolean(true);
let st = JanetStruct::builder(2)
.put(10.0, value1)
.put(11.0, value2)
.finalize();
assert_eq!(2, st.len());
assert_eq!(Some(value1), st.get_owned(10.0));
assert_eq!(Some(value2), st.get_owned(11.0));
assert_eq!(None, st.get_owned(12.0));
Ok(())
}
#[test]
fn find() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let key1 = Janet::number(10.0);
let key2 = Janet::number(11.0);
let value1 = Janet::integer(10);
let value2 = Janet::boolean(true);
let st = JanetStruct::builder(2)
.put(key1, value1)
.put(key2, value2)
.finalize();
assert_eq!(Some((&key1, &value1)), st.find(key1));
assert_eq!(Some((&key2, &value2)), st.find(key2));
assert_eq!(Some((&Janet::nil(), &Janet::nil())), st.find(1));
assert_eq!(None, st.find(Janet::nil()));
Ok(())
}
#[test]
fn clone() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let key1 = Janet::number(10.0);
let key2 = Janet::number(11.0);
let value1 = Janet::integer(10);
let value2 = Janet::boolean(true);
let st = JanetStruct::builder(2)
.put(key1, value1)
.put(key2, value2)
.finalize();
let clone = st.clone();
assert_ne!(st.raw, clone.raw);
assert_eq!(st.get_key_value(key1), clone.get_key_value(key1));
assert_eq!(st.get_key_value(key2), clone.get_key_value(key2));
Ok(())
}
#[test]
fn iter() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let st = structs! {10 => "dez", 11 => "onze"};
let mut iter = st.iter();
assert_eq!(
iter.next(),
Some((&Janet::integer(10), &Janet::from("dez")))
);
assert_eq!(
iter.next(),
Some((&Janet::integer(11), &Janet::from("onze")))
);
assert_eq!(iter.next(), None);
Ok(())
}
#[test]
fn intoiter() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let st = structs! {10 => "dez", 11 => "onze"};
let mut iter = st.into_iter();
assert_eq!(iter.next(), Some((Janet::integer(10), Janet::from("dez"))));
assert_eq!(iter.next(), Some((Janet::integer(11), Janet::from("onze"))));
assert_eq!(iter.next(), None);
Ok(())
}
#[test]
fn index() -> Result<(), crate::client::Error> {
let _client = JanetClient::init()?;
let st = crate::structs! {1 => 1, 2 => true, 3 => "help"};
assert_eq!(&Janet::from(1), st[1]);
assert_eq!(&Janet::from(true), st[2]);
assert_eq!(&Janet::from("help"), st[3]);
Ok(())
}
}