diff --git a/serde_json/rust/json.rs b/serde_json/rust/json.rs index 1008995..ed9f8d2 100644 --- a/serde_json/rust/json.rs +++ b/serde_json/rust/json.rs @@ -422,6 +422,274 @@ impl SerdeJson { pub fn is_json_equal(&self, other: &Self) -> bool { self.value == other.value } + + /// Appends an item to the JSON array. + pub fn append(&mut self, item: SerdeJson) -> Status { + if let serde_json::Value::Array(arr) = &mut self.value { + arr.push(item.value); + ok() + } else { + internal_error("JSON value is not an array") + } + } + + /// Removes an item from the JSON array at `index`. + pub fn remove(&mut self, index: usize) -> Status { + if let serde_json::Value::Array(arr) = &mut self.value { + if index < arr.len() { + arr.remove(index); + ok() + } else { + invalid_argument_error(format!( + "Index {} out of bounds for array of length {}", + index, + arr.len() + )) + } + } else { + internal_error("JSON value is not an array") + } + } + + /// Returns the size of array, object, or string. + pub fn size(&self) -> Result { + match &self.value { + serde_json::Value::Array(arr) => Ok(arr.len() as i64), + serde_json::Value::Object(obj) => Ok(obj.len() as i64), + serde_json::Value::String(s) => Ok(s.len() as i64), + _ => Err("Size is only supported for arrays, objects, and strings".into()), + } + } + + /// Returns a borrowed reference view of this SerdeJson. + pub fn as_ref<'a>(&'a self) -> SerdeJsonRef<'a> { + SerdeJsonRef { node: &self.value } + } +} + +/// Borrowed zero-copy view of a JSON node. +#[derive(Copy, Clone)] +pub struct SerdeJsonRef<'a> { + pub(crate) node: &'a serde_json::Value, +} + +impl<'a> std::fmt::Debug for SerdeJsonRef<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + write!(f, "SerdeJsonRef") + } +} + +impl<'a> SerdeJsonRef<'a> { + pub fn from_serde_json(json: &'a SerdeJson) -> Self { + Self { node: &json.value } + } + + pub fn to_owned(&self) -> SerdeJson { + SerdeJson { value: self.node.clone() } + } + + pub fn get_field(&self, raw_field_name: &[u8]) -> Result, RawString> { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match self.node.get(field_name) { + Some(value) => Ok(Self { node: value }), + None => Err(format!("Field '{}' not found in JSON object", field_name).into()), + } + } + + pub fn get_field_string(&self, raw_field_name: &[u8]) -> Result { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match self.node[field_name].as_str() { + Some(s) => Ok(s.into()), + None => Err(format!("Field '{}' is not string", field_name).into()), + } + } + + pub fn get_field_bool(&self, raw_field_name: &[u8]) -> Result { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match self.node[field_name].as_bool() { + Some(b) => Ok(b), + None => Err(format!("Field '{}' is not boolean", field_name).into()), + } + } + + pub fn get_field_int(&self, raw_field_name: &[u8]) -> Result { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match self.node[field_name].as_i64() { + Some(i) => Ok(i), + None => Err(format!("Field '{}' is not integer", field_name).into()), + } + } + + pub fn get_field_double(&self, raw_field_name: &[u8]) -> Result { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match self.node[field_name].as_f64() { + Some(f) => Ok(f), + None => Err(format!("Field '{}' is not double", field_name).into()), + } + } + + pub fn get_field_object(&self, raw_field_name: &[u8]) -> Result, RawString> { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(err.to_string().into()), + }; + match &self.node[field_name] { + o @ serde_json::Value::Object(_) => Ok(Self { node: o }), + _ => Err(format!("Field '{}' is not object", field_name).into()), + } + } + + pub fn get_field_array_element( + &self, + raw_field_name: &[u8], + index: usize, + ) -> Result, GetArrayElementError> { + let field_name = match std::str::from_utf8(raw_field_name) { + Ok(field_name) => field_name, + Err(err) => return Err(GetArrayElementError::new_failed_precondition(err.to_string())), + }; + match self.node[field_name].as_array() { + Some(a) => match a.get(index) { + Some(v) => Ok(Self { node: v }), + None => Err(GetArrayElementError::new_out_of_bounds(format!( + "Index {} out of bounds for array field '{}' of length {}", + index, + field_name, + a.len() + ))), + }, + None => Err(GetArrayElementError::new_failed_precondition(format!( + "Field '{}' is not array", + field_name + ))), + } + } + + pub fn get_bool(&self) -> Result { + match self.node.as_bool() { + Some(b) => Ok(b), + None => Err("This object is not boolean".into()), + } + } + + pub fn get_int(&self) -> Result { + match self.node.as_i64() { + Some(i) => Ok(i), + None => Err("This object is not integer".into()), + } + } + + pub fn get_double(&self) -> Result { + match self.node.as_f64() { + Some(f) => Ok(f), + None => Err("This object is not double".into()), + } + } + + pub fn get_string(&self) -> Result { + match self.node.as_str() { + Some(s) => Ok(s.into()), + None => Err("This object is not string".into()), + } + } + + pub fn get_array_element( + &self, + index: usize, + ) -> Result, GetArrayElementError> { + match self.node.as_array() { + Some(a) => match a.get(index) { + Some(v) => Ok(Self { node: v }), + None => Err(GetArrayElementError::new_out_of_bounds(format!( + "Index {} out of bounds for array of length {}", + index, + a.len() + ))), + }, + None => Err(GetArrayElementError::new_failed_precondition("This object is not array")), + } + } + + pub fn is_null(&self) -> bool { + self.node.is_null() + } + + pub fn is_boolean(&self) -> bool { + self.node.is_boolean() + } + + pub fn is_number(&self) -> bool { + self.node.is_number() + } + + pub fn is_i64(&self) -> bool { + self.node.is_i64() + } + + pub fn is_f64(&self) -> bool { + self.node.is_f64() + } + + pub fn is_string(&self) -> bool { + self.node.is_string() + } + + pub fn is_array(&self) -> bool { + self.node.is_array() + } + + pub fn is_object(&self) -> bool { + self.node.is_object() + } + + pub fn to_string(&self, sort_keys: bool) -> RawString { + if sort_keys { + let mut value = self.node.clone(); + value.sort_all_objects(); + return value.to_string().into(); + } + self.node.to_string().into() + } + + pub fn get_keys(&self) -> Result { + let object = match self.node.as_object() { + Some(o) => o, + None => return Err("This isn't a object".into()), + }; + + Ok(object.keys().map(|k| k.as_str().into()).collect::>().into()) + } + + pub fn has_field(&self, raw_field_name: &[u8]) -> Result { + match std::str::from_utf8(raw_field_name) { + Ok(field_name) => Ok(self.node.get(field_name).is_some()), + Err(err) => Err(err.to_string().into()), + } + } + + pub fn size(&self) -> Result { + match self.node { + serde_json::Value::Array(arr) => Ok(arr.len() as i64), + serde_json::Value::Object(obj) => Ok(obj.len() as i64), + serde_json::Value::String(s) => Ok(s.len() as i64), + _ => Err("Size is only supported for arrays, objects, and strings".into()), + } + } } make_vec_type!(RawString, VecRawString); diff --git a/serde_json/serde_json_bridge.cc b/serde_json/serde_json_bridge.cc index 2e8b74d..104accb 100644 --- a/serde_json/serde_json_bridge.cc +++ b/serde_json/serde_json_bridge.cc @@ -489,4 +489,254 @@ bool SerdeJson::operator!=(const SerdeJson& other) const { return !json_obj_.is_json_equal(other.json_obj_); } +SerdeJsonRef SerdeJson::AsRef() const { + return SerdeJsonRef(json_obj_.as_ref()); +} + +absl::StatusOr SerdeJson::Size() const { return AsRef().Size(); } + +absl::Status SerdeJson::Append(const SerdeJson& value) { + return ToStatus(json_obj_.append(value.json_obj_)); +} + +absl::Status SerdeJson::Append(SerdeJson&& value) { + return ToStatus(json_obj_.append(std::move(value.json_obj_))); +} + +absl::Status SerdeJson::Remove(size_t index) { + return ToStatus(json_obj_.remove(index)); +} + +// Implementation of SerdeJsonRef +SerdeJsonRef::SerdeJsonRef(rust::json::SerdeJsonRef rs_ref) + : rs_ref_(rs_ref) {} + +SerdeJson SerdeJsonRef::ToOwned() const { + return SerdeJson(rs_ref_.to_owned()); +} + +absl::StatusOr SerdeJsonRef::GetInt() const { + rs_std::Result + rs_result = rs_ref_.get_int(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr SerdeJsonRef::GetBool() const { + rs_std::Result rs_result = + rs_ref_.get_bool(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr SerdeJsonRef::GetString() const { + rs_std::Result + rs_result = rs_ref_.get_string(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return FromRustRawString(std::move(rs_result).value()); +} + +absl::StatusOr SerdeJsonRef::GetDouble() const { + rs_std::Result + rs_result = rs_ref_.get_double(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr> SerdeJsonRef::GetArray() const { + return absl::UnimplementedError("GetArray on SerdeJsonRef"); +} + +absl::StatusOr SerdeJsonRef::GetArrayElement(size_t index) const { + rs_std::Result + rs_result = rs_ref_.get_array_element(index); + if (!rs_result.has_value()) { + rust::json::GetArrayElementError err = + std::move(rs_result).err(); + std::string err_msg = FromRustRawString(err.msg); + if (err.is_out_of_bounds) { + return absl::OutOfRangeError(std::move(err_msg)); + } + return absl::FailedPreconditionError(std::move(err_msg)); + } + return SerdeJsonRef(std::move(rs_result).value()); +} + +absl::StatusOr SerdeJsonRef::GetField( + absl::string_view key) const { + rs_std::Result + rs_result = rs_ref_.get_field(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return SerdeJsonRef(std::move(rs_result).value()); +} + +absl::StatusOr SerdeJsonRef::GetFieldString( + absl::string_view key) const { + rs_std::Result + rs_result = rs_ref_.get_field_string(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return FromRustRawString(std::move(rs_result).value()); +} + +absl::StatusOr SerdeJsonRef::GetFieldBool(absl::string_view key) const { + rs_std::Result rs_result = + rs_ref_.get_field_bool(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr SerdeJsonRef::GetFieldInt(absl::string_view key) const { + rs_std::Result + rs_result = rs_ref_.get_field_int(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr SerdeJsonRef::GetFieldDouble( + absl::string_view key) const { + rs_std::Result + rs_result = rs_ref_.get_field_double(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr SerdeJsonRef::GetFieldObject( + absl::string_view key) const { + rs_std::Result + rs_result = rs_ref_.get_field_object(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return SerdeJsonRef(std::move(rs_result).value()); +} + +absl::StatusOr> SerdeJsonRef::GetFieldArray( + absl::string_view key) const { + return absl::UnimplementedError("GetFieldArray on SerdeJsonRef"); +} + +absl::StatusOr SerdeJsonRef::GetFieldArrayElement( + absl::string_view key, size_t index) const { + rs_std::Result + rs_result = rs_ref_.get_field_array_element( + absl::Span( + reinterpret_cast(key.data()), key.size()), + index); + if (!rs_result.has_value()) { + rust::json::GetArrayElementError err = + std::move(rs_result).err(); + std::string err_msg = FromRustRawString(err.msg); + if (err.is_out_of_bounds) { + return absl::OutOfRangeError(std::move(err_msg)); + } + return absl::FailedPreconditionError(std::move(err_msg)); + } + return SerdeJsonRef(std::move(rs_result).value()); +} + +bool SerdeJsonRef::IsNull() const { return rs_ref_.is_null(); } +bool SerdeJsonRef::IsEmpty() const { + absl::StatusOr sz = Size(); + return sz.ok() && *sz == 0; +} +bool SerdeJsonRef::IsObject() const { return rs_ref_.is_object(); } +bool SerdeJsonRef::IsArray() const { return rs_ref_.is_array(); } +bool SerdeJsonRef::IsString() const { return rs_ref_.is_string(); } +bool SerdeJsonRef::IsNumber() const { return rs_ref_.is_number(); } +bool SerdeJsonRef::IsDouble() const { return rs_ref_.is_f64(); } +bool SerdeJsonRef::IsBool() const { return rs_ref_.is_boolean(); } +bool SerdeJsonRef::IsInt() const { return rs_ref_.is_i64(); } + +absl::StatusOr SerdeJsonRef::HasField(absl::string_view key) const { + rs_std::Result rs_result = + rs_ref_.has_field(absl::Span( + reinterpret_cast(key.data()), key.size())); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return std::move(rs_result).value(); +} + +absl::StatusOr> SerdeJsonRef::GetKeys() const { + rs_std::Result + rs_result = rs_ref_.get_keys(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + rust::json::VecRawString vec = std::move(rs_result).value(); + std::vector keys; + keys.reserve(vec.len()); + for (size_t i = 0; i < vec.len(); ++i) { + keys.push_back(FromRustRawString(vec.as_ptr()[i])); + } + return keys; +} + +absl::StatusOr SerdeJsonRef::Size() const { + rs_std::Result + rs_result = rs_ref_.size(); + if (!rs_result.has_value()) { + return absl::FailedPreconditionError( + FromRustRawString(std::move(rs_result).err())); + } + return static_cast(std::move(rs_result).value()); +} + +std::string SerdeJsonRef::ToString(bool sort_keys) const { + rust::raw_string::RawString raw_str = + rs_ref_.to_string(sort_keys); + return FromRustRawString(raw_str); +} + +absl::StatusOr<::google::protobuf::Struct> SerdeJsonRef::ToProtoStruct() const { + return ToOwned().ToProtoStruct(); +} + +absl::StatusOr<::google::protobuf::Value> SerdeJsonRef::ToProtoValue() const { + return ToOwned().ToProtoValue(); +} + } // namespace security::json::serde_json_bridge diff --git a/serde_json/serde_json_bridge.h b/serde_json/serde_json_bridge.h index 6c06f76..d639f35 100644 --- a/serde_json/serde_json_bridge.h +++ b/serde_json/serde_json_bridge.h @@ -14,6 +14,70 @@ namespace security::json::serde_json_bridge { +class SerdeJson; + +// Borrowed zero-copy read-only view of a SerdeJson node. +class SerdeJsonRef final { + public: + SerdeJsonRef() = default; + + // Converts this borrowed view into an owned SerdeJson by deep cloning. + SerdeJson ToOwned() const; + + // Returns the value of the current json node. + absl::StatusOr GetInt() const; + absl::StatusOr GetBool() const; + absl::StatusOr GetString() const; + absl::StatusOr GetDouble() const; + absl::StatusOr> GetArray() const; + absl::StatusOr GetArrayElement(size_t index) const; + + // Returns a node of the corresponding `key` field of this json object. + absl::StatusOr GetField(absl::string_view key) const; + + // Returns the value of the corresponding field of this json object. + absl::StatusOr GetFieldString(absl::string_view key) const; + absl::StatusOr GetFieldBool(absl::string_view key) const; + absl::StatusOr GetFieldInt(absl::string_view key) const; + absl::StatusOr GetFieldDouble(absl::string_view key) const; + absl::StatusOr GetFieldObject(absl::string_view key) const; + absl::StatusOr> GetFieldArray( + absl::string_view key) const; + absl::StatusOr GetFieldArrayElement(absl::string_view key, + size_t index) const; + + // Methods for checking the type of this json node. + bool IsNull() const; + bool IsEmpty() const; + bool IsObject() const; + bool IsArray() const; + bool IsString() const; + bool IsNumber() const; + bool IsDouble() const; + bool IsBool() const; + bool IsInt() const; + + // If the current node is an object, returns whether the field exists. + absl::StatusOr HasField(absl::string_view key) const; + + // Returns the keys of a json object. + absl::StatusOr> GetKeys() const; + + // Size of array, object, or string. + absl::StatusOr Size() const; + + // Convert this object to string. + std::string ToString(bool sort_keys = true) const; + absl::StatusOr<::google::protobuf::Struct> ToProtoStruct() const; + absl::StatusOr<::google::protobuf::Value> ToProtoValue() const; + + private: + friend class SerdeJson; + explicit SerdeJsonRef(rust::json::SerdeJsonRef rs_ref); + + rust::json::SerdeJsonRef rs_ref_; +}; + class SerdeJson final { public: // Compare two SerdeJson objects. @@ -33,6 +97,9 @@ class SerdeJson final { static absl::StatusOr CreateNull(); static absl::StatusOr CreateString(absl::string_view value); + // Returns a borrowed zero-copy view of this SerdeJson. + SerdeJsonRef AsRef() const; + // Returns the value of the current json node. absl::StatusOr GetInt() const; absl::StatusOr GetBool() const; @@ -74,6 +141,9 @@ class SerdeJson final { // is not an object. absl::StatusOr> GetKeys() const; + // Size of array, object, or string. + absl::StatusOr Size() const; + // Convert this object to string. std::string ToString(bool sort_keys = true) const; absl::StatusOr<::google::protobuf::Struct> ToProtoStruct() const; @@ -91,7 +161,13 @@ class SerdeJson final { absl::Status AddFieldArray(absl::string_view key, std::vector value); + // Array mutation methods (in-place / move semantics). + absl::Status Append(const SerdeJson& value); + absl::Status Append(SerdeJson&& value); + absl::Status Remove(size_t index); + private: + friend class SerdeJsonRef; explicit SerdeJson(rust::json::SerdeJson); static std::vector ConvertVecSerdeJsonToVector(