Hastic standalone https://hastic.io
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

177 lines
5.0 KiB

3 years ago
use crate::utils::get_random_str;
use rusqlite::{params, Connection, Row};
3 years ago
3 years ago
use serde::{Deserialize, Serialize};
3 years ago
3 years ago
use std::sync::{Arc, Mutex};
2 years ago
use super::data_service::DataService;
pub const ID_LENGTH: usize = 20;
3 years ago
pub type SegmentId = String;
2 years ago
3 years ago
// TODO: make logic with this enum shorter
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq)]
3 years ago
pub enum SegmentType {
3 years ago
Detection = 0,
3 years ago
Label = 1,
AntiLabel = 2,
3 years ago
}
impl SegmentType {
fn from(u: u64) -> SegmentType {
if u == 1 {
SegmentType::Label
3 years ago
} else if u == 2 {
SegmentType::AntiLabel
3 years ago
} else {
SegmentType::Detection
}
}
3 years ago
fn to_integer(&self) -> u64 {
if *self == SegmentType::Label {
1
3 years ago
} else if *self == SegmentType::AntiLabel {
3 years ago
2
3 years ago
} else {
0
3 years ago
}
}
3 years ago
}
#[derive(Debug, Serialize, Deserialize)]
3 years ago
pub struct Segment {
3 years ago
pub id: Option<SegmentId>,
3 years ago
pub from: u64,
pub to: u64,
3 years ago
pub segment_type: SegmentType,
}
impl Segment {
fn from(row: &Row) -> anyhow::Result<Segment, rusqlite::Error> {
Ok(Segment {
id: row.get(0)?,
from: row.get(1)?,
to: row.get(2)?,
segment_type: SegmentType::from(row.get(3)?),
})
}
3 years ago
}
2 years ago
// TODO: get DataService
#[derive(Clone)]
3 years ago
pub struct SegmentsService {
3 years ago
connection: Arc<Mutex<Connection>>,
3 years ago
}
3 years ago
impl SegmentsService {
2 years ago
pub fn new(ds: &DataService) -> anyhow::Result<SegmentsService> {
3 years ago
// TODO: add unilytic_unit id as a new field
2 years ago
let conn = ds.segments_connection.clone();
conn.lock().unwrap().execute(
3 years ago
"CREATE TABLE IF NOT EXISTS segment (
3 years ago
id TEXT PRIMARY KEY,
start INTEGER NOT NULL,
end INTEGER NOT NULL,
segment_type INTEGER NOT NULL
3 years ago
)",
[],
)?;
3 years ago
Ok(SegmentsService {
2 years ago
connection: conn
3 years ago
})
3 years ago
}
3 years ago
// returns id of created segment
3 years ago
pub fn insert_segment(&self, segment: &Segment) -> anyhow::Result<Segment> {
let id: SegmentId = get_random_str(ID_LENGTH);
3 years ago
// merging
let sgms = self.get_segments_intersected(segment.from, segment.to)?;
let mut min = segment.from;
let mut max = segment.to;
let mut ids_to_delete = Vec::<SegmentId>::new();
for s in sgms {
min = min.min(s.from);
max = max.max(s.to);
ids_to_delete.push(s.id.unwrap());
}
3 years ago
self.delete_segments(&ids_to_delete)?;
3 years ago
self.connection.lock().unwrap().execute(
3 years ago
"INSERT INTO segment (id, start, end, segment_type) VALUES (?1, ?2, ?3, ?4)",
3 years ago
params![id, min, max, segment.segment_type.to_integer()],
3 years ago
)?;
Ok(Segment {
id: Some(id),
from: min,
to: max,
3 years ago
segment_type: segment.segment_type,
})
3 years ago
}
pub fn get_segments_inside(&self, from: u64, to: u64) -> anyhow::Result<Vec<Segment>> {
3 years ago
let conn = self.connection.lock().unwrap();
3 years ago
let mut stmt = conn.prepare(
"SELECT id, start, end, segment_type FROM segment WHERE ?1 < start AND end < ?2",
)?;
3 years ago
3 years ago
let res = stmt
3 years ago
.query_map(params![from, to], Segment::from)?
3 years ago
.map(|e| e.unwrap())
.collect();
3 years ago
Ok(res)
}
pub fn get_segments_intersected(&self, from: u64, to: u64) -> anyhow::Result<Vec<Segment>> {
let conn = self.connection.lock().unwrap();
let mut stmt = conn.prepare(
3 years ago
"SELECT id, start, end, segment_type
FROM segment
3 years ago
WHERE (start <= ?1 and ?1 <= end) OR
3 years ago
(start <= ?2 AND ?2 <= end) OR
3 years ago
(?1 <= start AND start <= ?2) OR
3 years ago
(?1 <= end AND end <= ?2) ",
)?;
let res = stmt
3 years ago
.query_map(params![from, to], Segment::from)?
.map(|e| e.unwrap())
.collect();
Ok(res)
}
pub fn delete_segments_in_range(&self, from: u64, to: u64) -> anyhow::Result<usize> {
let conn = self.connection.lock().unwrap();
let res = conn.execute(
"DELETE FROM segment where ?1 <= start AND end <= ?2",
params![from, to],
)?;
Ok(res)
}
pub fn delete_segments(&self, ids: &Vec<SegmentId>) -> anyhow::Result<usize> {
if ids.len() == 0 {
return Ok(0);
}
3 years ago
// TODO: here could be sql injection if you substitute id with string :)
let conn = self.connection.lock().unwrap();
let ids_comma = ids
.iter()
.map(|id| "\"".to_string() + id + "\"")
.collect::<Vec<String>>()
.join(",");
3 years ago
let query_str = format!("DELETE FROM segment WHERE id in ({})", ids_comma);
let mut stmt = conn.prepare(&query_str)?;
let res = stmt.execute([])?;
Ok(res)
}
3 years ago
}