add newtype

This commit is contained in:
Namekuji 2023-05-31 12:09:30 -04:00
parent bc69e2df87
commit 29e914c9c3
10 changed files with 101 additions and 52 deletions

View File

@ -9,6 +9,7 @@ edition = "2021"
async-trait = "0.1.68" async-trait = "0.1.68"
chrono = "0.4.24" chrono = "0.4.24"
database = { path = "../database" } database = { path = "../database" }
derive_more = "0.99.17"
jsonschema = "0.17.0" jsonschema = "0.17.0"
once_cell = "1.17.1" once_cell = "1.17.1"
parse-display = "0.8.0" parse-display = "0.8.0"

View File

@ -1,6 +1,6 @@
//! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3 //! `SeaORM` Entity. Generated by sea-orm-codegen 0.11.3
use super::sea_orm_active_enums::AntennaSrcEnum; use super::{newtype, sea_orm_active_enums::AntennaSrcEnum};
use sea_orm::entity::prelude::*; use sea_orm::entity::prelude::*;
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] #[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
@ -17,7 +17,7 @@ pub struct Model {
#[sea_orm(column_name = "userListId")] #[sea_orm(column_name = "userListId")]
pub user_list_id: Option<String>, pub user_list_id: Option<String>,
#[sea_orm(column_type = "JsonBinary")] #[sea_orm(column_type = "JsonBinary")]
pub keywords: Json, pub keywords: newtype::Keyword,
#[sea_orm(column_name = "withFile")] #[sea_orm(column_name = "withFile")]
pub with_file: bool, pub with_file: bool,
pub expression: Option<String>, pub expression: Option<String>,
@ -28,11 +28,11 @@ pub struct Model {
pub with_replies: bool, pub with_replies: bool,
#[sea_orm(column_name = "userGroupJoiningId")] #[sea_orm(column_name = "userGroupJoiningId")]
pub user_group_joining_id: Option<String>, pub user_group_joining_id: Option<String>,
pub users: Vec<String>, pub users: newtype::StringVec,
#[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")] #[sea_orm(column_name = "excludeKeywords", column_type = "JsonBinary")]
pub exclude_keywords: Json, pub exclude_keywords: newtype::Keyword,
#[sea_orm(column_type = "JsonBinary")] #[sea_orm(column_type = "JsonBinary")]
pub instances: Json, pub instances: newtype::StringVec,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View File

@ -33,6 +33,7 @@ pub mod migrations;
pub mod moderation_log; pub mod moderation_log;
pub mod muted_note; pub mod muted_note;
pub mod muting; pub mod muting;
pub mod newtype;
pub mod note; pub mod note;
pub mod note_edit; pub mod note_edit;
pub mod note_favorite; pub mod note_favorite;

View File

@ -0,0 +1,51 @@
#[macro_export]
macro_rules! impl_json_newtype {
($a:tt) => {
impl From<$a> for Value {
fn from(source: $a) -> Self {
Value::Json(serde_json::to_value(source).ok().map(Box::new))
}
}
impl TryGetable for $a {
fn try_get_by<I: sea_orm::ColIdx>(
res: &QueryResult,
idx: I,
) -> Result<Self, TryGetError> {
let json_value: serde_json::Value =
res.try_get_by(idx).map_err(TryGetError::DbErr)?;
serde_json::from_value(json_value)
.map_err(|e| TryGetError::DbErr(DbErr::Json(e.to_string())))
}
}
impl sea_query::ValueType for $a {
fn try_from(v: Value) -> Result<Self, sea_query::ValueTypeErr> {
match v {
Value::Json(Some(x)) => Ok($a(
serde_json::from_value(*x).map_err(|_| sea_query::ValueTypeErr)?
)),
_ => Err(sea_query::ValueTypeErr),
}
}
fn type_name() -> String {
stringify!($a).to_owned()
}
fn array_type() -> sea_orm::sea_query::ArrayType {
sea_orm::sea_query::ArrayType::Json
}
fn column_type() -> sea_query::ColumnType {
sea_query::ColumnType::Json
}
}
impl sea_query::Nullable for $a {
fn null() -> Value {
Value::Json(None)
}
}
};
}

View File

@ -0,0 +1,17 @@
mod macros;
use derive_more::From;
use schemars::JsonSchema;
use sea_orm::{sea_query, DbErr, QueryResult, TryGetError, TryGetable, Value};
use serde::{Deserialize, Serialize};
use crate::impl_json_newtype;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, From)]
pub struct Keyword(pub Vec<Vec<String>>);
impl_json_newtype!(Keyword);
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema, From)]
pub struct StringVec(pub Vec<String>);
impl_json_newtype!(StringVec);

View File

