use crate::state::ChannelId;
use helix_core::effect::{BatchUpdateSpec, Row, SqlValue, StorageOp, UpsertSpec};
fn schedule_field<'a>(data: &'a serde_json::Value, keys: &[&str]) -> Option<&'a serde_json::Value> {
let schedule = data.get("schedule").unwrap_or(data);
keys.iter()
.find_map(|key| schedule.get(*key))
.or_else(|| keys.iter().find_map(|key| data.get(*key)))
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct ScheduleFact {
pub(crate) channel_id: ChannelId,
pub(crate) schedule_id: String,
pub(crate) owner_user_id: String,
message: String,
message_preview: String,
send_at: i64,
status: String,
pub(crate) revision: u64,
has_schedule_post: bool,
}
impl ScheduleFact {
pub(crate) fn from_created_ws(
data: &serde_json::Value,
revision_fallback: Option<u64>,
) -> Option<Self> {
let channel_id = schedule_field(data, &["channelId", "channel_id"])
.and_then(serde_json::Value::as_str)
.and_then(ChannelId::from_str)?;
let revision = schedule_field(data, &["revision"])
.and_then(serde_json::Value::as_u64)
.or(revision_fallback)
.filter(|revision| *revision > 0)?;
Some(Self {
channel_id,
schedule_id: schedule_field(data, &["scheduleId", "schedule_id"])
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string(),
owner_user_id: schedule_field(
data,
&[
"scheduleOwnerUserId",
"schedule_owner_user_id",
"ownerUserId",
"owner_user_id",
"userId",
"user_id",
],
)
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string(),
message: schedule_field(data, &["message"])
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string(),
message_preview: schedule_field(data, &["messagePreview", "message_preview"])
.and_then(serde_json::Value::as_str)
.map(|value| value.chars().take(96).collect())
.unwrap_or_default(),
send_at: schedule_field(
data,
&[
"sendAt",
"send_at",
"schedulePostAt",
"schedule_post_at",
"scheduledAt",
"scheduled_at",
],
)
.and_then(serde_json::Value::as_i64)
.unwrap_or(0),
status: schedule_field(data, &["status"])
.and_then(serde_json::Value::as_str)
.filter(|status| !status.is_empty())
.unwrap_or("scheduled")
.to_string(),
revision,
has_schedule_post: true,
})
}
pub(crate) fn from_canceled_ws(
data: &serde_json::Value,
revision_fallback: Option<u64>,
) -> Option<Self> {
let channel_id = schedule_field(data, &["channelId", "channel_id"])
.and_then(serde_json::Value::as_str)
.and_then(ChannelId::from_str)?;
let revision = schedule_field(data, &["revision"])
.and_then(serde_json::Value::as_u64)
.or(revision_fallback)
.filter(|revision| *revision > 0)?;
Some(Self {
channel_id,
schedule_id: String::new(),
owner_user_id: schedule_field(
data,
&[
"scheduleOwnerUserId",
"schedule_owner_user_id",
"ownerUserId",
"owner_user_id",
"userId",
"user_id",
],
)
.and_then(serde_json::Value::as_str)
.unwrap_or_default()
.to_string(),
message: String::new(),
message_preview: String::new(),
send_at: 0,
status: "canceled".to_string(),
revision,
has_schedule_post: false,
})
}
pub(crate) fn storage_ops(self) -> Vec<StorageOp> {
let (schedule_id_for_schedule, schedule_id_for_channel) = duplicate_text(self.schedule_id);
let (owner_for_schedule, owner_for_channel) = duplicate_text(self.owner_user_id);
let (message_for_schedule, message_for_channel) = duplicate_text(self.message);
let (preview_for_schedule, preview_for_channel) = duplicate_text(self.message_preview);
let (status_for_schedule, status_for_channel) = duplicate_text(self.status);
let schedule_row: Row = vec![
(
"channel_id".to_string(),
SqlValue::Text(self.channel_id.as_str().to_string()),
),
(
"schedule_id".to_string(),
SqlValue::Text(schedule_id_for_schedule),
),
(
"owner_user_id".to_string(),
SqlValue::Text(owner_for_schedule),
),
("message".to_string(), SqlValue::Text(message_for_schedule)),
(
"message_preview".to_string(),
SqlValue::Text(preview_for_schedule),
),
("send_at".to_string(), SqlValue::Integer(self.send_at)),
("status".to_string(), SqlValue::Text(status_for_schedule)),
(
"revision".to_string(),
SqlValue::Integer(self.revision.min(i64::MAX as u64) as i64),
),
];
let channel_patch: Row = vec![
(
"has_schedule_post".to_string(),
SqlValue::Integer(i64::from(self.has_schedule_post)),
),
(
"schedule_id".to_string(),
SqlValue::Text(schedule_id_for_channel),
),
(
"schedule_owner_user_id".to_string(),
SqlValue::Text(owner_for_channel),
),
(
"schedule_message".to_string(),
SqlValue::Text(message_for_channel),
),
(
"schedule_message_preview".to_string(),
SqlValue::Text(preview_for_channel),
),
(
"schedule_send_at".to_string(),
SqlValue::Integer(self.send_at),
),
(
"schedule_status".to_string(),
SqlValue::Text(status_for_channel),
),
(
"schedule_revision".to_string(),
SqlValue::Integer(self.revision.min(i64::MAX as u64) as i64),
),
];
vec![
StorageOp::BatchUpsert(UpsertSpec {
version_column: None,
update_guard: None,
table: "channel_schedule",
rows: vec![schedule_row],
conflict_key: Some("channel_id"),
exclude_from_update: Vec::new(),
}),
StorageOp::BatchUpdate(BatchUpdateSpec {
table: "channel",
key_col: "id",
key_vals: vec![SqlValue::Text(self.channel_id.as_str().to_string())],
patch: channel_patch,
}),
]
}
}
fn duplicate_text(value: String) -> (String, String) {
(value.clone(), value)
}