Skip to content

Commit

Permalink
Generics and queries
Browse files Browse the repository at this point in the history
  • Loading branch information
tinrab committed Dec 20, 2023
1 parent 2c8120b commit 17579ab
Show file tree
Hide file tree
Showing 23 changed files with 1,371 additions and 199 deletions.
12 changes: 6 additions & 6 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni"
version = "0.1.34"
version = "0.1.39"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utility Library for Rust"
repository = "https://github.com/tinrab/bomboni"
Expand Down Expand Up @@ -38,9 +38,9 @@ tokio = ["bomboni_common/tokio"]
tonic = ["bomboni_proto/tonic", "bomboni_request/tonic"]

[dependencies]
bomboni_common = { path = "bomboni_common", version = "0.1.34" }
bomboni_common = { path = "bomboni_common", version = "0.1.39" }

bomboni_prost = { path = "bomboni_prost", version = "0.1.34", optional = true }
bomboni_proto = { path = "bomboni_proto", version = "0.1.34", optional = true }
bomboni_request = { path = "bomboni_request", version = "0.1.34", optional = true }
bomboni_template = { path = "bomboni_template", version = "0.1.34", optional = true }
bomboni_prost = { path = "bomboni_prost", version = "0.1.39", optional = true }
bomboni_proto = { path = "bomboni_proto", version = "0.1.39", optional = true }
bomboni_request = { path = "bomboni_request", version = "0.1.39", optional = true }
bomboni_template = { path = "bomboni_template", version = "0.1.39", optional = true }
2 changes: 1 addition & 1 deletion bomboni_common/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_common"
version = "0.1.34"
version = "0.1.39"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Common things for Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
2 changes: 1 addition & 1 deletion bomboni_prost/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_prost"
version = "0.1.34"
version = "0.1.39"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with prost. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down
4 changes: 2 additions & 2 deletions bomboni_proto/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_proto"
version = "0.1.34"
version = "0.1.39"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with Protobuf/gRPC. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand Down Expand Up @@ -36,5 +36,5 @@ serde_json = { version = "1.0.108", optional = true }
serde_json = "1.0.108"

[build-dependencies]
bomboni_prost = { path = "../bomboni_prost", version = "0.1.34" }
bomboni_prost = { path = "../bomboni_prost", version = "0.1.39" }
prost-build = "0.12.3"
12 changes: 8 additions & 4 deletions bomboni_request/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bomboni_request"
version = "0.1.34"
version = "0.1.39"
authors = ["Tin Rabzelj <tin@flinect.com>"]
description = "Utilities for working with API requests. Part of Bomboni library."
repository = "https://github.com/tinrab/bomboni"
Expand All @@ -19,8 +19,8 @@ testing = []
tonic = ["bomboni_proto/tonic", "dep:tonic"]

[dependencies]
bomboni_common = { path = "../bomboni_common", version = "0.1.34" }
bomboni_proto = { path = "../bomboni_proto", version = "0.1.34" }
bomboni_common = { path = "../bomboni_common", version = "0.1.39" }
bomboni_proto = { path = "../bomboni_proto", version = "0.1.39" }
thiserror = "1.0.50"
itertools = "0.12.0"
time = { version = "0.3.30", features = ["formatting", "parsing"] }
Expand All @@ -35,4 +35,8 @@ rand = "0.8.5"
regex = "1.10.2"

tonic = { version = "0.10.2", optional = true }
bomboni_request_derive = { path = "../bomboni_request_derive", version = "0.1.34", optional = true }
bomboni_request_derive = { path = "../bomboni_request_derive", version = "0.1.39", optional = true }

[dev-dependencies]
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
74 changes: 66 additions & 8 deletions bomboni_request/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ pub struct FieldError {

#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum CommonError {
#[error(transparent)]
Query(#[from] QueryError),
#[error("requested entity was not found")]
ResourceNotFound,
#[error("unauthorized")]
Expand Down Expand Up @@ -193,7 +191,21 @@ impl RequestError {
pub fn wrap_request(self, name: &str) -> Self {
match self {
Self::Field(error) => Self::bad_request(name, [(error.field, error.error)]),
err => err,
Self::Domain(error) => {
if let Some(error) = error.as_any().downcast_ref::<QueryError>() {
#[allow(trivial_casts)]
Self::bad_request(
name,
[(
error.get_violating_field_name(),
Box::new(error.clone()) as DomainErrorBox,
)],
)
} else {
RequestError::Domain(error)
}
}
error => error,
}
}

Expand All @@ -215,21 +227,39 @@ impl RequestError {
error.error,
)],
),
err => err,
Self::Domain(error) => {
if let Some(error) = error.as_any().downcast_ref::<QueryError>() {
#[allow(trivial_casts)]
Self::bad_request(
name,
[(
format!(
"{}.{}",
root_path.into_iter().map(|step| step.to_string()).join("."),
error.get_violating_field_name()
),
Box::new(error.clone()) as DomainErrorBox,
)],
)
} else {
RequestError::Domain(error)
}
}
error => error,
}
}