@ -3,7 +3,7 @@ use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use crate::entity::{antenna, antenna_note, user_group_joining}; use crate::entity::{antenna, antenna_note, user_group_joining};
use crate::error::Error; use crate::error::Error;
use crate::schema::{antenna::Antenna, json_to_keyword, json_to_string_list}; use crate::schema::antenna::Antenna;
use super::Repository; use super::Repository;
@ -30,13 +30,13 @@ impl Repository<Antenna> for antenna::Model {
id: self.id, id: self.id,
created_at: self.created_at.into(), created_at: self.created_at.into(),
name: self.name, name: self.name,
keywords: json_to_keyword(&self.keywords), keywords: self.keywords,
exclude_keywords: json_to_keyword(&self.exclude_keywords), exclude_keywords: self.exclude_keywords,
src: self.src.try_into()?, src: self.src.try_into()?,
user_list_id: self.user_list_id, user_list_id: self.user_list_id,
user_group_id, user_group_id,
users: self.users, users: self.users,
instances: json_to_string_list(&self.instances), instances: self.instances,
case_sensitive: self.case_sensitive, case_sensitive: self.case_sensitive,
notify: self.notify, notify: self.notify,
with_replies: self.with_replies, with_replies: self.with_replies,

View File

@ -4,8 +4,8 @@ use parse_display::FromStr;
use schemars::JsonSchema; use schemars::JsonSchema;
use utoipa::ToSchema; use utoipa::ToSchema;
use super::{Keyword, Schema, StringList}; use super::Schema;
use crate::entity::sea_orm_active_enums::AntennaSrcEnum; use crate::entity::{newtype, sea_orm_active_enums::AntennaSrcEnum};
#[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)] #[derive(Clone, Debug, PartialEq, Eq, JsonSchema, ToSchema)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
@ -13,14 +13,14 @@ pub struct Antenna {
pub id: String, pub id: String,
pub created_at: chrono::DateTime<chrono::Utc>, pub created_at: chrono::DateTime<chrono::Utc>,
pub name: String, pub name: String,
pub keywords: Keyword, pub keywords: newtype::Keyword,
pub exclude_keywords: Keyword, pub exclude_keywords: newtype::Keyword,
#[schema(inline)] #[schema(inline)]
pub src: AntennaSrc, pub src: AntennaSrc,
pub user_list_id: Option<String>, pub user_list_id: Option<String>,
pub user_group_id: Option<String>, pub user_group_id: Option<String>,
pub users: StringList, pub users: newtype::StringVec,
pub instances: StringList, pub instances: newtype::StringVec,
#[serde(default)] #[serde(default)]
pub case_sensitive: bool, pub case_sensitive: bool,
#[serde(default)] #[serde(default)]

View File

@ -3,10 +3,6 @@ pub mod app;
use jsonschema::JSONSchema; use jsonschema::JSONSchema;
use schemars::{schema_for, JsonSchema}; use schemars::{schema_for, JsonSchema};
use serde_json::Value;
type Keyword = Vec<Vec<String>>;
type StringList = Vec<String>;
/// Structs of schema defitions implement this trait in order to /// Structs of schema defitions implement this trait in order to
/// provide the JSON Schema validator [`jsonschema::JSONSchema`]. /// provide the JSON Schema validator [`jsonschema::JSONSchema`].
@ -23,29 +19,3 @@ trait Schema<T: JsonSchema> {
.expect("Unable to compile schema") .expect("Unable to compile schema")
} }
} }
pub(crate) fn json_to_keyword(value: &Value) -> Keyword {
match value.as_array() {
None => vec![vec![]],
Some(or_vec) => or_vec
.iter()
.map(|and_val| match and_val.as_array() {
None => vec![],
Some(and_vec) => and_vec
.iter()
.map(|word| word.as_str().unwrap_or_default().to_string())
.collect(),
})
.collect(),
}
}
pub(crate) fn json_to_string_list(value: &Value) -> StringList {
match value.as_array() {
None => vec![],
Some(v) => v
.iter()
.map(|s| s.as_str().unwrap_or_default().to_string())
.collect(),
}
}

View File

@ -7,7 +7,6 @@ use model::entity::{antenna, sea_orm_active_enums::AntennaSrcEnum, user};
use sea_orm::{ use sea_orm::{
ActiveModelTrait, ActiveValue::Set, DatabaseConnection, DbErr, EntityTrait, TransactionTrait, ActiveModelTrait, ActiveValue::Set, DatabaseConnection, DbErr, EntityTrait, TransactionTrait,
}; };
use serde_json::json;
use std::env; use std::env;
use util::{ use util::{
id::{create_id, init_id}, id::{create_id, init_id},
@ -64,8 +63,16 @@ async fn setup_model(db: &DatabaseConnection) {
user_id: Set(user_id.to_owned()), user_id: Set(user_id.to_owned()),
name: Set("Test Antenna".to_string()), name: Set("Test Antenna".to_string()),
src: Set(AntennaSrcEnum::All), src: Set(AntennaSrcEnum::All),
keywords: Set(json!([["foo", "bar"], ["foobar"]])), keywords: Set(vec![
exclude_keywords: Set(json!([["abc"], ["def", "ghi"]])), vec!["foo".to_string(), "bar".to_string()],
vec!["foobar".to_string()],
]
.into()),
exclude_keywords: Set(vec![
vec!["abc".to_string()],
vec!["def".to_string(), "ghi".to_string()],
]
.into()),
with_file: Set(false), with_file: Set(false),
notify: Set(true), notify: Set(true),
case_sensitive: Set(true), case_sensitive: Set(true),

View File

@ -38,16 +38,18 @@ mod it_test {
keywords: vec![ keywords: vec![
vec!["foo".to_string(), "bar".to_string()], vec!["foo".to_string(), "bar".to_string()],
vec!["foobar".to_string()] vec!["foobar".to_string()]
], ]
.into(),
exclude_keywords: vec![ exclude_keywords: vec![
vec!["abc".to_string()], vec!["abc".to_string()],
vec!["def".to_string(), "ghi".to_string()] vec!["def".to_string(), "ghi".to_string()]
], ]
.into(),
src: schema::antenna::AntennaSrc::All, src: schema::antenna::AntennaSrc::All,
user_list_id: None, user_list_id: None,
user_group_id: None, user_group_id: None,
users: vec![], users: vec![].into(),
instances: vec![], instances: vec![].into(),
case_sensitive: true, case_sensitive: true,
notify: true, notify: true,
with_replies: false, with_replies: false,