1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! # django-query
//!
//! This crate is a toolkit for assembling a mock instance of a
//! Django-style API, although some of the parts may be useful beyond
//! just mocking. The main tools provided are:
//!
//! - The [Filterable] trait, and its derive macro, which allow you to
//!   use attribute markup to automatically parse Django-style filter
//!   URLs into filter objects.
//!
//! - The [Sortable] trait, and its derive macro, which allow you to
//!   use attribute markup to automatically parse Django-style
//!   ordering URLs into sorter objects.
//!
//! - The [IntoRow] trait, and its derive macro, which allow you to
//!   create Django-style JSON responses for your type; note that this
//!   isn't just standard serialization because complex objects are
//!   replaced by one of their fields, which functions as a foreign
//!   key.
//!
//! - The [mock::Endpoint] type, which implements [wiremock::Respond],
//!   and can provide a mock endpoint for a collection of objects
//!   whose type implements the preceding three traits.
//!
//! Example:
//! ```rust
//! use django_query::{IntoRow, Filterable, Sortable, mock::Endpoint};
//! use std::sync::Arc;
//! use wiremock::{Mock, MockServer, matchers, http::Url};
//!
//! #[derive(IntoRow, Filterable, Sortable)]
//! struct Foo {
//!     #[django(sort, op(in, icontains, iexact))]
//!     name: String,
//!     #[django(sort, op(lt, gt))]
//!     value: i32
//! }
//!
//! #[derive(IntoRow, Filterable, Sortable)]
//! struct Bar {
//!     #[django(op(icontains, startswith))]
//!     names: Vec<String>,
//!     #[django(traverse, foreign_key="name")]
//!     foo: Arc<Foo>
//! }
//!
//! tokio_test::block_on( async {
//!    let server = MockServer::start().await;
//!    let bars = Arc::new(vec![
//!        Bar {
//!          names: vec! [
//!            "apple".to_string(),
//!            "banana".to_string()
//!          ],
//!          foo: Arc::new(Foo {
//!            name: "foo1".to_string(),
//!            value: 5,
//!          }),
//!        },
//!        Bar {
//!          names: vec! [
//!            "carrot".to_string(),
//!          ],
//!          foo: Arc::new(Foo {
//!            name: "foo2".to_string(),
//!            value: 4,
//!          }),
//!       },
//!    ]);
//!
//!    Mock::given(matchers::method("GET"))
//!         .respond_with(Endpoint::new(bars, Some(&server.uri())))
//!         .mount(&server)
//!         .await;
//!
//!    // Let's build up a Django-style query URL to test; first the
//!    // mock server's base URI
//!    let mut url = Url::parse(&server.uri())
//!        .expect("failed to parse MockServer URL");
//!    // Now request all only Bars that have a name in names that contains
//!    // "PL" (case insensitive)
//!    url.query_pairs_mut().append_pair("names__icontains", "PL");
//!    // Request only Bars whose foo has a value greater than 4
//!    url.query_pairs_mut().append_pair("foo__value__gt", "4");
//!    let body: serde_json::Value = reqwest::get(url)
//!        .await
//!        .expect("error getting response")
//!        .json()
//!        .await
//!        .expect("error parsing response");
//!
//!    assert_eq!(body, serde_json::json!{
//!      {
//!        "count": 1,
//!        "next": null,
//!        "prev": null,
//!        "results": [
//!          { "names": ["apple", "banana"], "foo": "foo1" },
//!        ]
//!      }
//!    });
//! });
//! ```
pub mod filtering;
pub mod mock;
pub mod operators;
pub mod ordering;
pub mod row;

pub use crate::filtering::{Filterable, OperatorSet};
pub use crate::operators::Scalar;
pub use crate::ordering::{OrderingSet, Sortable};
pub use crate::row::{IntoCellValue, IntoRow, StringCellValue};
pub use django_query_derive::{Filterable, IntoRow, Sortable};