pub fn downcast_domain_ref<T: std::any::Any>(&self) -> Option<&T> {
if let Self::Domain(err) = self {
err.as_any().downcast_ref::<T>()
if let Self::Domain(error) = self {
error.as_any().downcast_ref::<T>()
} else {
None
}
}

pub fn downcast_domain<T: 'static + Clone>(&self) -> Option<T> {
if let Self::Domain(err) = self {
err.as_any().downcast_ref::<T>().cloned()
if let Self::Domain(error) = self {
error.as_any().downcast_ref::<T>().cloned()
} else {
None
}
Expand Down Expand Up @@ -369,4 +399,32 @@ mod tests {
}
);
}

#[test]
fn query_error_metadata() {
assert_eq!(
serde_json::to_value(Status::from(
RequestError::from(QueryError::InvalidPageSize).wrap_request("List"),
))
.unwrap(),
serde_json::from_str::<serde_json::Value>(
r#"{
"code": "INVALID_ARGUMENT",
"message": "invalid `List` request",
"details": [
{
"@type": "type.googleapis.com/google.rpc.BadRequest",
"fieldViolations": [
{
"field": "page_size",
"description": "page size specified is invalid"
}
]
}
]
}"#
)
.unwrap()
);
}
}
51 changes: 31 additions & 20 deletions bomboni_request/src/ordering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::{
cmp,
collections::BTreeSet,
fmt::{self, Display, Formatter},
ops::{Deref, DerefMut},
};

use itertools::Itertools;
Expand All @@ -15,9 +16,7 @@ use super::schema::SchemaMapped;
pub mod error;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct Ordering {
pub terms: Vec<OrderingTerm>,
}
pub struct Ordering(Vec<OrderingTerm>);

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OrderingTerm {
Expand All @@ -33,7 +32,7 @@ pub enum OrderingDirection {

impl Ordering {
pub fn new(terms: Vec<OrderingTerm>) -> Self {
Self { terms }
Self(terms)
}

pub fn parse(source: &str) -> OrderingResult<Self> {
Expand Down Expand Up @@ -65,14 +64,14 @@ impl Ordering {

terms.push(OrderingTerm { name, direction });
}
Ok(Self { terms })
Ok(Self(terms))
}

pub fn evaluate<T>(&self, lhs: &T, rhs: &T) -> Option<cmp::Ordering>
where
T: SchemaMapped,
{
for term in &self.terms {
for term in self.iter() {
let a = lhs.get_field(&term.name);
let b = rhs.get_field(&term.name);
match a.partial_cmp(&b)? {
Expand All @@ -95,7 +94,7 @@ impl Ordering {
}

pub fn is_valid(&self, schema: &Schema) -> bool {
for term in &self.terms {
for term in self.iter() {
if let Some(field) = schema.get_field(&term.name) {
if !field.ordered {
return false;
Expand All @@ -108,9 +107,23 @@ impl Ordering {
}
}

impl Deref for Ordering {
type Target = Vec<OrderingTerm>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl DerefMut for Ordering {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}

impl Display for Ordering {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(&self.terms.iter().map(ToString::to_string).join(", "))
f.write_str(&self.iter().map(ToString::to_string).join(", "))
}
}

Expand Down Expand Up @@ -141,18 +154,16 @@ mod tests {
let ordering = Ordering::parse(" , user.displayName, task .userId desc").unwrap();
assert_eq!(
ordering,
Ordering {
terms: vec![
OrderingTerm {
name: "user.displayName".into(),
direction: Ascending,
},
OrderingTerm {
name: "task.userId".into(),
direction: Descending,
},
]
}
Ordering(vec![
OrderingTerm {
name: "user.displayName".into(),
direction: Ascending,
},
OrderingTerm {
name: "task.userId".into(),
direction: Descending,
},
])
);
}

Expand Down
5 changes: 4 additions & 1 deletion bomboni_request/src/parse/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ pub mod parse_id {

#[cfg(test)]
mod tests {
use crate::{error::RequestError, parse::RequestParse};
use crate::{
error::RequestError,
parse::{RequestParse, RequestResult},
};
use bomboni_common::id::Id;
use bomboni_request_derive::Parse;

Expand Down
Loading

0 comments on commit 17579ab

Please sign in to comment.