From 800104aef935cd14f81da8e4277d0cc8f381778b Mon Sep 17 00:00:00 2001 From: Aaron Loyd Date: Sun, 13 Oct 2024 22:28:10 -0500 Subject: [PATCH] Implement most of the rest of the accessors Now I want to refactor it all again. --- doc/todo.md | 19 - examples/addressbook/src/lib.rs | 6 +- recapn/src/field.rs | 959 ++++++++++++++++++++++++++------ 3 files changed, 787 insertions(+), 197 deletions(-) diff --git a/doc/todo.md b/doc/todo.md index 7ee9737..a7c94a0 100644 --- a/doc/todo.md +++ b/doc/todo.md @@ -17,25 +17,6 @@ * We need to test the different patterns that are expected to be usable by users of the library. - [ ] Field accessors! Because someone 5 years ago thought it'd be a good idea to implement them in the library. - - [ ] `PtrVariantOwner` generic accessors - * Needs `by_ref`, `init_field`, `field`, `is_null`, `adopt`, `disown_into`, `try_clear`, and `clear` methods. We can basically just copy `PtrVariantBuilder` for this. - - [ ] `PtrFieldBuilder` for `List` - * Needs `try_set`, `set`, and `try_init` - - [ ] `PtrFieldOwner` for `List` - * Copy from `PtrFieldBuilder`. Change lifetimes as appropriate. - - [ ] `PtrVariantBuilder` for `List` - * Needs `get`, `try_set`, `set`, `init`, `try_init`, `init_with_size`. Essentially just the same accessors as `PtrFieldBuilder` but based on the variant helpers. - * We also need the accessors from `List` which is slightly different due to the lack of fixed size to check. - - [ ] `PtrVariantOwner` for `List` - * Copy from `PtrVariantBuilder`. - - [ ] `PtrFieldOwner` and `PtrVariantOwner` for `Struct` - - [ ] `PtrVariantReader` and`PtrVariantBuilder` for `Text` - - [ ] `PtrVariantReader` and `PtrVariantBuilder` for `Data` - - [ ] `PtrVariantReader`, `PtrVariantBuilder`, and `PtrVariantOwner` for `AnyPtr` - * Probably just needs an alias or two for the obvious ops. - - [ ] `PtrFieldBuilder`, `PtrFieldOwner`, `PtrVariantReader`, `PtrVariantBuilder`, and `PtrVariantOwner` for `AnyStruct` - - [ ] `PtrFieldBuilder`, `PtrFieldOwner`, `PtrVariantReader`, `PtrVariantBuilder`, and `PtrVariantOwner` for `AnyList` - - [ ] `PtrFieldReader`, `PtrFieldBuilder`, `PtrVariantReader`, and `PtrVariantBuilder` for `Capability` - [ ] Support "any" capability. We need some sort of marker for the type. Other than that it just returns whatever the `CapSystem`'s `Cap` type is. - [ ] List accessors! Same thing as field accessors, but for list elements! - As a whole, list accessors need to be refactored to be more inline with field accessors in their current state. This brings with it better reusability of ptr element accessors like `is_null`, `clear`, `adopt`, and `disown_into`. diff --git a/examples/addressbook/src/lib.rs b/examples/addressbook/src/lib.rs index a8e8339..22df97d 100644 --- a/examples/addressbook/src/lib.rs +++ b/examples/addressbook/src/lib.rs @@ -20,17 +20,13 @@ pub fn write_address_book(target: &mut W) { alice.id().set(123); alice.name().set(text!("Alice")); alice.email().set(text!("alice@example.com")); + alice.employment().school().set_str("MIT"); let mut alice_phones = alice.phones().init(1); let mut phone = alice_phones.at(0).get(); phone.number().set(text!("555-1212")); phone.r#type().set(person::phone_number::Type::Mobile); - let mut employment = alice.employment(); - let school = employment.school(); - let field = school.init_field(); - field.set_str("MIT"); - let mut bob = people.at(1).get(); bob.id().set(456); bob.name().set(text!("Bob")); diff --git a/recapn/src/field.rs b/recapn/src/field.rs index 3fd51dd..11dd14e 100644 --- a/recapn/src/field.rs +++ b/recapn/src/field.rs @@ -48,7 +48,7 @@ use crate::orphan::{Orphan, Orphanage}; use crate::ptr::{ self, CopySize, Data, ElementCount, ErrorHandler, IgnoreErrors, StructSize, UnwrapErrors, }; -use crate::rpc::{Capable, Empty, InsertableInto, Table}; +use crate::rpc::{BreakableCapSystem, CapTable, Capable, Empty, InsertableInto, Table}; use crate::ty::{self, EnumResult, ListValue, StructReader as _}; use crate::{any, data, text, Error, Family, NotInSchema, Result}; @@ -989,7 +989,7 @@ pub type PtrVariantOwner<'p, T, V> = PtrVariant>; impl<'b, 'p, T: Table, V: PtrValue> PtrVariantReader<'b, 'p, T, V> { #[inline] - fn raw_ptr_or_null(&self) -> ptr::PtrReader<'b, T> { + fn raw_ptr_or_null(&self) -> ptr::PtrReader<'p, T> { if self.is_set() { self.repr.ptr_field(self.descriptor.slot as u16) } else { @@ -1009,10 +1009,13 @@ impl<'b, 'p, T: Table, V: PtrValue> PtrVariantReader<'b, 'p, T, V> { impl<'b, B: BuildAccessablePtr<'b>, V: PtrValue> PtrVariant { #[inline] fn raw_build_ptr(&mut self) -> Option> { - self.is_set().then(|| unsafe { - self.repr - .ptr_field(self.descriptor.slot as u16) - }) + self.is_set().then(|| self.raw_build_field_ptr()) + } + + /// Get a pointer build for this field without checking to make sure it's set. + #[inline] + fn raw_build_field_ptr(&mut self) -> ptr::PtrBuilder<'_, B::Table> { + unsafe { self.repr.ptr_field(self.descriptor.slot as u16) } } #[inline] @@ -1025,7 +1028,17 @@ impl<'b, B: BuildAccessablePtr<'b>, V: PtrValue> PtrVariant { } #[inline] - pub fn init_field(mut self) -> PtrField { + fn set_case_and_build_ptr(&mut self) -> ptr::PtrBuilder<'_, B::Table> { + let was_set = self.set_case(); + let mut ptr = self.raw_build_field_ptr(); + if !was_set { + ptr.clear(); + } + ptr + } + + #[inline] + pub fn init_case(mut self) -> PtrField { let was_set = self.set_case(); let mut field = PtrField { descriptor: self.descriptor, @@ -1127,10 +1140,7 @@ impl FieldType for List { field_type_items! {} } -impl<'b, 'p, T: Table, V> PtrFieldReader<'b, 'p, T, List> -where - V: ty::DynListValue, -{ +impl<'p, T: Table, V: ty::DynListValue> PtrFieldReader<'_, 'p, T, List> { #[inline] fn default(&self) -> list::Reader<'static, V, T> { match &self.descriptor.default { @@ -1169,9 +1179,9 @@ where } } -impl<'b, 'p, T: Table, V> PtrFieldReader<'b, 'p, T, List> +impl<'p, T: Table, V: ty::DynListValue> PtrFieldReader<'_, 'p, T, List> where - V: ty::DynListValue + for<'lb> list::ListAccessable<&'lb list::Reader<'p, V, T>>, + V: for<'lb> list::ListAccessable<&'lb list::Reader<'p, V, T>>, { #[inline] pub fn iter_by(&self, strat: S) -> list::Iter<'p, V, S, T> @@ -1182,9 +1192,9 @@ where } } -impl<'b, 'p, T: Table, V, Item> IntoIterator for PtrFieldReader<'b, 'p, T, List> +impl<'p, T: Table, V: ty::DynListValue, Item> IntoIterator for PtrFieldReader<'_, 'p, T, List> where - V: ty::DynListValue + for<'lb> list::ListAccessable<&'lb list::Reader<'p, V, T>>, + V: for<'lb> list::ListAccessable<&'lb list::Reader<'p, V, T>>, InfalliblePtrs: for<'lb> list::IterStrategy, Item = Item>, { type IntoIter = list::Iter<'p, V, InfalliblePtrs, T>; @@ -1196,7 +1206,7 @@ where } } -impl<'b, 'p, T: Table, V: ty::DynListValue> PtrVariantReader<'b, 'p, T, List> { +impl<'p, T: Table, V: ty::DynListValue> PtrVariantReader<'_, 'p, T, List> { #[inline] fn default(&self) -> list::Reader<'static, V, T> { match &self.descriptor.default { @@ -1237,7 +1247,7 @@ impl<'b, 'p, T: Table, V: ty::DynListValue> PtrVariantReader<'b, 'p, T, List> } } -impl<'b, 'p, T: Table, V: ty::ListValue> PtrFieldBuilder<'b, 'p, T, List> { +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>, V: ty::ListValue> PtrField, B> { /// Returns a builder for the field. If it's not set or a error occurs while reading the /// existing value, the default is set instead. #[inline] @@ -1260,14 +1270,11 @@ impl<'b, 'p, T: Table, V: ty::ListValue> PtrFieldBuilder<'b, 'p, T, List> { } #[inline] - pub fn try_set( + pub fn try_set( &mut self, value: &list::Reader<'_, V, impl InsertableInto>, err_handler: E, - ) -> Result<(), E::Error> - where - E: ErrorHandler, - { + ) -> Result<(), E::Error> { self.raw_build_ptr().try_set_list( value.as_ref(), CopySize::Minimum(V::ELEMENT_SIZE), @@ -1282,18 +1289,18 @@ impl<'b, 'p, T: Table, V: ty::ListValue> PtrFieldBuilder<'b, 'p, T, List> { #[inline] pub fn init(self, count: u32) -> list::Builder<'b, V, T> { - let count = ElementCount::new(count).expect("too many elements for list"); - List::new(self.into_raw_build_ptr().init_list(V::ELEMENT_SIZE, count)) + self.try_init(count).expect("too many elements for list") } #[inline] pub fn try_init(self, count: u32) -> Result, TooManyElementsError> { - let max = V::ELEMENT_SIZE.max_elements().get(); - if count > max { - return Err(TooManyElementsError(())); + let size = V::ELEMENT_SIZE; + if count > size.max_elements().get() { + return Err(TooManyElementsError(())) } + let count = ElementCount::new(count).unwrap(); - Ok(self.init(count)) + Ok(List::new(self.into_raw_build_ptr().init_list(size, count))) } /// Initialize a new instance with the given element size. @@ -1303,73 +1310,154 @@ impl<'b, 'p, T: Table, V: ty::ListValue> PtrFieldBuilder<'b, 'p, T, List> { #[inline] pub fn init_with_size(self, count: u32, size: ElementSize) -> list::Builder<'b, V, T> { assert_eq!(V::ELEMENT_SIZE.upgrade_to(size), Some(size)); - let count = ElementCount::new(count).expect("too many elements for list"); + assert!(count < size.max_elements().get(), "too many elements for list"); + let count = ElementCount::new(count).unwrap(); List::new(self.into_raw_build_ptr().init_list(size, count)) } } -impl<'b, 'p, T: Table> PtrFieldBuilder<'b, 'p, T, List> { +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>> PtrField, B> { /// Returns a builder for the field. If it's not set or a error occurs while reading the /// existing value, the default is set instead. #[inline] - pub fn get(self, expected_size: Option) -> list::Builder<'b, AnyStruct, T> { + pub fn get(self, size: StructSize) -> list::Builder<'b, AnyStruct, T> { + let size = ElementSize::InlineComposite(size); let default = &self.descriptor.default; let ptr = match self .into_raw_build_ptr() - .to_list_mut(expected_size.map(ElementSize::InlineComposite)) + .to_list_mut(Some(size)) { Ok(ptr) => ptr, Err((_, ptr)) => { if let Some(default) = default { let default_size = default.element_size(); - let size = match expected_size { - Some(e) => { - let expected = ElementSize::InlineComposite(e); - default_size - .upgrade_to(expected) - .expect("default value can't be upgraded to struct list!") - } - None => default_size, - }; + let size = default_size.upgrade_to(size) + .expect("default value should be upgradable to struct list"); let mut builder = ptr.init_list(size, default.len()); builder.try_copy_from(default, UnwrapErrors).unwrap(); builder } else { - let s = - ElementSize::InlineComposite(expected_size.unwrap_or(StructSize::EMPTY)); - ptr.init_list(s, ElementCount::ZERO) + ptr.init_list(size, ElementCount::ZERO) } } }; List::new(ptr) } + #[inline] + pub fn try_set( + &mut self, + value: &list::Reader<'_, AnyStruct, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.raw_build_ptr().try_set_list( + value.as_ref(), + CopySize::FromValue, + err_handler, + ) + } + + #[inline] + pub fn set(&mut self, value: &list::Reader<'_, AnyStruct, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() + } + + #[inline] + pub fn init(self, size: StructSize, count: u32) -> list::Builder<'b, AnyStruct, T> { + self.try_init(size, count) + .expect("too many elements for list") + } + #[inline] pub fn try_init( self, size: StructSize, count: u32, ) -> Result, TooManyElementsError> { - match ElementCount::new(count) { - None => Err(TooManyElementsError(())), - Some(count) => Ok(List::new( - self.into_raw_build_ptr() - .init_list(ElementSize::InlineComposite(size), count), - )), + let size = ElementSize::InlineComposite(size); + if count > size.max_elements().get() { + return Err(TooManyElementsError(())) } + let count = ElementCount::new(count).unwrap(); + + Ok(List::new(self.into_raw_build_ptr().init_list(size, count))) } +} +impl<'b, T, B, V> PtrVariant, B> +where + T: Table, + B: BuildAccessablePtr<'b, Table = T>, + V: ty::ListValue, +{ #[inline] - pub fn init(self, size: StructSize, count: u32) -> list::Builder<'b, AnyStruct, T> { - self.try_init(size, count) - .expect("too many elements for list") + pub fn try_set( + &mut self, + value: &list::Reader<'_, V, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.set_case_and_build_ptr() + .try_set_list(value.as_ref(), CopySize::FromValue, err_handler) } - // TODO the rest of the accessors + #[inline] + pub fn set(&mut self, value: &list::Reader<'_, V, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() + } + + #[inline] + pub fn init(self, count: u32) -> list::Builder<'b, V, T> { + self.try_init(count).expect("too many elements for list") + } + + #[inline] + pub fn try_init(self, count: u32) -> Result, TooManyElementsError> { + let size = V::ELEMENT_SIZE; + if count > size.max_elements().get() { + return Err(TooManyElementsError(())) + } + + Ok(self.init_case().init(count)) + } } -impl<'b, 'p, T: Table, V: ty::DynListValue> PtrVariantBuilder<'b, 'p, T, List> { - // TODO acceessors +impl<'b, T, B> PtrVariant, B> +where + T: Table, + B: BuildAccessablePtr<'b, Table = T>, +{ + #[inline] + pub fn try_set( + &mut self, + value: &list::Reader<'_, AnyStruct, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.set_case_and_build_ptr() + .try_set_list(value.as_ref(), CopySize::FromValue, err_handler) + } + + #[inline] + pub fn set(&mut self, value: &list::Reader<'_, AnyStruct, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() + } + + #[inline] + pub fn init(self, size: StructSize, count: u32) -> list::Builder<'b, AnyStruct, T> { + self.try_init(size, count).expect("too many elements for list") + } + + #[inline] + pub fn try_init( + self, + size: StructSize, + count: u32, + ) -> Result, TooManyElementsError> { + if count > ElementSize::InlineComposite(size).max_elements().get() { + return Err(TooManyElementsError(())) + } + + Ok(self.init_case().init(size, count)) + } } impl FieldType for Struct { @@ -1392,10 +1480,7 @@ impl PtrField, Repr> { } } -impl<'b, 'p, T: Table, S> PtrFieldReader<'b, 'p, T, Struct> -where - S: ty::Struct, -{ +impl<'p, T: Table, S: ty::Struct> PtrFieldReader<'_, 'p, T, Struct> { #[inline] fn default_imbued_ptr(&self) -> ptr::StructReader<'static, T> { self.default_ptr().imbue_from(self.repr) @@ -1435,8 +1520,10 @@ where } } -impl<'b, 'p: 'b, T: Table + 'p, S> PtrFieldBuilder<'b, 'p, T, Struct> +impl<'b, T, B, S> PtrField, B> where + T: Table, + B: BuildAccessablePtr<'b, Table = T>, S: ty::Struct, { /// Returns a builder for the field. If it's not set or a error occurs while reading the @@ -1475,12 +1562,12 @@ where /// If an error occurs while reading the input value, it's passed to the error handler, which /// can choose to write null instead or return an error. #[inline] - pub fn try_set(self, value: &S::Reader<'_, T2>, err_handler: E) -> Result<(), E::Error> - where - T2: InsertableInto, - E: ErrorHandler, - { - self.into_raw_build_ptr().try_set_struct( + pub fn try_set( + &mut self, + value: &S::Reader<'_, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.raw_build_ptr().try_set_struct( value.as_ptr(), ptr::CopySize::Minimum(S::SIZE), err_handler, @@ -1488,10 +1575,7 @@ where } #[inline] - pub fn set(self, value: &S::Reader<'_, T2>) - where - T2: InsertableInto, - { + pub fn set(&mut self, value: &S::Reader<'_, impl InsertableInto>) { self.try_set(value, IgnoreErrors).unwrap() } } @@ -1512,10 +1596,7 @@ impl PtrVariant, Repr> { } } -impl<'b, 'p: 'b, T: Table + 'p, S> PtrVariantReader<'b, 'p, T, Struct> -where - S: ty::Struct, -{ +impl<'p, T: Table, S: ty::Struct> PtrVariantReader<'_, 'p, T, Struct> { #[inline] fn default_imbued_ptr(&self) -> ptr::StructReader<'static, T> { self.descriptor @@ -1526,7 +1607,7 @@ where } #[inline] - pub fn get_or_default(&self) -> S::Reader<'b, T> { + pub fn get_or_default(&self) -> S::Reader<'p, T> { let ptr = match self.raw_ptr_or_null().to_struct() { Ok(Some(ptr)) => ptr, _ => self.default_imbued_ptr(), @@ -1535,12 +1616,12 @@ where } #[inline] - pub fn get_option(&self) -> Option> { + pub fn get_option(&self) -> Option> { self.try_get_option().ok().flatten() } #[inline] - pub fn try_get(&self) -> Result> { + pub fn try_get(&self) -> Result> { let ptr = match self.raw_ptr_or_null().to_struct() { Ok(Some(ptr)) => ptr, Ok(None) => self.default_imbued_ptr(), @@ -1550,7 +1631,7 @@ where } #[inline] - pub fn try_get_option(&self) -> Result>> { + pub fn try_get_option(&self) -> Result>> { self.field() .map(|r| r.try_get_option()) .transpose() @@ -1558,44 +1639,30 @@ where } } -impl<'b, 'p: 'b, T: Table + 'p, S> PtrVariantBuilder<'b, 'p, T, Struct> +impl<'b, T, B, S> PtrVariant, B> where + T: Table, + B: BuildAccessablePtr<'b, Table = T>, S: ty::Struct, { - /// Returns a builder for the field. If it's not set or a error occurs while reading the - /// existing value, the field default is set in its place instead. - #[inline] - pub fn get(self) -> S::Builder<'b, T> { - self.init_field().get() - } - #[inline] pub fn init(self) -> S::Builder<'b, T> { - self.init_field().init() - } - - /// Returns a builder for the field. If it's not set or a error occurs while reading the - /// existing value, the struct default is set instead. - #[inline] - pub fn get_or_init(self) -> S::Builder<'b, T> { - self.init_field().get_or_init() + self.init_case().init() } #[inline] - pub fn try_set( - self, + pub fn try_set( + &mut self, value: &S::Reader<'_, impl InsertableInto>, err_handler: E, - ) -> Result<(), E::Error> - where - E: ErrorHandler, - { - self.init_field().try_set(value, err_handler) + ) -> Result<(), E::Error> { + self.set_case_and_build_ptr() + .try_set_struct(value.as_ptr(), CopySize::Minimum(S::SIZE), err_handler) } #[inline] - pub fn set(self, value: &S::Reader<'_, impl InsertableInto>) { - self.init_field().set(value) + pub fn set(&mut self, value: &S::Reader<'_, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() } } @@ -1615,7 +1682,7 @@ impl PtrField { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, text::Text> { +impl<'p, T: Table> PtrFieldReader<'_, 'p, T, text::Text> { #[inline] pub fn get(&self) -> text::Reader<'p> { self.get_option().unwrap_or_else(|| self.default()) @@ -1657,7 +1724,7 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, text::Text> { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, text::Text> { +impl<'b, B: BuildAccessablePtr<'b>> PtrField { /// Returns a builder for the field. If it's not set or a error occurs while reading the /// existing value, the default is set instead. #[inline] @@ -1688,8 +1755,8 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, text::Text> { } #[inline] - pub fn set(self, value: text::Reader<'_>) -> text::Builder<'b> { - text::Builder::new_unchecked(self.into_raw_build_ptr().set_blob(value.into())) + pub fn set(&mut self, value: text::Reader<'_>) { + self.raw_build_ptr().set_blob(value.into()); } /// Set the text element to a copy of the given string. @@ -1699,30 +1766,128 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, text::Text> { /// If the string is too large to fit in a Cap'n Proto message, this function will /// panic. #[inline] - pub fn set_str(self, value: &str) -> text::Builder<'b> { + pub fn set_str(&mut self, value: &str) { self.try_set_str(value) .ok() .expect("str is too large to fit in a Cap'n Proto message") } #[inline] - pub fn try_set_str(self, value: &str) -> Result, Self> { + pub fn try_set_str(&mut self, value: &str) -> Result<(), text::TryFromStrError> { let Some(len) = u32::try_from(value.len() + 1) .ok() .and_then(text::ByteCount::new) else { - return Err(self); + return Err(text::TryFromStrError(())); }; - let mut builder = self.init_byte_count(len); + let blob = self.raw_build_ptr().init_blob(len.into()); + let mut builder = text::Builder::new_unchecked(blob); builder.as_bytes_mut().copy_from_slice(value.as_bytes()); - Ok(builder) + Ok(()) } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantReader<'b, 'p, T, text::Text> {} +impl PtrVariant { + /// Returns the default value of the field + #[inline] + pub fn default(&self) -> text::Reader<'static> { + self.descriptor.default.unwrap_or_else(text::Reader::empty) + } +} + +impl<'p, T: Table> PtrVariantReader<'_, 'p, T, text::Text> { + #[inline] + pub fn get(&self) -> text::Reader<'p> { + self.get_option().unwrap_or_else(|| self.default()) + } + + /// Gets the text field as a string, returning an error if the value isn't valid UTF-8 text. + /// This is shorthand for `get().as_str()` + /// + /// If the field is null or an error occurs while reading the pointer to the text itself, this + /// returns the default value. + #[inline] + pub fn as_str(&self) -> Result<&'p str, Utf8Error> { + self.get().as_str() + } + + #[inline] + pub fn get_option(&self) -> Option> { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result> { + self.try_get_option() + .map(|op| op.unwrap_or_else(|| self.default())) + } + + #[inline] + pub fn try_get_option(&self) -> Result>> { + match self.raw_ptr_or_null().to_blob() { + Ok(Some(ptr)) => { + let text = + text::Reader::new(ptr).ok_or(Error::TextNotNulTerminated)?; + + Ok(Some(text)) + } + Ok(None) => Ok(None), + Err(err) => Err(err), + } + } +} + +impl<'b, B: BuildAccessablePtr<'b>> PtrVariant { + /// Creates a text builder with the given length in bytes including the null terminator. + /// + /// # Panics + /// + /// If the length is zero or greater than text::ByteCount::MAX this function will + /// panic. + #[inline] + pub fn init(self, len: u32) -> text::Builder<'b> { + self.init_byte_count(text::ByteCount::new(len).expect("invalid text field size")) + } + + #[inline] + pub fn init_byte_count(self, len: text::ByteCount) -> text::Builder<'b> { + self.init_case().init_byte_count(len) + } + + #[inline] + pub fn set(&mut self, value: text::Reader<'_>) { + self.set_case_and_build_ptr().set_blob(value.into()); + } + + /// Set the text element to a copy of the given string. + /// + /// # Panics + /// + /// If the string is too large to fit in a Cap'n Proto message, this function will + /// panic. + #[inline] + pub fn set_str(&mut self, value: &str) { + self.try_set_str(value) + .ok() + .expect("str is too large to fit in a Cap'n Proto message") + } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantBuilder<'b, 'p, T, text::Text> {} + #[inline] + pub fn try_set_str(&mut self, value: &str) -> Result<(), text::TryFromStrError> { + let Some(len) = u32::try_from(value.len() + 1) + .ok() + .and_then(text::ByteCount::new) + else { + return Err(text::TryFromStrError(())); + }; + + let blob = self.set_case_and_build_ptr().init_blob(len.into()); + let mut builder = text::Builder::new_unchecked(blob); + builder.as_bytes_mut().copy_from_slice(value.as_bytes()); + Ok(()) + } +} impl PtrValue for data::Data { type Default = data::Reader<'static>; @@ -1740,25 +1905,25 @@ impl PtrField { } } -impl<'b, 'p, T: Table + 'p> PtrFieldReader<'b, 'p, T, data::Data> { +impl<'p, T: Table> PtrFieldReader<'_, 'p, T, data::Data> { #[inline] - pub fn get(&self) -> data::Reader<'b> { + pub fn get(&self) -> data::Reader<'p> { self.get_option().unwrap_or_else(|| self.default()) } #[inline] - pub fn get_option(&self) -> Option> { + pub fn get_option(&self) -> Option> { self.try_get_option().ok().flatten() } #[inline] - pub fn try_get(&self) -> Result> { + pub fn try_get(&self) -> Result> { self.try_get_option() .map(|op| op.unwrap_or_else(|| self.default())) } #[inline] - pub fn try_get_option(&self) -> Result>> { + pub fn try_get_option(&self) -> Result>> { match self.raw_ptr().to_blob() { Ok(Some(ptr)) => Ok(Some(ptr.into())), Ok(None) => Ok(None), @@ -1767,7 +1932,7 @@ impl<'b, 'p, T: Table + 'p> PtrFieldReader<'b, 'p, T, data::Data> { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, data::Data> { +impl<'b, B: BuildAccessablePtr<'b>> PtrField { /// Returns a builder for the field. If it's not set or a error occurs while reading the /// existing value, the default is set instead. #[inline] @@ -1786,8 +1951,8 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, data::Data> { } #[inline] - pub fn set(self, value: data::Reader<'_>) -> data::Builder<'b> { - self.into_raw_build_ptr().set_blob(value.into()).into() + pub fn set(&mut self, value: data::Reader<'_>) { + self.raw_build_ptr().set_blob(value.into()); } /// Set the data element to a copy of the given slice. @@ -1797,29 +1962,81 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, data::Data> { /// If the slice is too large to fit in a Cap'n Proto message, this function will /// panic. #[inline] - pub fn set_slice(self, value: &[u8]) -> data::Builder<'b> { - self.try_set_slice(value) - .ok() - .expect("slice is too large to fit in a Cap'n Proto message") + pub fn set_slice(&mut self, value: &[u8]) { + self.try_set_slice(value).unwrap() } #[inline] - pub fn try_set_slice(self, value: &[u8]) -> Result, Self> { - let len = u32::try_from(value.len()).ok().and_then(ElementCount::new); - let Some(len) = len else { return Err(self) }; + pub fn try_set_slice(&mut self, value: &[u8]) -> Result<(), data::TryFromSliceError> { + let value = data::Reader::try_from_slice(value)?; + self.set(value); + Ok(()) + } +} - let mut builder = self.init(len); - builder.copy_from_slice(value); - Ok(builder) +impl PtrVariant { + /// Returns the default value of the field + #[inline] + pub fn default(&self) -> data::Reader<'static> { + self.descriptor.default.unwrap_or_else(data::Reader::empty) } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantReader<'b, 'p, T, data::Data> { - // TODO accessors +impl<'p, T: Table> PtrVariantReader<'_, 'p, T, data::Data> { + #[inline] + pub fn get(&self) -> data::Reader<'p> { + self.get_option().unwrap_or_else(|| self.default()) + } + + #[inline] + pub fn get_option(&self) -> Option> { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result> { + self.try_get_option() + .map(|op| op.unwrap_or_else(|| self.default())) + } + + #[inline] + pub fn try_get_option(&self) -> Result>> { + match self.raw_ptr_or_null().to_blob() { + Ok(Some(ptr)) => Ok(Some(ptr.into())), + Ok(None) => Ok(None), + Err(err) => Err(err), + } + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantBuilder<'b, 'p, T, data::Data> { - // TODO accessors +impl<'b, T: Table> PtrVariantBuilder<'b, '_, T, data::Data> { + #[inline] + pub fn init(self, count: ElementCount) -> data::Builder<'b> { + self.init_case().init(count) + } + + #[inline] + pub fn set(&mut self, value: data::Reader<'_>) { + self.set_case_and_build_ptr().set_blob(value.into()); + } + + /// Set the data element to a copy of the given slice. + /// + /// # Panics + /// + /// If the slice is too large to fit in a Cap'n Proto message, this function will + /// panic. + #[inline] + pub fn set_slice(&mut self, value: &[u8]) { + self.try_set_slice(value).unwrap() + } + + #[inline] + pub fn try_set_slice(&mut self, value: &[u8]) -> Result<(), data::TryFromSliceError> { + let value = data::Reader::try_from_slice(value)?; + self.set(value); + Ok(()) + } } impl PtrValue for AnyPtr { @@ -1830,12 +2047,77 @@ impl FieldType for AnyPtr { field_type_items!(); } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantReader<'b, 'p, T, AnyPtr> { - // TODO accessors +impl PtrField { + /// Returns the default value of the field + #[inline] + pub fn default(&self) -> any::PtrReader<'static> { + AnyPtr::from(self.descriptor.default.clone().unwrap_or_default()) + } +} + +impl<'p, T: Table> PtrFieldReader<'_, 'p, T, AnyPtr> { + /// Get the value of the field. An alias for `ptr()` + #[inline] + pub fn get(&self) -> any::PtrReader<'p, T> { + self.ptr() + } + + /// Get the value of the field. If the pointer value is null, this returns the default value. + #[inline] + pub fn get_or_default(&self) -> any::PtrReader<'p, T> { + let ptr = self.ptr(); + if ptr.is_null() { + self.default().imbue_from(&ptr) + } else { + ptr + } + } +} + +impl<'b, B: BuildAccessablePtr<'b>> PtrField { + /// Get the value of the field. An alias for `ptr()` + #[inline] + pub fn get(self) -> any::PtrBuilder<'b, B::Table> { + self.ptr() + } +} + +impl PtrVariant { + /// Returns the default value of the field + #[inline] + pub fn default(&self) -> any::PtrReader<'static> { + AnyPtr::from(self.descriptor.default.clone().unwrap_or_default()) + } +} + +impl<'p, T: Table> PtrVariantReader<'_, 'p, T, AnyPtr> { + /// Get the value of the field or a null pointer if it's not set. + #[inline] + pub fn get_or_null(&self) -> any::PtrReader<'p, T> { + match self.field() { + Some(f) => f.get(), + None => AnyPtr::null().imbue_from(self.repr), + } + } + + /// Get the value of the field. If the pointer value is null, this returns the default value. + #[inline] + pub fn get_or_default(&self) -> any::PtrReader<'p, T> { + let ptr = self.get_or_null(); + if ptr.is_null() { + self.default().imbue_from(&ptr) + } else { + ptr + } + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantBuilder<'b, 'p, T, AnyPtr> { - // TODO accessors +impl<'b, B: BuildAccessablePtr<'b>> PtrVariant { + /// Set the case of the union to this field and get a pointer builder for the field. + #[inline] + pub fn get(self) -> any::PtrBuilder<'b, B::Table> { + self.init_case().get() + } } impl PtrValue for AnyStruct { @@ -1863,30 +2145,30 @@ impl PtrField { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, AnyStruct> { +impl<'p, T: Table> PtrFieldReader<'_, 'p, T, AnyStruct> { #[inline] fn default_imbued(&self) -> any::StructReader<'static, T> { self.default().imbue_from(self.repr) } #[inline] - pub fn get(&self) -> any::StructReader<'b, T> { + pub fn get(&self) -> any::StructReader<'p, T> { self.get_option().unwrap_or_else(|| self.default_imbued()) } #[inline] - pub fn get_option(&self) -> Option> { + pub fn get_option(&self) -> Option> { self.try_get_option().ok().flatten() } #[inline] - pub fn try_get(&self) -> Result> { + pub fn try_get(&self) -> Result> { self.try_get_option() .map(|op| op.unwrap_or_else(|| self.default_imbued())) } #[inline] - pub fn try_get_option(&self) -> Result>> { + pub fn try_get_option(&self) -> Result>> { match self.raw_ptr().to_struct() { Ok(Some(ptr)) => Ok(Some(ptr.into())), Ok(None) => Ok(None), @@ -1895,16 +2177,136 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, AnyStruct> { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, AnyStruct> { - // TODO accessors +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>> PtrField { + /// Returns a builder for the field. If it's not set or a error occurs while reading the + /// existing value, the field default is set in its place instead. + #[inline] + pub fn get(self, size: Option) -> any::StructBuilder<'b, T> { + let default = &self.descriptor.default; + let ptr = match self.into_raw_build_ptr().to_struct_mut(size) { + Ok(ptr) => ptr, + Err((_, ptr)) => match default { + Some(default) => { + let mut new_size = default.size(); + if let Some(size) = size { + new_size = new_size.max(size); + } + let mut builder = ptr.init_struct(new_size); + builder.copy_with_caveats(default, false); + builder + } + None => ptr.init_struct(size.unwrap_or_default()), + } + }; + unsafe { ty::StructBuilder::from_ptr(ptr) } + } + + #[inline] + pub fn init(self, size: StructSize) -> any::StructBuilder<'b, T> { + unsafe { ty::StructBuilder::from_ptr(self.into_raw_build_ptr().init_struct(size)) } + } + + /// Returns a builder for the field. If it's not set or a error occurs while reading the + /// existing value, the struct default is set instead. + #[inline] + pub fn get_or_init(self, size: StructSize) -> any::StructBuilder<'b, T> { + let ptr = self.into_raw_build_ptr().to_struct_mut_or_init(size); + unsafe { ty::StructBuilder::from_ptr(ptr) } + } + + /// Try to set this field to a copy of the given value. + /// + /// If an error occurs while reading the input value, it's passed to the error handler, which + /// can choose to write null instead or return an error. + #[inline] + pub fn try_set( + &mut self, + value: &any::StructReader<'_, impl InsertableInto>, + size: Option, + err_handler: E, + ) -> Result<(), E::Error> { + let size = size.map(ptr::CopySize::Minimum).unwrap_or(CopySize::FromValue); + self.raw_build_ptr().try_set_struct(value.as_ptr(), size, err_handler) + } + + #[inline] + pub fn set( + &mut self, + value: &any::StructReader<'_, impl InsertableInto>, + size: Option, + ) { + self.try_set(value, size, IgnoreErrors).unwrap() + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantReader<'b, 'p, T, AnyStruct> { - // TODO accessors +impl<'p, T: Table> PtrVariantReader<'_, 'p, T, AnyStruct> { + #[inline] + fn default_imbued_ptr(&self) -> ptr::StructReader<'static, T> { + self.descriptor + .default + .clone() + .unwrap_or_else(ptr::StructReader::empty) + .imbue_from(self.repr) + } + + #[inline] + pub fn get_or_default(&self) -> any::StructReader<'p, T> { + let ptr = match self.raw_ptr_or_null().to_struct() { + Ok(Some(ptr)) => ptr, + _ => self.default_imbued_ptr(), + }; + ty::StructReader::from_ptr(ptr) + } + + #[inline] + pub fn get_option(&self) -> Option> { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result> { + let ptr = match self.raw_ptr_or_null().to_struct() { + Ok(Some(ptr)) => ptr, + Ok(None) => self.default_imbued_ptr(), + Err(err) => return Err(err), + }; + Ok(ty::StructReader::from_ptr(ptr)) + } + + #[inline] + pub fn try_get_option(&self) -> Result>> { + self.field() + .map(|r| r.try_get_option()) + .transpose() + .map(Option::flatten) + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantBuilder<'b, 'p, T, AnyStruct> { - // TODO accessors +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>> PtrVariant { + #[inline] + pub fn init(self, size: StructSize) -> any::StructBuilder<'b, T> { + self.init_case().init(size) + } + + #[inline] + pub fn try_set( + &mut self, + value: &any::StructReader<'_, impl InsertableInto>, + size: Option, + err_handler: E, + ) -> Result<(), E::Error> { + let size = size.map(ptr::CopySize::Minimum).unwrap_or(CopySize::FromValue); + self.set_case_and_build_ptr().try_set_struct(value.as_ref(), size, err_handler) + } + + #[inline] + pub fn set( + &mut self, + value: &any::StructReader<'_, impl InsertableInto>, + size: Option, + ) { + self.try_set(value, size, IgnoreErrors).unwrap() + } } impl PtrValue for AnyList { @@ -1932,30 +2334,30 @@ impl PtrField { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, AnyList> { +impl<'p, T: Table> PtrFieldReader<'_, 'p, T, AnyList> { #[inline] fn default_imbued(&self) -> any::ListReader<'static, T> { self.default().imbue_from(self.repr) } #[inline] - pub fn get(&self) -> any::ListReader<'b, T> { + pub fn get(&self) -> any::ListReader<'p, T> { self.get_option().unwrap_or_else(|| self.default_imbued()) } #[inline] - pub fn get_option(&self) -> Option> { + pub fn get_option(&self) -> Option> { self.try_get_option().ok().flatten() } #[inline] - pub fn try_get(&self) -> Result> { + pub fn try_get(&self) -> Result> { self.try_get_option() .map(|op| op.unwrap_or_else(|| self.default_imbued())) } #[inline] - pub fn try_get_option(&self) -> Result>> { + pub fn try_get_option(&self) -> Result>> { match self.raw_ptr().to_list(None) { Ok(Some(ptr)) => Ok(Some(ptr.into())), Ok(None) => Ok(None), @@ -1964,16 +2366,145 @@ impl<'b, 'p: 'b, T: Table + 'p> PtrFieldReader<'b, 'p, T, AnyList> { } } -impl<'b, 'p: 'b, T: Table + 'p> PtrFieldBuilder<'b, 'p, T, AnyList> { - // TODO accessors +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>> PtrField { + /// Returns a builder for the field. If it's not set or a error occurs while reading the + /// existing value, the default is set instead. + #[inline] + pub fn get(self) -> any::ListBuilder<'b, T> { + let default = &self.descriptor.default; + match self.into_raw_build_ptr().to_list_mut(None) { + Ok(ptr) => ptr.into(), + Err((_, ptr)) => { + let new_list = if let Some(default) = default { + let mut new_list = ptr.init_list(default.element_size(), default.len()); + new_list.try_copy_from(default, UnwrapErrors).unwrap(); + new_list + } else { + ptr.init_list(ElementSize::Void, ElementCount::ZERO) + }; + AnyList::from(new_list) + } + } + } + + #[inline] + pub fn try_set( + &mut self, + value: &any::ListReader<'_, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.raw_build_ptr().try_set_list( + value.as_ref(), + CopySize::FromValue, + err_handler, + ) + } + + #[inline] + pub fn set(&mut self, value: &any::ListReader<'_, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() + } + + #[inline] + pub fn init(self, size: ElementSize, count: u32) -> any::ListBuilder<'b, T> { + self.try_init(size, count).expect("too many elements for list") + } + + #[inline] + pub fn try_init( + self, + size: ElementSize, + count: u32, + ) -> Result, TooManyElementsError> { + if count > size.max_elements().get() { + return Err(TooManyElementsError(())) + } + let count = ElementCount::new(count).unwrap(); + + Ok(AnyList::from(self.into_raw_build_ptr().init_list(size, count))) + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantReader<'b, 'p, T, AnyList> { - // TODO accessors +impl<'b, 'p, T: Table> PtrVariantReader<'b, 'p, T, AnyList> { + #[inline] + fn default_imbued_ptr(&self) -> ptr::ListReader<'static, T> { + self.descriptor + .default + .clone() + .unwrap_or_else(|| ptr::ListReader::empty(ElementSize::Void)) + .imbue_from(self.repr) + } + + #[inline] + pub fn get_or_default(&self) -> any::ListReader<'p, T> { + let ptr = match self.raw_ptr_or_null().to_list(None) { + Ok(Some(ptr)) => ptr, + _ => self.default_imbued_ptr(), + }; + AnyList::from(ptr) + } + + #[inline] + pub fn get_option(&self) -> Option> { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result> { + let ptr = match self.raw_ptr_or_null().to_list(None) { + Ok(Some(ptr)) => ptr, + Ok(None) => self.default_imbued_ptr(), + Err(err) => return Err(err), + }; + Ok(AnyList::from(ptr)) + } + + #[inline] + pub fn try_get_option(&self) -> Result>> { + self.field() + .map(|r| r.try_get_option()) + .transpose() + .map(Option::flatten) + } } -impl<'b, 'p: 'b, T: Table + 'p> PtrVariantBuilder<'b, 'p, T, AnyList> { - // TODO accessors +impl<'b, T: Table, B: BuildAccessablePtr<'b, Table = T>> PtrVariant { + #[inline] + pub fn try_set( + &mut self, + value: &any::ListReader<'_, impl InsertableInto>, + err_handler: E, + ) -> Result<(), E::Error> { + self.set_case_and_build_ptr().try_set_list( + value.as_ref(), + CopySize::FromValue, + err_handler, + ) + } + + #[inline] + pub fn set(&mut self, value: &any::ListReader<'_, impl InsertableInto>) { + self.try_set(value, IgnoreErrors).unwrap() + } + + #[inline] + pub fn init(self, size: ElementSize, count: u32) -> any::ListBuilder<'b, T> { + self.try_init(size, count).expect("too many elements for list") + } + + #[inline] + pub fn try_init( + self, + size: ElementSize, + count: u32, + ) -> Result, TooManyElementsError> { + if count > size.max_elements().get() { + return Err(TooManyElementsError(())) + } + let count = ElementCount::new(count).unwrap(); + + Ok(AnyList::from(self.init_case().into_raw_build_ptr().init_list(size, count))) + } } impl FieldType for Capability { @@ -1982,24 +2513,106 @@ impl FieldType for Capability { impl<'b, 'p, T, C> PtrFieldReader<'b, 'p, T, Capability> where - T: Table, - C: ty::Capability, + T: CapTable, + C: ty::Capability, { - // TODO accessors + #[inline] + pub fn try_get_option(&self) -> Result> { + match self.raw_ptr().try_to_capability() { + Ok(Some(c)) => Ok(Some(C::from_client(c))), + Ok(None) => Ok(None), + Err(err) => Err(err), + } + } +} + +impl<'b, 'p, T, C> PtrFieldReader<'b, 'p, T, Capability> +where + T: CapTable + BreakableCapSystem, + C: ty::Capability, +{ + #[inline] + pub fn get(&self) -> C { + let cap = match self.raw_ptr().try_to_capability() { + Ok(Some(c)) => c, + Ok(None) => T::null(), + Err(err) => T::broken(&err), + }; + C::from_client(cap) + } + + #[inline] + pub fn get_option(&self) -> Option { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result { + let cap = self.raw_ptr().try_to_capability()?.unwrap_or_else(T::null); + Ok(C::from_client(cap)) + } } impl<'b, 'p, T, C> PtrFieldBuilder<'b, 'p, T, Capability> where - T: Table, - C: ty::Capability, + T: CapTable, + C: ty::Capability, { - // TODO accessors + #[inline] + pub fn set(&mut self, client: C) { + self.raw_build_ptr().set_cap(client.into_inner()) + } } -impl<'b, 'p, T: Table, C: ty::Capability> PtrVariantReader<'b, 'p, T, Capability> { - // TODO accessors +impl<'b, 'p, T, C> PtrVariantReader<'b, 'p, T, Capability> +where + T: CapTable, + C: ty::Capability, +{ + #[inline] + pub fn try_get_option(&self) -> Result> { + match self.raw_ptr_or_null().try_to_capability() { + Ok(Some(c)) => Ok(Some(C::from_client(c))), + Ok(None) => Ok(None), + Err(err) => Err(err), + } + } } -impl<'b, 'p, T: Table, C: ty::Capability> PtrVariantBuilder<'b, 'p, T, Capability> { - // TODO accessors +impl<'b, 'p, T, C> PtrVariantReader<'b, 'p, T, Capability> +where + T: CapTable + BreakableCapSystem, + C: ty::Capability, +{ + #[inline] + pub fn get(&self) -> C { + let cap = match self.raw_ptr_or_null().try_to_capability() { + Ok(Some(c)) => c, + Ok(None) => T::null(), + Err(err) => T::broken(&err), + }; + C::from_client(cap) + } + + #[inline] + pub fn get_option(&self) -> Option { + self.try_get_option().ok().flatten() + } + + #[inline] + pub fn try_get(&self) -> Result { + let cap = self.raw_ptr_or_null().try_to_capability()?.unwrap_or_else(T::null); + Ok(C::from_client(cap)) + } +} + +impl<'b, 'p, T, C> PtrVariantBuilder<'b, 'p, T, Capability> +where + T: CapTable, + C: ty::Capability, +{ + #[inline] + pub fn set(&mut self, client: C) { + self.set_case_and_build_ptr().set_cap(client.into_inner()) + } }