diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..c041aea --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: Fmt, Clippy, Test + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + name: test + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node_index: [clippy, fmt, test] + + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.78.0 + override: true + components: rustfmt, clippy + + - uses: actions-rs/install@v0.1 + with: + crate: cargo-audit + version: latest + + - name: Run cargo fmt + if: ${{ matrix.node_index == 'fmt' }} + run: cargo fmt --all -- --check + + - name: Run cargo clippy + if: ${{ matrix.node_index == 'clippy' }} + run: cargo clippy -- -D warnings + + - name: Run cargo test + if: ${{ matrix.node_index == 'test' }} + run: cargo test \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 8f6c370..677bc17 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,15 @@ name = "pg_filters" version = "0.1.0" edition = "2021" +authors = ["Kingsley Hendrickse "] +description = "A simple rust helper to generate postgres sql for pagination, sorting and filtering" +license = "Apache-2.0 OR MIT" +documentation = "https://docs.rs/ag-grid-rs/latest/ag_grid_rs/" +readme = "../README.md" +homepage = "https://github.com/kingsleyh/pg_filters" +repository = "https://github.com/kingsleyh/pg_filters" +keywords = ["postgres", "pagination", "filtering", "sorting", "sql", "database"] +categories = ["data-structures", "web-programming"] [lib] name = "pg_filters" diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..e06f487 --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2022 Michael Freeborn + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..e5d2e1c --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Michael Freeborn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index 5538328..62af887 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,15 @@ # PG Filters +[![License](https://img.shields.io/badge/license-MIT%2FApache-blue.svg)](https://github.com/kingsleyh/pg_filters#license) +[![Docs](https://docs.rs/pg_filters/badge.svg)](https://docs.rs/pg_filters/latest/pg_filters/) +[![Test](https://github.com/kingsleyh/pg_filters/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/kingsleyh/pg_filters/actions/workflows/ci.yml) + A simple rust helper to generate postgres sql for pagination, sorting and filtering ## Usage ```rust - let filters = PgFilters::new( + let filters = PgFilters::new( Some(PaginationOptions { current_page: 1, per_page: 10, @@ -14,23 +18,15 @@ A simple rust helper to generate postgres sql for pagination, sorting and filter }), vec![ SortedColumn::new("age".into(), "desc".into()), - SortedColumn { - column: "name".into(), - order: SortOrder::Asc, - }, + SortedColumn::new("name".into(), "asc".into()), ], vec![ FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()), - FilteringRule { - column: "age".into(), - filter_operator: FilterOperator::GreaterThan, - conditional_operator: ConditionalOperator::Or, - value: FilterValue::Int(18), - }, + FilteringRule::new("age".into(), ">".into(), "or".into(), "18".into()), ], ); - let sql = filters.sql(); + let sql = filters.sql(); assert_eq!(sql, " WHERE name = 'John' OR age > 18 ORDER BY age DESC, name ASC LIMIT 10 OFFSET 0"); ``` @@ -60,4 +56,10 @@ Along with the sql it also returns objects containing the pagination, sorting an } ``` -see the tests for more examples \ No newline at end of file +see the tests for more examples + +# License + +Licensed under either of these: +- MIT([https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)) +- Apache-2.0([https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)) \ No newline at end of file diff --git a/src/lib/filtering.rs b/src/lib/filtering.rs index 87d43b7..b1cb753 100644 --- a/src/lib/filtering.rs +++ b/src/lib/filtering.rs @@ -1,3 +1,41 @@ +//! This module contains the structures and functions to handle filtering rules +//! +//! The FilteringRule structure is used to define a rule for filtering data +//! The Filtering structure is used to define a set of filtering rules +//! +//! # Examples +//! +//! ```rust +//! use pg_filters::filtering::{FilteringRule, FilterOperator, FilterValue, Filtering, ConditionalOperator}; +//! +//! let filtering_rule = FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()); +//! +//! assert_eq!(filtering_rule.column, "name"); +//! assert_eq!(filtering_rule.filter_operator, FilterOperator::Equal); +//! assert_eq!(filtering_rule.conditional_operator, ConditionalOperator::And); +//! assert_eq!(filtering_rule.value, FilterValue::String("John".into())); +//! ``` +//! + +/// The FilterValue enum is used to define the value of a filtering rule +/// +/// The FilterValue enum can be one of the following: +/// +/// * String +/// * Int +/// * Float +/// * Bool +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::FilterValue; +/// +/// let filter_value = FilterValue::String("John".into()); +/// +/// assert_eq!(filter_value, FilterValue::String("John".into())); +/// ``` +/// #[derive(Debug, Clone, PartialEq)] pub enum FilterValue { String(String), @@ -6,14 +44,62 @@ pub enum FilterValue { Bool(bool), } +/// The FilteringRule structure is used to define a rule for filtering data +/// +/// The FilteringRule structure contains the following fields: +/// +/// * column: The name of the column to filter +/// * filter_operator: The operator to use for filtering +/// * conditional_operator: The operator to use to combine this rule with the next rule +/// * value: The value to use for filtering +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::{FilteringRule, FilterOperator, FilterValue, ConditionalOperator}; +/// +/// let filtering_rule = FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()); +/// +/// assert_eq!(filtering_rule.column, "name"); +/// assert_eq!(filtering_rule.filter_operator, FilterOperator::Equal); +/// assert_eq!(filtering_rule.conditional_operator, ConditionalOperator::And); +/// assert_eq!(filtering_rule.value, FilterValue::String("John".into())); +/// ``` +/// #[derive(Debug, Clone)] pub struct FilteringRule { + /// The name of the column to filter pub column: String, + /// The operator to use for filtering pub filter_operator: FilterOperator, + /// The operator to use to combine this rule with the next rule pub conditional_operator: ConditionalOperator, + /// The value to use for filtering pub value: FilterValue, } +/// The FilteringRule structure is used to define a rule for filtering data +/// +/// The FilteringRule structure contains the following fields: +/// +/// * column: The name of the column to filter +/// * filter_operator: The operator to use for filtering +/// * conditional_operator: The operator to use to combine this rule with the next rule +/// * value: The value to use for filtering +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::{FilteringRule, FilterOperator, FilterValue, ConditionalOperator}; +/// +/// let filtering_rule = FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()); +/// +/// assert_eq!(filtering_rule.column, "name"); +/// assert_eq!(filtering_rule.filter_operator, FilterOperator::Equal); +/// assert_eq!(filtering_rule.conditional_operator, ConditionalOperator::And); +/// assert_eq!(filtering_rule.value, FilterValue::String("John".into())); +/// ``` +/// impl FilteringRule { pub fn new( column: String, @@ -70,12 +156,57 @@ impl FilteringRule { } } + +/// The ConditionalOperator enum is used to define the operator to use to combine filtering rules +/// +/// The ConditionalOperator enum can be one of the following: +/// +/// * And +/// * Or +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::ConditionalOperator; +/// +/// let conditional_operator = ConditionalOperator::And; +/// +/// assert_eq!(conditional_operator, ConditionalOperator::And); +/// ``` +/// #[derive(Debug, Clone, PartialEq)] pub enum ConditionalOperator { And, Or, } +/// The FilterOperator enum is used to define the operator to use for filtering +/// +/// The FilterOperator enum can be one of the following: +/// +/// * Equal +/// * NotEqual +/// * GreaterThan +/// * GreaterThanOrEqual +/// * LessThan +/// * LessThanOrEqual +/// * Like +/// * NotLike +/// * In +/// * NotIn +/// * IsNull +/// * IsNotNull +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::FilterOperator; +/// +/// let filter_operator = FilterOperator::Equal; +/// +/// assert_eq!(filter_operator, FilterOperator::Equal); +/// ``` +/// #[derive(Debug, Clone, PartialEq)] pub enum FilterOperator { Equal, @@ -92,12 +223,54 @@ pub enum FilterOperator { IsNotNull, } +/// The Filtering structure is used to define a set of filtering rules +/// +/// The Filtering structure contains the following fields: +/// +/// * filters: A vector of FilteringRule structures +/// * sql: The SQL string generated from the filtering rules +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::{Filtering, FilteringRule}; +/// +/// let filters = Filtering::new(vec![ +/// FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()), +/// FilteringRule::new("age".into(), ">".into(), "or".into(), "18".into()), +/// ]); +/// +/// assert_eq!(filters.filters.len(), 2); +/// assert_eq!(filters.sql, " WHERE name = 'John' OR age > 18"); +/// ``` +/// #[derive(Debug, Clone)] pub struct Filtering { pub filters: Vec, pub sql: String, } +/// The Filtering structure is used to define a set of filtering rules +/// +/// The Filtering structure contains the following fields: +/// +/// * filters: A vector of FilteringRule structures +/// * sql: The SQL string generated from the filtering rules +/// +/// # Examples +/// +/// ```rust +/// use pg_filters::filtering::{Filtering, FilteringRule}; +/// +/// let filters = Filtering::new(vec![ +/// FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()), +/// FilteringRule::new("age".into(), ">".into(), "or".into(), "18".into()), +/// ]); +/// +/// assert_eq!(filters.filters.len(), 2); +/// assert_eq!(filters.sql, " WHERE name = 'John' OR age > 18"); +/// ``` +/// impl Filtering { pub fn new(rules: Vec) -> Filtering { let mut sql = if rules.len() > 0 { diff --git a/src/lib/mod.rs b/src/lib/mod.rs index 7544910..eebac92 100644 --- a/src/lib/mod.rs +++ b/src/lib/mod.rs @@ -1,3 +1,62 @@ +//! PgFilters - a simple library to handle pagination, sorting and filtering in Postgres +//! +//! # Examples +//! +//! ```rust +//! use pg_filters::{filtering::{FilteringRule}, sorting::{SortedColumn}, PaginationOptions, PgFilters}; +//! +//!let filters = PgFilters::new( +//! Some(PaginationOptions { +//! current_page: 1, +//! per_page: 10, +//! per_page_limit: 10, +//! total_records: 1000, +//! }), +//! vec![ +//! SortedColumn::new("age".into(), "desc".into()), +//! SortedColumn::new("name".into(), "asc".into()), +//! ], +//! vec![ +//! FilteringRule::new("name".into(), "=".into(), "and".into(), "John".into()), +//! FilteringRule::new("age".into(), ">".into(), "or".into(), "18".into()), +//! ], +//!); +//! +//!let sql = filters.sql(); +//!assert_eq!(sql, " WHERE name = 'John' OR age > 18 ORDER BY age DESC, name ASC LIMIT 10 OFFSET 0"); +//! ``` +//! +//! # Notes +//! +//! This library is designed to work with Postgres databases +//! The total_records field in PaginationOptions must be fetched from the database +//! +//! ### Valid Filter Values +//! +//!* can be upper or lower case +//! +//!"=" +//!"!=" +//!">" +//!">=" +//!"<" +//!"<=" +//!"LIKE" +//!"NOT LIKE" +//!"IN" +//!"NOT IN" +//!"IS NULL" +//!"IS NOT NULL" +//! +//! +//!### Valid Conditional Filter Values +//! +//!* can be upper or lower case +//! +//!"AND" +//!"OR" +//! +//! use filtering::{Filtering, FilteringRule}; use pagination::Paginate; use sorting::{SortedColumn, Sorting}; @@ -6,21 +65,33 @@ pub mod filtering; pub mod pagination; pub mod sorting; +/// Struct to hold the pagination options, sorting columns and filtering rules #[derive(Debug, Clone)] pub struct PgFilters { + /// Pagination options pub pagination: Option, + /// Sorting columns pub sorting: Option, + /// Filtering rules pub filters: Option, } +/// Struct to hold the pagination options +/// total_records must be fetched from the database and passed to the constructor #[derive(Debug, Clone)] pub struct PaginationOptions { + /// Current page - the page number to fetch, starts from 1 - usually passed from a query parameter in a web application pub current_page: i64, + /// Number of records per page, usually passed from a query parameter in a web application pub per_page: i64, + /// Maximum number of records per page pub per_page_limit: i64, + /// Total number of records - must be fetched from the database and passed here pub total_records: i64, } +/// New function for PaginationOptions +/// total_records must be fetched from the database and passed to the constructor impl PaginationOptions { pub fn new( current_page: i64, @@ -37,6 +108,12 @@ impl PaginationOptions { } } +/// New function for PgFilters +/// pagination, sorting_columns and filtering_rules are optional +/// pagination is an Option +/// sorting_columns is a Vec +/// filtering_rules is a Vec +/// impl PgFilters { pub fn new( pagination: Option, @@ -62,6 +139,7 @@ impl PgFilters { } } + /// Function to generate the SQL query pub fn sql(&self) -> String { let mut sql = "".to_string(); diff --git a/src/lib/pagination.rs b/src/lib/pagination.rs index 4b1090f..44be0f5 100644 --- a/src/lib/pagination.rs +++ b/src/lib/pagination.rs @@ -1,4 +1,32 @@ -#[derive(Debug, Clone)] +//! Pagination struct and methods +//! +//! Pagination struct holds the current page, previous page, next page, total pages, number of records per page and total records +//! +//! Pagination struct has a new method that takes the current page, number of records per page, total pages and total records and returns a Pagination struct +//! +//! Paginate struct holds the pagination struct and the SQL LIMIT and OFFSET clause + +/// Pagination struct +/// +/// Pagination struct holds the current page, previous page, next page, total pages, number of records per page and total records +/// +/// Pagination struct has a new method that takes the current page, number of records per page, total pages and total records and returns a Pagination struct +/// +/// # Example +/// +/// ```rust +/// use pg_filters::pagination::Pagination; +/// +/// let pagination = Pagination::new(1, 10, 100, 1000); +/// assert_eq!(pagination.current_page, 1); +/// assert_eq!(pagination.previous_page, 1); +/// assert_eq!(pagination.next_page, 2); +/// assert_eq!(pagination.total_pages, 100); +/// assert_eq!(pagination.per_page, 10); +/// assert_eq!(pagination.total_records, 1000); +/// ``` +/// +#[derive(Debug, Clone)] pub struct Pagination { pub current_page: i64, pub previous_page: i64, @@ -8,6 +36,24 @@ pub struct Pagination { pub total_records: i64, } +/// New function for Pagination +/// +/// Pagination struct has a new method that takes the current page, number of records per page, total pages and total records and returns a Pagination struct +/// +/// # Example +/// +/// ```rust +/// use pg_filters::pagination::Pagination; +/// +/// let pagination = Pagination::new(1, 10, 100, 1000); +/// assert_eq!(pagination.current_page, 1); +/// assert_eq!(pagination.previous_page, 1); +/// assert_eq!(pagination.next_page, 2); +/// assert_eq!(pagination.total_pages, 100); +/// assert_eq!(pagination.per_page, 10); +/// assert_eq!(pagination.total_records, 1000); +/// ``` +/// impl Pagination { pub fn new( current_page: i64, @@ -37,12 +83,34 @@ impl Pagination { } } +/// Paginate struct #[derive(Debug, Clone)] pub struct Paginate { + /// Pagination struct pub pagination: Pagination, + /// SQL LIMIT and OFFSET clause pub sql: String, } +/// New function for Paginate +/// +/// Paginate struct has a new method that takes the current page, number of records per page, maximum number of records per page and total records and returns a Paginate struct +/// +/// # Example +/// +/// ```rust +/// use pg_filters::pagination::Paginate; +/// +/// let paginate = Paginate::new(1, 10, 10, 1000); +/// assert_eq!(paginate.pagination.current_page, 1); +/// assert_eq!(paginate.pagination.previous_page, 1); +/// assert_eq!(paginate.pagination.next_page, 2); +/// assert_eq!(paginate.pagination.total_pages, 100); +/// assert_eq!(paginate.pagination.per_page, 10); +/// assert_eq!(paginate.pagination.total_records, 1000); +/// assert_eq!(paginate.sql, "LIMIT 10 OFFSET 0"); +/// ``` +/// impl Paginate { pub fn new( current_page: i64, diff --git a/src/lib/sorting.rs b/src/lib/sorting.rs index dd1ce3f..2d07c4a 100644 --- a/src/lib/sorting.rs +++ b/src/lib/sorting.rs @@ -1,15 +1,71 @@ -#[derive(Debug, Clone)] +//! Sorting module +//! +//! This module contains the Sorting struct and its implementation +//! +//! The Sorting struct is used to generate the ORDER BY clause in SQL queries +//! +//! # Example +//! +//! ```rust +//! use pg_filters::sorting::{SortedColumn, Sorting}; +//! +//! let sorting = Sorting::new(vec![ +//! SortedColumn::new("name", "asc".to_string()), +//! SortedColumn::new("age", "desc".to_string()), +//! ]); +//! +//! assert_eq!(sorting.columns.len(), 2); +//! assert_eq!(sorting.sql, " ORDER BY age DESC, name ASC"); +//! ``` +//! + + +/// SortOrder enum +/// +/// This enum is used to specify the sorting order of a column +/// +/// # Example +/// +/// ```rust +/// use pg_filters::sorting::SortOrder; +/// +/// let order = SortOrder::Asc; +/// +/// assert_eq!(order, SortOrder::Asc); +/// ``` +#[derive(Debug, Clone, PartialEq)] pub enum SortOrder { Asc, Desc, } +/// SortedColumn struct +/// +/// This struct is used to hold the column name and sorting order #[derive(Debug, Clone)] pub struct SortedColumn { + /// Column name pub column: String, + /// Sorting order pub order: SortOrder, } +/// New function for SortedColumn +/// +/// This function takes a column name and sorting order and returns a SortedColumn struct +/// +/// # Example +/// +/// ```rust +/// use pg_filters::sorting::SortedColumn; +/// use pg_filters::sorting::SortOrder; +/// +/// let column = SortedColumn::new("name", "asc".to_string()); +/// +/// assert_eq!(column.column, "name"); +/// assert_eq!(column.order, SortOrder::Asc); +/// ``` +/// impl SortedColumn { pub fn new(column: &str, order: String) -> SortedColumn { let order = match order.to_lowercase().as_str() { @@ -24,12 +80,50 @@ impl SortedColumn { } } +/// Sorting struct +/// +/// This struct is used to generate the ORDER BY clause in SQL queries +/// +/// # Example +/// +/// ```rust +/// use pg_filters::sorting::{SortedColumn, Sorting}; +/// +/// let sorting = Sorting::new(vec![ +/// SortedColumn::new("name", "asc".to_string()), +/// SortedColumn::new("age", "desc".to_string()), +/// ]); +/// +/// assert_eq!(sorting.columns.len(), 2); +/// assert_eq!(sorting.sql, " ORDER BY age DESC, name ASC"); +/// ``` +/// #[derive(Debug, Clone)] pub struct Sorting { + /// Vector of SortedColumn structs pub columns: Vec, + /// SQL ORDER BY clause pub sql: String, } +/// New function for Sorting +/// +/// This function takes a vector of SortedColumn structs and returns a Sorting struct +/// +/// # Example +/// +/// ```rust +/// use pg_filters::sorting::{SortedColumn, Sorting}; +/// +/// let sorting = Sorting::new(vec![ +/// SortedColumn::new("name", "asc".to_string()), +/// SortedColumn::new("age", "desc".to_string()), +/// ]); +/// +/// assert_eq!(sorting.columns.len(), 2); +/// assert_eq!(sorting.sql, " ORDER BY age DESC, name ASC"); +/// ``` +/// impl Sorting { pub fn new(columns: Vec) -> Sorting { let mut columns = columns; diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -}