Add much stuff for rewrite

This commit is contained in:
Tobias Reisinger 2023-11-19 18:54:27 +01:00
parent 4261141c3a
commit bd44dc3183
37 changed files with 1356 additions and 2551 deletions

View file

@ -1,8 +1,13 @@
use std::env;
use crate::db::errors::DatabaseError;
use crate::db::model_utils::Period;
use crate::db::models::{NewSchedule, Periods};
use crate::types::EmgauwaUid;
use diesel::prelude::*;
use diesel_migrations::embed_migrations;
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
use dotenv::dotenv;
use log::{info, trace};
pub mod errors;
pub mod models;
@ -12,7 +17,7 @@ pub mod tag;
mod model_utils;
embed_migrations!("migrations");
pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
fn get_connection() -> SqliteConnection {
dotenv().ok();
@ -23,6 +28,46 @@ fn get_connection() -> SqliteConnection {
}
pub fn run_migrations() {
let connection = get_connection();
embedded_migrations::run(&connection).expect("Failed to run migrations.");
info!("Running migrations");
let mut connection = get_connection();
connection
.run_pending_migrations(MIGRATIONS)
.expect("Failed to run migrations.");
}
fn init_schedule(schedule: &NewSchedule) -> Result<(), DatabaseError> {
trace!("Initializing schedule {:?}", schedule.name);
match schedules::get_schedule_by_uid(schedule.uid.clone()) {
Ok(_) => Ok(()),
Err(err) => match err {
DatabaseError::NotFound => {
trace!("Schedule {:?} not found, inserting", schedule.name);
let mut connection = get_connection();
diesel::insert_into(schema::schedules::table)
.values(schedule)
.execute(&mut connection)
.map(|_| ())
.map_err(DatabaseError::InsertError)
}
_ => Err(err),
},
}
}
pub fn init(db: &str) {
run_migrations();
init_schedule(&NewSchedule {
uid: &EmgauwaUid::Off,
name: "Off",
periods: &Periods(vec![]),
})
.expect("Error initializing schedule Off");
init_schedule(&NewSchedule {
uid: &EmgauwaUid::On,
name: "On",
periods: &Periods(vec![Period::new_on()]),
})
.expect("Error initializing schedule On");
}

View file

@ -11,6 +11,7 @@ pub enum DatabaseError {
NotFound,
Protected,
UpdateError(diesel::result::Error),
Unknown,
}
impl DatabaseError {
@ -47,6 +48,7 @@ impl From<&DatabaseError> for String {
DatabaseError::DeleteError => String::from("error on deleting from database"),
DatabaseError::Protected => String::from("model is protected"),
DatabaseError::UpdateError(_) => String::from("error on updating the model"),
DatabaseError::Unknown => String::from("unknown error"),
}
}
}

View file

