Try display something

This commit is contained in:
Adrian Woźniak 2023-07-27 17:36:30 +02:00
parent e8d63c3b84
commit a00602192a
15 changed files with 994 additions and 71 deletions

770
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -3,7 +3,8 @@ name = "oswilno-actix-admin"
version = "0.1.0"
edition = "2021"
proc-macro=true
[lib]
proc-macro = true
[dependencies]

View File

@ -1,14 +1,151 @@
pub fn add(left: usize, right: usize) -> usize {
left + right
extern crate proc_macro;
use std::iter::Peekable;
use proc_macro::token_stream::IntoIter;
use proc_macro::{TokenStream, TokenTree};
fn expect_iden<F: FnMut(String) -> bool>(s: &mut Peekable<IntoIter>, mut name: F) {
let token = s.next().unwrap();
match token {
TokenTree::Ident(iden) => {
if !name(iden.to_string()) {
panic!("expect identifier");
}
}
_ => panic!("expect identifier got {token:?}"),
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
fn skip_proc_macro(s: &mut Peekable<IntoIter>) {
while let Some(token) = s.peek() {
match token {
TokenTree::Punct(p) if p.to_string().as_str() == "#" => {
s.next();
s.next();
}
_ => return,
}
}
}
#[derive(Default, Debug)]
struct VariantInfo {
rs_name: String,
db_name: String,
}
#[derive(Default, Debug)]
struct EnumDef {
name: String,
variants: Vec<VariantInfo>,
}
#[proc_macro_derive(ActixAdminEnum)]
pub fn derive_actix_admin_enum(item: TokenStream) -> TokenStream {
let clone = item.clone();
let mut it = clone.into_iter().peekable();
skip_proc_macro(&mut it);
expect_iden(&mut it, |iden| &iden == "pub");
expect_iden(&mut it, |iden| &iden == "enum");
let mut def = EnumDef::default();
expect_iden(&mut it, |iden| {
def.name = iden;
true
});
let mut it = if let TokenTree::Group(g) = it.next().unwrap() {
g.stream().into_iter().peekable()
} else {
panic!("expect enum body");
};
// Parse macros
parse_enum_variants(&mut it, &mut def);
let mut buffer = String::new();
buffer.push_str(&format!("impl std::str::FromStr for {} {{\n type Err = ();\n fn from_str(s: &str) -> Result<Self, ()> {{\n match s {{\n", def.name));
for v in def.variants {
buffer.push_str(&format!(
" {:?} => Ok({}::{}),\n",
v.db_name, def.name, v.rs_name
));
}
buffer.push_str(" _ => Err(()),\n");
buffer.push_str(" }\n }\n}");
// eprintln!("{buffer}");
buffer.as_str().parse().unwrap()
}
fn parse_enum_variants(body_it: &mut Peekable<IntoIter>, def: &mut EnumDef) {
while body_it.peek().is_some() {
if let Some(variant) = parse_enum_variant(body_it) {
def.variants.push(variant);
}
if let Some(TokenTree::Punct(p)) = body_it.peek() {
if p.to_string().as_str() == "," {
body_it.next();
}
}
}
}
fn parse_enum_variant(body_it: &mut Peekable<IntoIter>) -> Option<VariantInfo> {
let mut variant_info = VariantInfo::default();
while body_it.peek().is_some() {
if let Some(db_name) = parse_enum_variant_macro(body_it) {
variant_info.db_name = db_name;
break;
}
if let Some(TokenTree::Punct(p)) = body_it.peek() {
if p.to_string().as_str() == "," {
body_it.next();
}
}
}
let iden = body_it.next().expect("No variant name");
variant_info.rs_name = iden.to_string();
Some(variant_info)
}
fn parse_enum_variant_macro(body_it: &mut Peekable<IntoIter>) -> Option<String> {
let (_punc, group) = (
body_it.next().expect("No # for macro"),
body_it.next().expect("No macro body"),
);
// eprintln!("{punc:#?} {group:#?}");
let mut it = if let TokenTree::Group(g) = group {
// parse #[sea_orm]
g.stream().into_iter().peekable()
} else {
panic!("Unexpected token after #: {group:?}");
};
let token = it.next();
if let Some(TokenTree::Ident(iden)) = token {
if iden.to_string().as_str() != "sea_orm" {
eprintln!("iden is not sea_orm");
return None;
}
} else {
eprintln!("token should be iden but is {token:#?}");
return None;
}
let it = if let Some(TokenTree::Group(g)) = it.next() {
g.stream().into_iter().peekable()
} else {
return None;
};
let mut it = it.skip_while(|t| match t {
TokenTree::Ident(id) => id.to_string().as_str() != "string_value",
_ => true,
});
let (_id, _punct, literal) = (it.next(), it.next(), it.next());
match literal {
Some(TokenTree::Literal(l)) => Some(l.to_string().replace('\"', "")),
_ => None,
}
}

View File

@ -20,6 +20,16 @@ fn create_actix_admin_builder() -> ActixAdminBuilder {
};
let mut admin_builder = ActixAdminBuilder::new(configuration);
{
use oswilno_contract::prelude::ParkingSpaces;
let view = ActixAdminViewModel::from(ParkingSpaces);
admin_builder.add_entity_to_category::<ParkingSpaces>(&view, "Parking spaces");
}
{
use oswilno_contract::prelude::ParkingSpaceRents;
let view = ActixAdminViewModel::from(ParkingSpaceRents);
admin_builder.add_entity_to_category::<ParkingSpaceRents>(&view, "Parking spaces");
}
admin_builder
}

View File

@ -13,3 +13,4 @@ sea-orm = { version = "0.11.3", features = ["postgres-array", "runtime-actix-rus
serde = { version = "1.0.175", features = ["derive"] }
tera = "1.19.0"
uuid = { version = "1.4.1", features = ["v4"] }
oswilno-actix-admin = { path = "../oswilno-actix-admin" }

View File

@ -64,7 +64,6 @@ impl PrimaryKeyTrait for PrimaryKey {
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Images,
ParkingSpaces,
}
@ -88,18 +87,11 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Images => Entity::has_many(super::images::Entity).into(),
Self::ParkingSpaces => Entity::has_many(super::parking_spaces::Entity).into(),
}
}
}
impl Related<super::images::Entity> for Entity {
fn to() -> RelationDef {
Relation::Images.def()
}
}
impl Related<super::parking_spaces::Entity> for Entity {
fn to() -> RelationDef {
Relation::ParkingSpaces.def()

View File

@ -59,9 +59,7 @@ impl PrimaryKeyTrait for PrimaryKey {
}
#[derive(Copy, Clone, Debug, EnumIter)]
pub enum Relation {
Accounts,
}
pub enum Relation {}
impl ColumnTrait for Column {
type EntityName = Entity;
@ -80,18 +78,7 @@ impl ColumnTrait for Column {
impl RelationTrait for Relation {
fn def(&self) -> RelationDef {
match self {
Self::Accounts => Entity::belongs_to(super::accounts::Entity)
.from(Column::AccountId)
.to(super::accounts::Column::Id)
.into(),
}
}
}
impl Related<super::accounts::Entity> for Entity {
fn to() -> RelationDef {
Relation::Accounts.def()
panic!("No RelationDef")
}
}

View File

@ -11,8 +11,19 @@ impl EntityName for Entity {
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
#[derive(
Clone,
Debug,
PartialEq,
DeriveModel,
DeriveActiveModel,
Eq,
DeriveActixAdmin,
DeriveActixAdminModel,
DeriveActixAdminViewModel,
)]
pub struct Model {
#[actix_admin(primary_key)]
pub id: i32,
pub price: i32,
pub parking_space_id: i32,
@ -80,3 +91,6 @@ impl Related<super::parking_spaces::Entity> for Entity {
}
impl ActiveModelBehavior for ActiveModel {}
use actix_admin::prelude::*;
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
impl ActixAdminModelFilterTrait<Entity> for Entity {}

View File

@ -13,8 +13,19 @@ impl EntityName for Entity {
}
}
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
#[derive(
Clone,
Debug,
PartialEq,
DeriveModel,
DeriveActiveModel,
Eq,
DeriveActixAdmin,
DeriveActixAdminModel,
DeriveActixAdminViewModel,
)]
pub struct Model {
#[actix_admin(primary_key)]
pub id: i32,
pub state: ParkingSpaceState,
pub location: String,
@ -56,7 +67,7 @@ impl ColumnTrait for Column {
fn def(&self) -> ColumnDef {
match self {
Self::Id => ColumnType::Integer.def(),
Self::State => ParkingSpaceState::db_type().def(),
Self::State => ParkingSpaceState::db_type(),
Self::Location => ColumnType::String(None).def(),
Self::AccountId => ColumnType::Integer.def(),
Self::CreatedAt => ColumnType::DateTime.def(),
@ -90,3 +101,6 @@ impl Related<super::parking_space_rents::Entity> for Entity {
}
impl ActiveModelBehavior for ActiveModel {}
use actix_admin::prelude::*;
impl ActixAdminModelValidationTrait<ActiveModel> for Entity {}
impl ActixAdminModelFilterTrait<Entity> for Entity {}

View File

@ -4,6 +4,7 @@ use sea_orm::entity::prelude::*;
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "image_state")]
#[derive(oswilno_actix_admin::ActixAdminEnum)]
pub enum ImageState {
#[sea_orm(string_value = "approved")]
Approved,
@ -18,6 +19,7 @@ pub enum ImageState {
db_type = "Enum",
enum_name = "parking_space_state"
)]
#[derive(oswilno_actix_admin::ActixAdminEnum)]
pub enum ParkingSpaceState {
#[sea_orm(string_value = "Banned")]
Banned,
@ -28,6 +30,7 @@ pub enum ParkingSpaceState {
}
#[derive(Debug, Clone, PartialEq, Eq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "userrole")]
#[derive(oswilno_actix_admin::ActixAdminEnum)]
pub enum Userrole {
#[sea_orm(string_value = "Admin")]
Admin,

View File

@ -9,3 +9,7 @@ actix = "0.13.0"
actix-web = "4.3.1"
askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] }
tokio = { version = "1.29.1", features = ["full"] }
oswilno-contract = { path = "../oswilno-contract" }
sea-orm = { version = "0.12.1", features = ["runtime-actix-rustls", "sqlx", "sqlx-postgres"] }
serde = { version = "1.0.176", features = ["derive"] }
serde_json = "1.0.104"

View File

@ -1,3 +1,18 @@
use actix_web::web::ServiceConfig;
use actix_web::web::{ServiceConfig, Data};
use actix_web::{get, HttpResponse};
pub fn mount(config: &mut ServiceConfig) {}
pub fn mount(config: &mut ServiceConfig) {
config.service(all_parking_spaces);
}
#[get("/ps")]
async fn all_parking_spaces(db: Data<sea_orm::DatabaseConnection>) -> HttpResponse {
use sea_orm::*;
use oswilno_contract::{*, prelude::*};
use parking_spaces::*;
let db = db.into_inner();
let spaces: Vec<Model> = Entity::find().all(&*db).await.unwrap();
HttpResponse::Ok().json(spaces)
}

View File

@ -18,6 +18,8 @@ serde = { version = "1.0.175", features = ["derive"] }
serde_json = "1.0.103"
tokio = { version = "1.29.1", features = ["full"] }
toml = "0.7.6"
tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] }
tracing = "0.1.37"
[dev-dependencies]
insta = { version = "1.31.0", features = ["ron"] }

View File

@ -1,4 +1,8 @@
use actix_web::{web, App, HttpServer, Responder};
use std::time::Duration;
use actix_web::web::Data;
use actix_web::{middleware, web, App, HttpServer, Responder};
use sea_orm::ConnectOptions;
async fn index() -> impl Responder {
"Hello world!"
@ -6,8 +10,33 @@ async fn index() -> impl Responder {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
{
use tracing_subscriber::prelude::*;
use tracing_subscriber::{fmt, EnvFilter};
let fmt_layer = fmt::layer().with_target(false);
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|_| EnvFilter::try_new("info"))
.unwrap();
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.init();
}
let mut db_opts = ConnectOptions::new("postgres://postgres@localhost/oswilno".to_string());
db_opts
.max_connections(100)
.min_connections(5)
.connect_timeout(Duration::from_secs(8))
.idle_timeout(Duration::from_secs(8))
.sqlx_logging(true);
let conn: sea_orm::DatabaseConnection = sea_orm::Database::connect(db_opts).await.unwrap();
HttpServer::new(move || {
App::new()
.wrap(middleware::Logger::default())
.app_data(Data::new(conn.clone()))
.service(web::scope("/app").route("/index.html", web::get().to(index)))
.configure(oswilno_parking_space::mount)
.configure(oswilno_admin::mount)

View File

@ -1,4 +1,4 @@
set -ex
# set -ex
sea-orm-cli generate entity -v -l --expanded-format -o ./crates/oswilno-contract/src -u postgres://postgres@localhost/oswilno
@ -17,4 +17,6 @@ for f in $( ls ./crates/oswilno-contract/src/*.rs ); do
fi
done
sed -i 's/pub enum/#[derive(oswilno_actix_admin::ActixAdminEnum)] pub enum/g' ./crates/oswilno-contract/src/sea_orm_active_enums.rs
cargo fmt