diff --git a/Cargo.lock b/Cargo.lock index f3e0f52..9b572cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -974,6 +974,7 @@ dependencies = [ "serde", "serde_json", "sqlx", + "sqlx-core", "tera", "thiserror", "tokio 1.17.0", diff --git a/api/Cargo.toml b/api/Cargo.toml index 12920a6..3b9af40 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -30,7 +30,8 @@ serde = { version = "1.0.136", features = ["derive"] } serde_json = { version = "1.0.79" } toml = { version = "0.5.8" } -sqlx = { version = "0.5.11", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] } +sqlx = { version = "0.5.13", features = ["migrate", "runtime-actix-rustls", "all-types", "postgres"] } +sqlx-core = { version = "0.5.13" } thiserror = { version = "1.0.30" } diff --git a/api/assets/index.html b/api/assets/index.html index 71611da..34ba01e 100644 --- a/api/assets/index.html +++ b/api/assets/index.html @@ -99,12 +99,18 @@ opEL.addEventListener('change', () => { switch (opEL.value) { - case 'auto-login': + case 'auto-login': { paramsEl.value = `login=Eraden\npassword=text` - return send(mthEl.value = 'POST', urlEl.value = '/admin/sign-in', { login: 'Eraden', password: 'test' }) - case 'get-products': - return send(mthEl.value = 'GET', urlEl.value = '/admin/api/v1/products', {}) - case 'create-product': + mthEl.value = 'POST'; + urlEl.value = '/admin/sign-in'; + break; + } + case 'get-products': { + mthEl.value = 'GET'; + urlEl.value = '/admin/api/v1/products'; + break; + } + case 'create-product': { const p = { name: 'Foo', short_description: 'asd', @@ -112,8 +118,11 @@ price_major: 12, price_minor: 0, }; - paramsEl.value = Object.entries(p).map((k, v) => `${ k }=${ v }}`).join('\n'); - return send(mthEl.value = 'POST', urlEl.value = '/admin/api/v1/product', p) + paramsEl.value = Object.entries(p).map(([k, v]) => `${ k }=${ v }`).join('\n'); + mthEl.value = 'POST'; + urlEl.value = '/admin/api/v1/product'; + break; + } } }) diff --git a/api/src/actors/database/products.rs b/api/src/actors/database/products.rs index d192ca8..9cf065d 100644 --- a/api/src/actors/database/products.rs +++ b/api/src/actors/database/products.rs @@ -36,7 +36,8 @@ SELECT id, long_description, category, price_major, - price_minor + price_minor, + deliver_days_flag FROM products "#, ) diff --git a/api/src/model.rs b/api/src/model.rs index e48381a..192faa7 100644 --- a/api/src/model.rs +++ b/api/src/model.rs @@ -8,6 +8,8 @@ use serde::{Deserialize, Deserializer, Serialize}; pub enum TransformError { #[error("Given value is below minimal value")] BelowMinimal, + #[error("Given value is not valid day flag")] + NotDay, } pub type RecordId = i32; @@ -169,6 +171,108 @@ impl<'de> serde::Deserialize<'de> for NonNegative { } } +#[derive(Serialize, Deserialize, Debug, Copy, Clone, Display, From)] +#[serde(rename_all = "lowercase")] +pub enum Day { + Monday = 1 << 0, + Tuesday = 1 << 1, + Wednesday = 1 << 2, + Thursday = 1 << 3, + Friday = 1 << 4, + Saturday = 1 << 5, + Sunday = 1 << 6, +} + +impl TryFrom for Day { + type Error = TransformError; + + fn try_from(value: i32) -> Result { + if value == (Day::Monday as i32) { + Ok(Day::Monday) + } else if value == (Day::Tuesday as i32) { + Ok(Day::Tuesday) + } else if value == (Day::Wednesday as i32) { + Ok(Day::Wednesday) + } else if value == (Day::Thursday as i32) { + Ok(Day::Thursday) + } else if value == (Day::Friday as i32) { + Ok(Day::Friday) + } else if value == (Day::Saturday as i32) { + Ok(Day::Saturday) + } else if value == (Day::Sunday as i32) { + Ok(Day::Sunday) + } else { + Err(TransformError::NotDay) + } + } +} + +#[derive(Serialize, Deserialize, Deref, Debug)] +#[serde(transparent)] +pub struct Days(Vec); + +impl<'q> ::sqlx::encode::Encode<'q, sqlx::Postgres> for Days +where + i32: ::sqlx::encode::Encode<'q, sqlx::Postgres>, +{ + fn encode_by_ref( + &self, + buf: &mut >::ArgumentBuffer, + ) -> ::sqlx::encode::IsNull { + let value = self.0.iter().fold(1, |memo, v| memo | *v as i32); + + >::encode_by_ref(&value, buf) + } + + fn size_hint(&self) -> usize { + >::size_hint(&Default::default()) + } +} + +impl<'r> ::sqlx::decode::Decode<'r, sqlx::Postgres> for Days +where + i32: ::sqlx::decode::Decode<'r, sqlx::Postgres>, +{ + fn decode( + value: >::ValueRef, + ) -> ::std::result::Result< + Self, + ::std::boxed::Box< + dyn ::std::error::Error + 'static + ::std::marker::Send + ::std::marker::Sync, + >, + > { + let value = >::decode(value)?; + Ok(Days( + (0..9) + .into_iter() + .filter_map(|n| { + eprintln!( + "d {} {} {} {:?}", + n, + 1 << n, + value & 1 << n, + Day::try_from(value & 1 << n).ok() + ); + Day::try_from(value & 1 << n).ok() + }) + .collect(), + )) + } +} + +impl sqlx::Type for Days +where + i32: ::sqlx::Type, +{ + fn type_info() -> ::TypeInfo { + >::type_info() + } + + fn compatible(ty: &::TypeInfo) -> bool { + >::compatible(ty) + } +} + #[derive(sqlx::Type, Serialize, Deserialize, Deref, Debug)] #[sqlx(transparent)] #[serde(transparent)] @@ -252,6 +356,7 @@ pub struct Product { pub category: Option, pub price_major: PriceMajor, pub price_minor: PriceMinor, + pub deliver_days_flag: Days, } #[derive(sqlx::Type, Serialize, Deserialize)] diff --git a/db/migrate/202204172215_delivery_and_price.sql b/db/migrate/202204172215_delivery_and_price.sql new file mode 100644 index 0000000..30604fa --- /dev/null +++ b/db/migrate/202204172215_delivery_and_price.sql @@ -0,0 +1,14 @@ +BEGIN; + +CREATE TYPE "Price" AS ( + "value" integer, + "currency" varchar +); + +ALTER TABLE products +ADD COLUMN "deliver_days_flag" int +NOT NULL +-- 0x1111111 +DEFAULT 127; + +COMMIT;