@ -1,16 +1,14 @@
use crate::db::models::Periods;
use chrono::{NaiveTime, Timelike};
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::sql_types::Binary;
use diesel::sqlite::Sqlite;
use diesel::{deserialize, serialize};
use serde::{Deserialize, Serialize};
use std::io::Write;
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
#[diesel(sql_type = Binary)]
pub struct Period {
#[serde(with = "period_format")]
pub start: NaiveTime,
@ -41,23 +39,51 @@ mod period_format {
}
}
impl ToSql<Binary, Sqlite> for Periods {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
for period in self.0.iter() {
out.write_all(&[
period.start.hour() as u8,
period.start.minute() as u8,
period.end.hour() as u8,
period.end.minute() as u8,
])?;
impl Period {
pub fn new(start: NaiveTime, end: NaiveTime) -> Self {
Period { start, end }
}
pub fn new_on() -> Self {
Period {
start: NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
end: NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
}
}
}
impl ToSql<Binary, Sqlite> for Periods
where
Vec<u8>: ToSql<Binary, Sqlite>,
{
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> serialize::Result {
let periods_u8: Vec<u8> = self
.0
.iter()
.flat_map(|period| {
let vec = vec![
period.start.hour() as u8,
period.start.minute() as u8,
period.end.hour() as u8,
period.end.minute() as u8,
];
vec
})
.collect();
out.set_value(periods_u8);
Ok(IsNull::No)
}
}
impl FromSql<Binary, Sqlite> for Periods {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
let blob = bytes.unwrap().read_blob();
impl<DB> FromSql<Binary, DB> for Periods
where
DB: diesel::backend::Backend,
Vec<u8>: FromSql<Binary, DB>,
{
fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
let blob: Vec<u8> = Vec::from_sql(bytes).unwrap();
let mut vec = Vec::new();
for i in (3..blob.len()).step_by(4) {
@ -66,8 +92,8 @@ impl FromSql<Binary, Sqlite> for Periods {
let end_val_h: u32 = blob[i - 1] as u32;
let end_val_m: u32 = blob[i] as u32;
vec.push(Period {
start: NaiveTime::from_hms(start_val_h, start_val_m, 0),
end: NaiveTime::from_hms(end_val_h, end_val_m, 0),
start: NaiveTime::from_hms_opt(start_val_h, start_val_m, 0).unwrap(),
end: NaiveTime::from_hms_opt(end_val_h, end_val_m, 0).unwrap(),
});
}
Ok(Periods(vec))

View file

@ -23,7 +23,7 @@ pub struct Schedule {
}
#[derive(Insertable)]
#[table_name = "schedules"]
#[diesel(table_name = crate::db::schema::schedules)]
pub struct NewSchedule<'a> {
pub uid: &'a EmgauwaUid,
pub name: &'a str,
@ -31,26 +31,27 @@ pub struct NewSchedule<'a> {
}
#[derive(Debug, Serialize, Deserialize, AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
pub struct Periods(pub(crate) Vec<Period>);
#[diesel(sql_type = Binary)]
pub struct Periods(pub Vec<Period>);
#[derive(Debug, Serialize, Identifiable, Queryable, Clone)]
#[diesel(table_name = crate::db::schema::tags)]
pub struct Tag {
pub id: i32,
pub tag: String,
}
#[derive(Insertable)]
#[table_name = "tags"]
#[diesel(table_name = crate::db::schema::tags)]
pub struct NewTag<'a> {
pub tag: &'a str,
}
#[derive(Queryable, Associations, Identifiable)]
#[belongs_to(Relay)]
#[belongs_to(Schedule)]
#[belongs_to(Tag)]
#[table_name = "junction_tag"]
#[diesel(belongs_to(Relay))]
#[diesel(belongs_to(Schedule))]
#[diesel(belongs_to(Tag))]
#[diesel(table_name = crate::db::schema::junction_tag)]
pub struct JunctionTag {
pub id: i32,
pub tag_id: i32,
@ -59,7 +60,7 @@ pub struct JunctionTag {
}
#[derive(Insertable)]
#[table_name = "junction_tag"]
#[diesel(table_name = crate::db::schema::junction_tag)]
pub struct NewJunctionTag {
pub tag_id: i32,
pub relay_id: Option<i32>,

View file

@ -13,37 +13,37 @@ use crate::db::tag::{create_junction_tag, create_tag};
use crate::db::{get_connection, schema};
pub fn get_schedule_tags(schedule: &Schedule) -> Vec<String> {
let connection = get_connection();
let mut connection = get_connection();
JunctionTag::belonging_to(schedule)
.inner_join(schema::tags::dsl::tags)
.select(schema::tags::tag)
.load::<String>(&connection)
.load::<String>(&mut connection)
.expect("Error loading tags")
}
pub fn get_schedules() -> Vec<Schedule> {
let connection = get_connection();
let mut connection = get_connection();
schedules
.load::<Schedule>(&connection)
.load::<Schedule>(&mut connection)
.expect("Error loading schedules")
}
pub fn get_schedule_by_uid(filter_uid: EmgauwaUid) -> Result<Schedule, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let result = schedules
.filter(schema::schedules::uid.eq(filter_uid))
.first::<Schedule>(&connection)
.first::<Schedule>(&mut connection)
.or(Err(DatabaseError::NotFound))?;
Ok(result)
}
pub fn get_schedules_by_tag(tag: &Tag) -> Vec<Schedule> {
let connection = get_connection();
let mut connection = get_connection();
JunctionTag::belonging_to(tag)
.inner_join(schedules)
.select(schema::schedules::all_columns)
.load::<Schedule>(&connection)
.load::<Schedule>(&mut connection)
.expect("Error loading tags")
}
@ -54,9 +54,9 @@ pub fn delete_schedule_by_uid(filter_uid: EmgauwaUid) -> Result<(), DatabaseErro
EmgauwaUid::Any(_) => Ok(filter_uid),
}?;
let connection = get_connection();
let mut connection = get_connection();
match diesel::delete(schedules.filter(schema::schedules::uid.eq(filter_uid)))
.execute(&connection)
.execute(&mut connection)
{
Ok(rows) => {
if rows != 0 {
@ -70,7 +70,7 @@ pub fn delete_schedule_by_uid(filter_uid: EmgauwaUid) -> Result<(), DatabaseErro
}
pub fn create_schedule(new_name: &str, new_periods: &Periods) -> Result<Schedule, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let new_schedule = NewSchedule {
uid: &EmgauwaUid::default(),
@ -80,12 +80,12 @@ pub fn create_schedule(new_name: &str, new_periods: &Periods) -> Result<Schedule
diesel::insert_into(schedules)
.values(&new_schedule)
.execute(&connection)
.execute(&mut connection)
.map_err(DatabaseError::InsertError)?;
let result = schedules
.find(sql("last_insert_rowid()"))
.get_result::<Schedule>(&connection)
.get_result::<Schedule>(&mut connection)
.or(Err(DatabaseError::InsertGetError))?;
Ok(result)
@ -96,7 +96,7 @@ pub fn update_schedule(
new_name: &str,
new_periods: &Periods,
) -> Result<Schedule, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let new_periods = match schedule.uid {
EmgauwaUid::Off | EmgauwaUid::On => schedule.periods.borrow(),
@ -108,21 +108,21 @@ pub fn update_schedule(
schema::schedules::name.eq(new_name),
schema::schedules::periods.eq(new_periods),
))
.execute(&connection)
.execute(&mut connection)
.map_err(DatabaseError::UpdateError)?;
get_schedule_by_uid(schedule.uid.clone())
}
pub fn set_schedule_tags(schedule: &Schedule, new_tags: &[String]) -> Result<(), DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
diesel::delete(junction_tag.filter(schema::junction_tag::schedule_id.eq(schedule.id)))
.execute(&connection)
.execute(&mut connection)
.or(Err(DatabaseError::DeleteError))?;
let mut database_tags: Vec<Tag> = tags
.filter(schema::tags::tag.eq_any(new_tags))
.load::<Tag>(&connection)
.load::<Tag>(&mut connection)
.expect("Error loading tags");
// create missing tags

View file

@ -8,29 +8,29 @@ use crate::db::schema::tags::dsl::tags;
use crate::db::{get_connection, schema};
pub fn create_tag(new_tag: &str) -> Result<Tag, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let new_tag = NewTag { tag: new_tag };
diesel::insert_into(tags)
.values(&new_tag)
.execute(&connection)
.execute(&mut connection)
.map_err(DatabaseError::InsertError)?;
let result = tags
.find(sql("last_insert_rowid()"))
.get_result::<Tag>(&connection)
.get_result::<Tag>(&mut connection)
.or(Err(DatabaseError::InsertGetError))?;
Ok(result)
}
pub fn get_tag(target_tag: &str) -> Result<Tag, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let result = tags
.filter(schema::tags::tag.eq(target_tag))
.first::<Tag>(&connection)
.first::<Tag>(&mut connection)
.or(Err(DatabaseError::NotFound))?;
Ok(result)
@ -41,7 +41,7 @@ pub fn create_junction_tag(
target_relay: Option<&Relay>,
target_schedule: Option<&Schedule>,
) -> Result<JunctionTag, DatabaseError> {
let connection = get_connection();
let mut connection = get_connection();
let new_junction_tag = NewJunctionTag {
relay_id: target_relay.map(|r| r.id),
@ -51,12 +51,12 @@ pub fn create_junction_tag(
diesel::insert_into(junction_tag)
.values(&new_junction_tag)
.execute(&connection)
.execute(&mut connection)
.map_err(DatabaseError::InsertError)?;
let result = junction_tag
.find(sql("last_insert_rowid()"))
.get_result::<JunctionTag>(&connection)
.get_result::<JunctionTag>(&mut connection)
.or(Err(DatabaseError::InsertGetError))?;
Ok(result)

View file

@ -28,7 +28,8 @@ pub async fn index() -> impl Responder {
}
#[get("/api/v1/schedules/tag/{tag}")]
pub async fn tagged(web::Path((tag,)): web::Path<(String,)>) -> impl Responder {
pub async fn tagged(path: web::Path<(String,)>) -> impl Responder {
let (tag,) = path.into_inner();
let tag_db = get_tag(&tag);
if tag_db.is_err() {
return HttpResponse::from(tag_db.unwrap_err());
@ -42,7 +43,8 @@ pub async fn tagged(web::Path((tag,)): web::Path<(String,)>) -> impl Responder {
}
#[get("/api/v1/schedules/{schedule_id}")]
pub async fn show(web::Path((schedule_uid,)): web::Path<(String,)>) -> impl Responder {
pub async fn show(path: web::Path<(String,)>) -> impl Responder {
let (schedule_uid,) = path.into_inner();
let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid));
match emgauwa_uid {
@ -108,9 +110,10 @@ pub async fn add_list(data: web::Json<Vec<RequestSchedule>>) -> impl Responder {
#[put("/api/v1/schedules/{schedule_id}")]
pub async fn update(
web::Path((schedule_uid,)): web::Path<(String,)>,
path: web::Path<(String,)>,
data: web::Json<RequestSchedule>,
) -> impl Responder {
let (schedule_uid,) = path.into_inner();
let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid));
if emgauwa_uid.is_err() {
return HttpResponse::from(emgauwa_uid.unwrap_err());
@ -138,7 +141,8 @@ pub async fn update(
}
#[delete("/api/v1/schedules/{schedule_id}")]
pub async fn delete(web::Path((schedule_uid,)): web::Path<(String,)>) -> impl Responder {
pub async fn delete(path: web::Path<(String,)>) -> impl Responder {
let (schedule_uid,) = path.into_inner();
let emgauwa_uid = EmgauwaUid::try_from(schedule_uid.as_str()).or(Err(HandlerError::BadUid));
match emgauwa_uid {

View file

@ -1,40 +1,45 @@
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate diesel_migrations;
extern crate core;
extern crate dotenv;
use actix_web::middleware::normalize::TrailingSlash;
use actix_web::middleware::TrailingSlash;
use actix_web::{middleware, web, App, HttpServer};
use env_logger::{Builder, Env};
use wiringpi::pin::Value::High;
use log::{trace, LevelFilter};
use simple_logger::SimpleLogger;
use std::fmt::format;
use std::str::FromStr;
mod db;
mod handlers;
mod return_models;
mod settings;
mod types;
mod utils;
#[actix_web::main]
async fn main() -> std::io::Result<()> {
db::run_migrations();
settings::init();
let settings = settings::get();
Builder::from_env(Env::default().default_filter_or("info")).init();
let log_level: LevelFilter = log::LevelFilter::from_str(&settings.logging.level)
.unwrap_or_else(|_| panic!("Error parsing log level."));
trace!("Log level set to {:?}", log_level);
let pi = wiringpi::setup();
SimpleLogger::new()
.with_level(log_level)
.init()
.unwrap_or_else(|_| panic!("Error initializing logger."));
//Use WiringPi pin 0 as output
let pin = pi.output_pin(0);
pin.digital_write(High);
db::init(&settings.database);
HttpServer::new(|| {
App::new()
.wrap(
middleware::DefaultHeaders::new()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers", "*")
.header("Access-Control-Allow-Methods", "*"),
.add(("Access-Control-Allow-Origin", "*"))
.add(("Access-Control-Allow-Headers", "*"))
.add(("Access-Control-Allow-Methods", "*")),
)
.wrap(middleware::Logger::default())
.wrap(middleware::NormalizePath::new(TrailingSlash::Trim))
@ -47,7 +52,7 @@ async fn main() -> std::io::Result<()> {
.service(handlers::v1::schedules::update)
.service(handlers::v1::schedules::delete)
})
.bind("127.0.0.1:5000")?
.bind(format!("{}:{}", settings.host, settings.port))?
.run()
.await
}

66
src/settings.rs Normal file
View file

@ -0,0 +1,66 @@
use config::Config;
use lazy_static::lazy_static;
use serde_derive::Deserialize;
use std::sync::RwLock;
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
#[allow(unused)]
pub struct Logging {
pub level: String,
pub file: String,
}
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
#[allow(unused)]
pub struct Settings {
pub database: String,
pub port: u16,
pub host: String,
pub logging: Logging,
}
impl Default for Settings {
fn default() -> Self {
Settings {
database: String::from("sqlite://emgauwa-core.sqlite"),
port: 5000,
host: String::from("127.0.0.1"),
logging: Logging::default(),
}
}
}
impl Default for Logging {
fn default() -> Self {
Logging {
level: String::from("info"),
file: String::from("stdout"),
}
}
}
lazy_static! {
static ref SETTINGS: RwLock<Settings> = RwLock::new(Settings::default());
}
pub fn init() {
let settings = Config::builder()
.add_source(config::File::with_name("emgauwa-core"))
.add_source(
config::Environment::with_prefix("EMGAUWA")
.prefix_separator("_")
.separator("__"),
)
.build()
.unwrap()
.try_deserialize::<Settings>()
.unwrap_or_else(|_| panic!("Error reading settings."));
*SETTINGS.write().unwrap() = settings;
}
pub fn get() -> Settings {
SETTINGS.read().unwrap().clone()
}

View file

@ -1,124 +1,12 @@
use std::convert::TryFrom;
use std::fmt::{Debug, Formatter};
use std::io::Write;
use std::str::FromStr;
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::sql_types::Binary;
use diesel::sqlite::Sqlite;
use diesel::{deserialize, serialize};
use serde::{Serialize, Serializer};
use uuid::Uuid;
pub mod emgauwa_uid;
#[derive(AsExpression, FromSqlRow, PartialEq, Clone)]
#[sql_type = "Binary"]
#[diesel(sql_type = Binary)]
pub enum EmgauwaUid {
Off,
On,
Any(Uuid),
}
impl EmgauwaUid {
const OFF_STR: &'static str = "off";
const ON_STR: &'static str = "on";
const OFF_U8: u8 = 0;
const ON_U8: u8 = 1;
const OFF_U128: u128 = 0;
const ON_U128: u128 = 1;
}
impl Default for EmgauwaUid {
fn default() -> Self {
EmgauwaUid::Any(Uuid::new_v4())
}
}
impl Debug for EmgauwaUid {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EmgauwaUid::Off => EmgauwaUid::OFF_STR.fmt(f),
EmgauwaUid::On => EmgauwaUid::ON_STR.fmt(f),
EmgauwaUid::Any(value) => value.fmt(f),
}
}
}
impl ToSql<Binary, Sqlite> for EmgauwaUid {
fn to_sql<W: Write>(&self, out: &mut Output<W, Sqlite>) -> serialize::Result {
match self {
EmgauwaUid::Off => out.write_all(&[EmgauwaUid::OFF_U8])?,
EmgauwaUid::On => out.write_all(&[EmgauwaUid::ON_U8])?,
EmgauwaUid::Any(value) => out.write_all(value.as_bytes())?,
}
Ok(IsNull::No)
}
}
impl FromSql<Binary, Sqlite> for EmgauwaUid {
fn from_sql(bytes: Option<&<Sqlite as Backend>::RawValue>) -> deserialize::Result<Self> {
match bytes {
None => Ok(EmgauwaUid::default()),
Some(value) => match value.read_blob() {
[EmgauwaUid::OFF_U8] => Ok(EmgauwaUid::Off),
[EmgauwaUid::ON_U8] => Ok(EmgauwaUid::On),
value_bytes => Ok(EmgauwaUid::Any(Uuid::from_slice(value_bytes).unwrap())),
},
}
}
}
impl Serialize for EmgauwaUid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
String::from(self).serialize(serializer)
}
}
impl From<Uuid> for EmgauwaUid {
fn from(uid: Uuid) -> EmgauwaUid {
match uid.as_u128() {
EmgauwaUid::OFF_U128 => EmgauwaUid::Off,
EmgauwaUid::ON_U128 => EmgauwaUid::On,
_ => EmgauwaUid::Any(uid),
}
}
}
impl TryFrom<&str> for EmgauwaUid {
type Error = uuid::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
EmgauwaUid::OFF_STR => Ok(EmgauwaUid::Off),
EmgauwaUid::ON_STR => Ok(EmgauwaUid::On),
any => match Uuid::from_str(any) {
Ok(uuid) => Ok(EmgauwaUid::Any(uuid)),
Err(err) => Err(err),
},
}
}
}
impl From<&EmgauwaUid> for Uuid {
fn from(emgauwa_uid: &EmgauwaUid) -> Uuid {
match emgauwa_uid {
EmgauwaUid::Off => uuid::Uuid::from_u128(EmgauwaUid::OFF_U128),
EmgauwaUid::On => uuid::Uuid::from_u128(EmgauwaUid::ON_U128),
EmgauwaUid::Any(value) => *value,
}
}
}
impl From<&EmgauwaUid> for String {
fn from(emgauwa_uid: &EmgauwaUid) -> String {
match emgauwa_uid {
EmgauwaUid::Off => String::from(EmgauwaUid::OFF_STR),
EmgauwaUid::On => String::from(EmgauwaUid::ON_STR),
EmgauwaUid::Any(value) => value.to_hyphenated().to_string(),
}
}
}

122
src/types/emgauwa_uid.rs Normal file
View file

@ -0,0 +1,122 @@
use std::convert::TryFrom;
use std::fmt::{Debug, Formatter};
use std::str::FromStr;
use crate::types::EmgauwaUid;
use diesel::backend::Backend;
use diesel::deserialize::FromSql;
use diesel::serialize::{IsNull, Output, ToSql};
use diesel::sql_types::Binary;
use diesel::{deserialize, serialize};
use serde::{Serialize, Serializer};
use uuid::Uuid;
impl EmgauwaUid {
const OFF_STR: &'static str = "off";
const ON_STR: &'static str = "on";
const OFF_U8: u8 = 0;
const ON_U8: u8 = 1;
const OFF_U128: u128 = 0;
const ON_U128: u128 = 1;
}
impl Default for EmgauwaUid {
fn default() -> Self {
EmgauwaUid::Any(Uuid::new_v4())
}
}
impl Debug for EmgauwaUid {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
EmgauwaUid::Off => EmgauwaUid::OFF_STR.fmt(f),
EmgauwaUid::On => EmgauwaUid::ON_STR.fmt(f),
EmgauwaUid::Any(value) => value.fmt(f),
}
}
}
impl<DB> ToSql<Binary, DB> for EmgauwaUid
where
DB: Backend,
[u8]: ToSql<Binary, DB>,
{
fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, DB>) -> serialize::Result {
match self {
EmgauwaUid::Off => [EmgauwaUid::OFF_U8].to_sql(out)?,
EmgauwaUid::On => [EmgauwaUid::ON_U8].to_sql(out)?,
EmgauwaUid::Any(value) => value.as_bytes().to_sql(out)?,
};
Ok(IsNull::No)
}
}
impl<DB> FromSql<Binary, DB> for EmgauwaUid
where
DB: Backend,
Vec<u8>: FromSql<Binary, DB>,
{
fn from_sql(bytes: DB::RawValue<'_>) -> deserialize::Result<Self> {
let blob: Vec<u8> = FromSql::<Binary, DB>::from_sql(bytes)?;
match blob.as_slice() {
[EmgauwaUid::OFF_U8] => Ok(EmgauwaUid::Off),
[EmgauwaUid::ON_U8] => Ok(EmgauwaUid::On),
value_bytes => Ok(EmgauwaUid::Any(Uuid::from_slice(value_bytes).unwrap())),
}
}
}
impl Serialize for EmgauwaUid {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
String::from(self).serialize(serializer)
}
}
impl From<Uuid> for EmgauwaUid {
fn from(uid: Uuid) -> EmgauwaUid {
match uid.as_u128() {
EmgauwaUid::OFF_U128 => EmgauwaUid::Off,
EmgauwaUid::ON_U128 => EmgauwaUid::On,
_ => EmgauwaUid::Any(uid),
}
}
}
impl TryFrom<&str> for EmgauwaUid {
type Error = uuid::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
EmgauwaUid::OFF_STR => Ok(EmgauwaUid::Off),
EmgauwaUid::ON_STR => Ok(EmgauwaUid::On),
any => match Uuid::from_str(any) {
Ok(uuid) => Ok(EmgauwaUid::Any(uuid)),
Err(err) => Err(err),
},
}
}
}
impl From<&EmgauwaUid> for Uuid {
fn from(emgauwa_uid: &EmgauwaUid) -> Uuid {
match emgauwa_uid {
EmgauwaUid::Off => uuid::Uuid::from_u128(EmgauwaUid::OFF_U128),
EmgauwaUid::On => uuid::Uuid::from_u128(EmgauwaUid::ON_U128),
EmgauwaUid::Any(value) => *value,
}
}
}
impl From<&EmgauwaUid> for String {
fn from(emgauwa_uid: &EmgauwaUid) -> String {
match emgauwa_uid {
EmgauwaUid::Off => String::from(EmgauwaUid::OFF_STR),
EmgauwaUid::On => String::from(EmgauwaUid::ON_STR),
EmgauwaUid::Any(value) => value.as_hyphenated().to_string(),
}
}
}