You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

234 lines
6.2 KiB

  1. use serde::{Deserialize, Serialize};
  2. use crate::schema::users;
  3. use crate::utils::regex;
  4. use derive_more::{Display, Into};
  5. use std::convert::TryFrom;
  6. use std::{convert::TryInto, str::FromStr};
  7. use validators::prelude::*;
  8. ///newtype to constrain id to positive int values
  9. ///
  10. ///sqlite does not allow u32 for primary keys
  11. #[derive(
  12. Debug,
  13. Clone,
  14. Eq,
  15. Hash,
  16. PartialEq,
  17. Deserialize,
  18. Display,
  19. Into,
  20. Serialize,
  21. DieselNewType,
  22. )]
  23. #[serde(try_from = "u32", into = "u32")]
  24. pub struct UserId(i32);
  25. impl From<UserId> for u32 {
  26. fn from(s: UserId) -> u32 {
  27. //this should be safe to unwrap since our newtype
  28. //does not allow negative values
  29. s.0.try_into().unwrap()
  30. }
  31. }
  32. impl FromStr for UserId {
  33. type Err = String;
  34. fn from_str(s: &str) -> Result<Self, Self::Err> {
  35. if let Ok(num) = s.parse::<u32>() {
  36. num.try_into()
  37. .map_err(|err| {
  38. format!("negative values are not allowed: {}", err)
  39. })
  40. .map(UserId)
  41. } else {
  42. Err("expected unsigned int, received string".to_owned())
  43. }
  44. }
  45. }
  46. impl TryFrom<u32> for UserId {
  47. type Error = String;
  48. fn try_from(value: u32) -> Result<Self, Self::Error> {
  49. value
  50. .try_into()
  51. .map_err(|err| format!("error while converting user_id: {}", err))
  52. .map(UserId)
  53. }
  54. }
  55. #[derive(Validator, Debug, Clone, DieselNewType)]
  56. #[validator(regex(regex::USERNAME_REG))]
  57. pub struct Username(String);
  58. impl Username {
  59. pub fn as_str(&self) -> &str {
  60. &self.0
  61. }
  62. }
  63. #[derive(Validator, Debug, Clone, DieselNewType)]
  64. #[validator(line(char_length(max = 200)))]
  65. pub struct Password(String);
  66. impl Password {
  67. pub fn as_str(&self) -> &str {
  68. &self.0
  69. }
  70. }
  71. #[derive(Debug, Clone, Deserialize, Serialize, Queryable, Identifiable)]
  72. #[table_name = "users"]
  73. pub struct User {
  74. pub id: UserId,
  75. pub name: Username,
  76. #[serde(skip_serializing)]
  77. pub password: Password,
  78. pub created_at: chrono::NaiveDateTime,
  79. }
  80. #[derive(Debug, Clone, Insertable, Deserialize)]
  81. #[table_name = "users"]
  82. pub struct NewUser {
  83. pub name: Username,
  84. #[serde(skip_serializing)]
  85. pub password: Password,
  86. }
  87. #[derive(Debug, Clone, Deserialize)]
  88. #[serde(try_from = "u16")]
  89. pub struct PaginationOffset(u16);
  90. impl PaginationOffset {
  91. pub fn as_uint(&self) -> u16 {
  92. self.0
  93. }
  94. }
  95. impl TryFrom<u16> for PaginationOffset {
  96. type Error = String;
  97. fn try_from(value: u16) -> Result<Self, Self::Error> {
  98. if value <= 2500 {
  99. Ok(PaginationOffset(value))
  100. } else {
  101. Err("Failed to validate".to_owned())
  102. }
  103. }
  104. }
  105. #[derive(Debug, Clone, Deserialize)]
  106. #[serde(try_from = "u16")]
  107. pub struct PaginationLimit(u16);
  108. impl PaginationLimit {
  109. pub fn as_uint(&self) -> u16 {
  110. self.0
  111. }
  112. }
  113. impl TryFrom<u16> for PaginationLimit {
  114. type Error = String;
  115. fn try_from(value: u16) -> Result<Self, Self::Error> {
  116. if value <= 50 {
  117. Ok(PaginationLimit(value))
  118. } else {
  119. Err("Failed to validate".to_owned())
  120. }
  121. }
  122. }
  123. #[derive(Debug, Clone, Deserialize)]
  124. #[serde(try_from = "u16")]
  125. pub struct PaginationPage(u16);
  126. impl PaginationPage {
  127. pub fn as_uint(&self) -> u16 {
  128. self.0
  129. }
  130. }
  131. impl TryFrom<u16> for PaginationPage {
  132. type Error = String;
  133. fn try_from(value: u16) -> Result<Self, Self::Error> {
  134. if value <= 50 {
  135. Ok(PaginationPage(value))
  136. } else {
  137. Err("Failed to validate".to_owned())
  138. }
  139. }
  140. }
  141. #[derive(Debug, Clone, Deserialize)]
  142. pub struct Pagination {
  143. pub page: PaginationPage,
  144. pub limit: PaginationLimit,
  145. }
  146. impl Pagination {
  147. pub fn calc_offset(&self) -> PaginationOffset {
  148. let res = self.page.as_uint() * self.limit.as_uint();
  149. PaginationOffset::try_from(res).unwrap()
  150. }
  151. }
  152. #[derive(Debug, Clone, Deserialize)]
  153. pub struct SearchQuery(String);
  154. impl SearchQuery {
  155. pub fn as_str(&self) -> &str {
  156. &self.0
  157. }
  158. }
  159. #[derive(Debug, Clone, Deserialize)]
  160. pub struct UserSearchRequest {
  161. pub q: SearchQuery,
  162. // pub pagination: Pagination
  163. }
  164. #[cfg(test)]
  165. mod test {
  166. use super::*;
  167. #[test]
  168. fn user_model_refinement_test() {
  169. //yes I had been watching a lot of star wars lately
  170. let mb_user = serde_json::from_str::<User>(
  171. r#"{"id":1,"name":"chewbacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  172. );
  173. // println!("{:?}", mb_user);
  174. assert_eq!(mb_user.is_ok(), true);
  175. let mb_user = serde_json::from_str::<User>(
  176. r#"{"id":1,"name":"chew-bacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  177. );
  178. assert_eq!(mb_user.is_ok(), true);
  179. let mb_user = serde_json::from_str::<User>(
  180. r#"{"id":1,"name":"chew.bacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  181. );
  182. assert_eq!(mb_user.is_ok(), false);
  183. let mb_user = serde_json::from_str::<User>(
  184. r#"{"id":-1,"name":"chewbacca","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  185. );
  186. assert_eq!(mb_user.is_ok(), false);
  187. let mb_user = serde_json::from_str::<User>(
  188. r#"{"id":1,"name":"ch","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  189. );
  190. assert_eq!(mb_user.is_ok(), false);
  191. let mb_user = serde_json::from_str::<User>(
  192. r#"{"id":1,"name":"chaegw;eaef","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  193. );
  194. assert_eq!(mb_user.is_ok(), false);
  195. let mb_user = serde_json::from_str::<User>(
  196. r#"{"id":1,"name":"chaegw_eaef","password":"aeqfq3fq","created_at":"2021-05-12T12:37:56"}"#,
  197. );
  198. assert_eq!(mb_user.is_ok(), false);
  199. }
  200. #[test]
  201. fn pagination_refinement_test() {
  202. let mb_pag =
  203. serde_json::from_str::<Pagination>(r#"{"limit":5,"page":5}"#);
  204. // println!("{:?}", mb_pag);
  205. assert_eq!(mb_pag.is_ok(), true);
  206. let mb_pag =
  207. serde_json::from_str::<Pagination>(r#"{"limit":51,"page":5}"#);
  208. assert_eq!(mb_pag.is_ok(), false);
  209. let mb_pag =
  210. serde_json::from_str::<Pagination>(r#"{"limit":5,"page":51}"#);
  211. assert_eq!(mb_pag.is_ok(), false);
  212. }
  213. }