Split out core types into sprinklers_core
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
174d69fbcc
commit
3b96f2019d
@ -1,5 +1,6 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"sprinklers_core",
|
||||
"sprinklers_rs"
|
||||
]
|
16
sprinklers_core/Cargo.toml
Normal file
16
sprinklers_core/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "sprinklers_core"
|
||||
version = "0.1.0"
|
||||
authors = ["Alex Mikhalev <alexmikhalevalex@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.15" }
|
||||
serde = { version = "1.0.116", features = ["derive"] }
|
||||
im = "15.0.0"
|
||||
tracing = { version = "0.1.19" }
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json = "1.0.57"
|
4
sprinklers_core/src/lib.rs
Normal file
4
sprinklers_core/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod model;
|
||||
pub mod schedule;
|
||||
pub mod serde;
|
||||
pub mod section_interface;
|
30
sprinklers_core/src/model/program.rs
Normal file
30
sprinklers_core/src/model/program.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use super::section::SectionId;
|
||||
use crate::schedule::Schedule;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProgramItem {
|
||||
pub section_id: SectionId,
|
||||
#[serde(with = "crate::serde::duration_secs")]
|
||||
pub duration: Duration,
|
||||
}
|
||||
|
||||
pub type ProgramSequence = Vec<ProgramItem>;
|
||||
|
||||
pub type ProgramId = u32;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Program {
|
||||
pub id: ProgramId,
|
||||
pub name: String,
|
||||
pub sequence: ProgramSequence,
|
||||
pub enabled: bool,
|
||||
pub schedule: Schedule,
|
||||
}
|
||||
|
||||
pub type ProgramRef = Arc<Program>;
|
||||
|
||||
pub type Programs = im::OrdMap<ProgramId, ProgramRef>;
|
@ -6,7 +6,6 @@
|
||||
//! describes a logical section and how it maps to a physical one.
|
||||
|
||||
use crate::section_interface::SecId;
|
||||
use rusqlite::{Error as SqlError, Row as SqlRow, ToSql};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
@ -23,21 +22,6 @@ pub struct Section {
|
||||
pub interface_id: SecId,
|
||||
}
|
||||
|
||||
impl Section {
|
||||
pub fn from_sql<'a>(row: &SqlRow<'a>) -> Result<Section, SqlError> {
|
||||
Ok(Section {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
interface_id: row.get(2)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn as_sql(&self) -> Vec<&dyn ToSql> {
|
||||
vec![&self.id, &self.name, &self.interface_id]
|
||||
}
|
||||
}
|
||||
|
||||
pub type SectionRef = Arc<Section>;
|
||||
|
||||
pub type Sections = im::OrdMap<SectionId, SectionRef>;
|
@ -1,6 +1,6 @@
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
pub mod duration {
|
||||
pub mod duration_secs {
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
@ -7,6 +7,7 @@ edition = "2018"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
sprinklers_core = { path = "../sprinklers_core" }
|
||||
rusqlite = "0.24.0"
|
||||
color-eyre = "0.5.1"
|
||||
eyre = "0.6.0"
|
||||
|
@ -1,14 +1,16 @@
|
||||
mod migration;
|
||||
mod migrations;
|
||||
mod program;
|
||||
mod section;
|
||||
|
||||
pub use migration::*;
|
||||
pub use migrations::create_migrations;
|
||||
|
||||
pub use rusqlite::Connection as DbConn;
|
||||
|
||||
use eyre::Result;
|
||||
use sprinklers_core::model::{Programs, Sections};
|
||||
|
||||
use crate::model::{Program, Programs, Section, Sections};
|
||||
use eyre::Result;
|
||||
use rusqlite::NO_PARAMS;
|
||||
|
||||
pub fn setup_db() -> Result<DbConn> {
|
||||
@ -26,7 +28,7 @@ pub fn query_sections(conn: &DbConn) -> Result<Sections> {
|
||||
"SELECT s.id, s.name, s.interface_id \
|
||||
FROM sections AS s;",
|
||||
)?;
|
||||
let rows = statement.query_map(NO_PARAMS, Section::from_sql)?;
|
||||
let rows = statement.query_map(NO_PARAMS, section::from_sql)?;
|
||||
let mut sections = Sections::new();
|
||||
for row in rows {
|
||||
let section = row?;
|
||||
@ -43,7 +45,7 @@ pub fn query_programs(conn: &DbConn) -> Result<Programs> {
|
||||
INNER JOIN program_sequences AS ps
|
||||
ON ps.program_id = p.id;",
|
||||
)?;
|
||||
let rows = statement.query_map(NO_PARAMS, Program::from_sql)?;
|
||||
let rows = statement.query_map(NO_PARAMS, program::from_sql)?;
|
||||
let mut programs = Programs::new();
|
||||
for row in rows {
|
||||
let program = row?;
|
||||
|
46
sprinklers_rs/src/database/program.rs
Normal file
46
sprinklers_rs/src/database/program.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use sprinklers_core::{
|
||||
model::{Program, ProgramSequence},
|
||||
schedule::Schedule,
|
||||
};
|
||||
|
||||
use rusqlite::{
|
||||
types::{FromSql, FromSqlError, FromSqlResult, ValueRef},
|
||||
Result as SqlResult, Row as SqlRow,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
struct SqlJson<T>(T);
|
||||
|
||||
impl<T> SqlJson<T> {
|
||||
fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromSql for SqlJson<T>
|
||||
where
|
||||
for<'de> T: Deserialize<'de>,
|
||||
{
|
||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
if let ValueRef::Text(text) = value {
|
||||
let deser_value: T =
|
||||
serde_json::from_slice(text).map_err(|err| FromSqlError::Other(Box::new(err)))?;
|
||||
Ok(SqlJson(deser_value))
|
||||
} else {
|
||||
Err(FromSqlError::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SqlProgramSequence = SqlJson<ProgramSequence>;
|
||||
type SqlSchedule = SqlJson<Schedule>;
|
||||
|
||||
pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Program> {
|
||||
Ok(Program {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
sequence: row.get::<_, SqlProgramSequence>(2)?.into_inner(),
|
||||
enabled: row.get(3)?,
|
||||
schedule: row.get::<_, SqlSchedule>(4)?.into_inner(),
|
||||
})
|
||||
}
|
16
sprinklers_rs/src/database/section.rs
Normal file
16
sprinklers_rs/src/database/section.rs
Normal file
@ -0,0 +1,16 @@
|
||||
use sprinklers_core::model::Section;
|
||||
|
||||
use rusqlite::{Error as SqlError, Row as SqlRow, ToSql};
|
||||
|
||||
pub fn from_sql<'a>(row: &SqlRow<'a>) -> Result<Section, SqlError> {
|
||||
Ok(Section {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
interface_id: row.get(2)?,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn as_sql(section: &Section) -> Vec<&dyn ToSql> {
|
||||
vec![§ion.id, §ion.name, §ion.interface_id]
|
||||
}
|
@ -2,24 +2,22 @@
|
||||
#![warn(clippy::print_stdout)]
|
||||
|
||||
mod database;
|
||||
mod model;
|
||||
mod mqtt;
|
||||
mod option_future;
|
||||
mod program_runner;
|
||||
mod schedule;
|
||||
mod section_interface;
|
||||
mod section_runner;
|
||||
mod section_runner_json;
|
||||
mod serde;
|
||||
#[cfg(test)]
|
||||
mod trace_listeners;
|
||||
mod update_listener;
|
||||
|
||||
use sprinklers_core::section_interface::MockSectionInterface;
|
||||
use update_listener::UpdateListener;
|
||||
|
||||
use eyre::Result;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, info};
|
||||
use tracing_subscriber::EnvFilter;
|
||||
use update_listener::UpdateListener;
|
||||
|
||||
#[actix_rt::main]
|
||||
async fn main() -> Result<()> {
|
||||
@ -40,7 +38,7 @@ async fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
// TODO: Section interface which actual does something. Preferrably selectable somehow
|
||||
let section_interface: Arc<_> = section_interface::MockSectionInterface::new(6).into();
|
||||
let section_interface: Arc<_> = MockSectionInterface::new(6).into();
|
||||
let mut section_runner = section_runner::SectionRunner::new(section_interface);
|
||||
let mut program_runner = program_runner::ProgramRunner::new(section_runner.clone());
|
||||
|
||||
|
@ -1,78 +0,0 @@
|
||||
use super::section::SectionId;
|
||||
use crate::schedule::Schedule;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ProgramItem {
|
||||
pub section_id: SectionId,
|
||||
#[serde(with = "crate::serde::duration")]
|
||||
pub duration: Duration,
|
||||
}
|
||||
|
||||
pub type ProgramSequence = Vec<ProgramItem>;
|
||||
|
||||
pub type ProgramId = u32;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Program {
|
||||
pub id: ProgramId,
|
||||
pub name: String,
|
||||
pub sequence: ProgramSequence,
|
||||
pub enabled: bool,
|
||||
pub schedule: Schedule,
|
||||
}
|
||||
|
||||
mod sql {
|
||||
use super::{Program, ProgramSequence};
|
||||
use crate::schedule::Schedule;
|
||||
use rusqlite::{
|
||||
types::{FromSql, FromSqlError, FromSqlResult, ValueRef},
|
||||
Result as SqlResult, Row as SqlRow,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
|
||||
struct SqlJson<T>(T);
|
||||
|
||||
impl<T> SqlJson<T> {
|
||||
fn into_inner(self) -> T {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromSql for SqlJson<T>
|
||||
where
|
||||
for<'de> T: Deserialize<'de>,
|
||||
{
|
||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||
if let ValueRef::Text(text) = value {
|
||||
let deser_value: T = serde_json::from_slice(text)
|
||||
.map_err(|err| FromSqlError::Other(Box::new(err)))?;
|
||||
Ok(SqlJson(deser_value))
|
||||
} else {
|
||||
Err(FromSqlError::InvalidType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SqlProgramSequence = SqlJson<ProgramSequence>;
|
||||
type SqlSchedule = SqlJson<Schedule>;
|
||||
|
||||
impl Program {
|
||||
pub fn from_sql<'a>(row: &SqlRow<'a>) -> SqlResult<Self> {
|
||||
Ok(Self {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
sequence: row.get::<_, SqlProgramSequence>(2)?.into_inner(),
|
||||
enabled: row.get(3)?,
|
||||
schedule: row.get::<_, SqlSchedule>(4)?.into_inner(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ProgramRef = Arc<Program>;
|
||||
|
||||
pub type Programs = im::OrdMap<ProgramId, ProgramRef>;
|
@ -6,11 +6,9 @@ mod topics;
|
||||
pub use request::RequestContext;
|
||||
|
||||
use self::topics::Topics;
|
||||
use crate::{
|
||||
model::{Program, ProgramId, Programs, Section, SectionId, Sections},
|
||||
section_runner::SecRunnerState,
|
||||
section_runner_json::SecRunnerStateJson,
|
||||
};
|
||||
use crate::{section_runner::SecRunnerState, section_runner_json::SecRunnerStateJson};
|
||||
use sprinklers_core::model::{Program, ProgramId, Programs, Section, SectionId, Sections};
|
||||
|
||||
use actix::{Actor, Addr};
|
||||
use eyre::WrapErr;
|
||||
use rumqttc::{LastWill, MqttOptions, QoS};
|
||||
|
@ -1,4 +1,5 @@
|
||||
use crate::{model::Sections, program_runner::ProgramRunner, section_runner::SectionRunner};
|
||||
use crate::{program_runner::ProgramRunner, section_runner::SectionRunner};
|
||||
use sprinklers_core::model::Sections;
|
||||
|
||||
use futures_util::ready;
|
||||
use futures_util::FutureExt;
|
||||
|
@ -1,5 +1,7 @@
|
||||
use super::*;
|
||||
use crate::{model::ProgramId, program_runner::ProgramRunnerError};
|
||||
use crate::program_runner::ProgramRunnerError;
|
||||
use sprinklers_core::model::ProgramId;
|
||||
|
||||
use eyre::WrapErr;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
|
@ -1,12 +1,15 @@
|
||||
use super::*;
|
||||
use crate::{model::SectionRef, section_runner::SectionRunHandle};
|
||||
use crate::section_runner::SectionRunHandle;
|
||||
use sprinklers_core::model::{self, SectionRef};
|
||||
use sprinklers_core::serde::duration_secs;
|
||||
|
||||
use eyre::WrapErr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct SectionId(pub crate::model::SectionId);
|
||||
pub struct SectionId(pub model::SectionId);
|
||||
|
||||
impl SectionId {
|
||||
fn get_section(self, sections: &Sections) -> Result<SectionRef, RequestError> {
|
||||
@ -20,7 +23,7 @@ impl SectionId {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RunSectionRequest {
|
||||
pub section_id: SectionId,
|
||||
#[serde(with = "crate::serde::duration")]
|
||||
#[serde(with = "duration_secs")]
|
||||
pub duration: Duration,
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::model::{ProgramId, SectionId};
|
||||
use sprinklers_core::model::{ProgramId, SectionId};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Topics<T>
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::model::{ProgramId, ProgramRef, Programs, Sections};
|
||||
use crate::section_runner::{
|
||||
Error as SectionRunnerError, SectionEvent, SectionEventRecv, SectionRunHandle, SectionRunner,
|
||||
};
|
||||
use sprinklers_core::model::{ProgramId, ProgramRef, Programs, Sections};
|
||||
|
||||
use actix::{
|
||||
Actor, ActorContext, ActorFuture, ActorStream, Addr, AsyncContext, Handler, Message,
|
||||
MessageResult, SpawnHandle, StreamHandler, WrapFuture,
|
||||
@ -467,12 +468,13 @@ impl ProgramRunner {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::section_interface::{MockSectionInterface, SectionInterface};
|
||||
use crate::{
|
||||
use crate::trace_listeners::{EventListener, Filters};
|
||||
use sprinklers_core::{
|
||||
model::{Program, ProgramItem, Section},
|
||||
schedule::{every_day, DateTimeBound, Schedule},
|
||||
trace_listeners::{EventListener, Filters},
|
||||
section_interface::{MockSectionInterface, SectionInterface},
|
||||
};
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use im::ordmap;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::model::{SectionId, SectionRef};
|
||||
use crate::section_interface::SectionInterface;
|
||||
use sprinklers_core::model::{SectionId, SectionRef};
|
||||
use sprinklers_core::section_interface::SectionInterface;
|
||||
|
||||
use actix::{
|
||||
Actor, ActorContext, Addr, AsyncContext, Handler, Message, MessageResult, SpawnHandle,
|
||||
};
|
||||
@ -618,11 +619,12 @@ impl SectionRunner {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::section_interface::MockSectionInterface;
|
||||
use crate::{
|
||||
use crate::trace_listeners::{EventListener, Filters};
|
||||
use sprinklers_core::{
|
||||
model::{Section, Sections},
|
||||
trace_listeners::{EventListener, Filters},
|
||||
section_interface::MockSectionInterface,
|
||||
};
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use im::ordmap;
|
||||
use tracing_subscriber::prelude::*;
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
model::SectionId,
|
||||
section_runner::{SecRun, SecRunState, SecRunnerState},
|
||||
};
|
||||
use crate::section_runner::{SecRun, SecRunState, SecRunnerState};
|
||||
use sprinklers_core::model::SectionId;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::Serialize;
|
||||
use std::time::SystemTime;
|
||||
|
Loading…
x
Reference in New Issue
Block a user