use crate::conf::{ConfigDifference, KnotConfig};
use crate::error;
use crate::error::KnotError;
#[derive(Debug)]
pub struct ControlMessage {
command: Option<String>,
flags: Option<String>,
error: Option<String>,
section: Option<String>,
item: Option<String>,
id: Option<String>,
zone: Option<String>,
owner: Option<String>,
ttl: Option<String>,
ty: Option<String>,
data: Option<String>,
filter: Option<String>,
}
#[derive(Debug)]
pub struct ControlMessageRef<'a> {
command: Option<&'a str>,
flags: Option<&'a str>,
error: Option<&'a str>,
section: Option<&'a str>,
item: Option<&'a str>,
id: Option<&'a str>,
zone: Option<&'a str>,
owner: Option<&'a str>,
ttl: Option<&'a str>,
ty: Option<&'a str>,
data: Option<&'a str>,
filter: Option<&'a str>,
}
impl ControlMessage {
pub fn as_ref(&self) -> ControlMessageRef {
ControlMessageRef {
command: borrow_opt_string(&self.command),
flags: borrow_opt_string(&self.flags),
error: borrow_opt_string(&self.error),
section: borrow_opt_string(&self.section),
item: borrow_opt_string(&self.item),
id: borrow_opt_string(&self.id),
zone: borrow_opt_string(&self.zone),
owner: borrow_opt_string(&self.owner),
ttl: borrow_opt_string(&self.ttl),
ty: borrow_opt_string(&self.ty),
data: borrow_opt_string(&self.data),
filter: borrow_opt_string(&self.filter),
}
}
}
impl ControlMessage {
pub fn build<'a>() -> ControlMessageBuilder<'a> {
ControlMessageBuilder {
message: ControlMessageRef {
command: None,
flags: None,
error: None,
section: None,
item: None,
id: None,
zone: None,
owner: None,
ttl: None,
ty: None,
data: None,
filter: None,
},
}
}
pub fn command(&self) -> Option<&str> {
borrow_opt_string(&self.command)
}
pub fn flags(&self) -> Option<&str> {
borrow_opt_string(&self.flags)
}
pub fn error(&self) -> Option<&str> {
borrow_opt_string(&self.error)
}
pub fn section(&self) -> Option<&str> {
borrow_opt_string(&self.section)
}
pub fn item(&self) -> Option<&str> {
borrow_opt_string(&self.item)
}
pub fn id(&self) -> Option<&str> {
borrow_opt_string(&self.id)
}
pub fn zone(&self) -> Option<&str> {
borrow_opt_string(&self.zone)
}
pub fn owner(&self) -> Option<&str> {
borrow_opt_string(&self.owner)
}
pub fn ttl(&self) -> Option<&str> {
borrow_opt_string(&self.ttl)
}
pub fn ty(&self) -> Option<&str> {
borrow_opt_string(&self.ty)
}
pub fn data(&self) -> Option<&str> {
borrow_opt_string(&self.data)
}
pub fn filter(&self) -> Option<&str> {
borrow_opt_string(&self.filter)
}
pub fn ensure_success_ref(&self) -> error::Result<&Self> {
if let Some(error) = &self.error {
Err(KnotError::KnotErrorMsg { msg: error.clone() })
} else {
Ok(self)
}
}
pub fn ensure_success(self) -> error::Result<Self> {
if let Some(error) = self.error {
Err(KnotError::KnotErrorMsg { msg: error })
} else {
Ok(self)
}
}
}
impl ControlMessageRef<'_> {
pub fn to_owned(&self) -> ControlMessage {
ControlMessage {
command: own_opt_string(self.command),
flags: own_opt_string(self.flags),
error: own_opt_string(self.error),
section: own_opt_string(self.section),
item: own_opt_string(self.item),
id: own_opt_string(self.id),
zone: own_opt_string(self.zone),
owner: own_opt_string(self.owner),
ttl: own_opt_string(self.ttl),
ty: own_opt_string(self.ty),
data: own_opt_string(self.data),
filter: own_opt_string(self.filter),
}
}
}
impl ControlMessageRef<'_> {
pub fn command(&self) -> Option<&str> {
self.command
}
pub fn flags(&self) -> Option<&str> {
self.flags
}
pub fn error(&self) -> Option<&str> {
self.error
}
pub fn section(&self) -> Option<&str> {
self.section
}
pub fn item(&self) -> Option<&str> {
self.item
}
pub fn id(&self) -> Option<&str> {
self.id
}
pub fn zone(&self) -> Option<&str> {
self.zone
}
pub fn owner(&self) -> Option<&str> {
self.owner
}
pub fn ttl(&self) -> Option<&str> {
self.ttl
}
pub fn ty(&self) -> Option<&str> {
self.ty
}
pub fn data(&self) -> Option<&str> {
self.data
}
pub fn filter(&self) -> Option<&str> {
self.filter
}
}
fn borrow_opt_string(thing: &Option<String>) -> Option<&str> {
if let Some(thing) = thing {
Some(thing)
} else {
None
}
}
fn own_opt_string(thing: Option<&str>) -> Option<String> {
thing.map(|thing| thing.to_owned())
}
pub struct ControlMessageBuilder<'a> {
message: ControlMessageRef<'a>,
}
impl<'a> ControlMessageBuilder<'a> {
pub fn command(mut self, command: &'a str) -> Self {
self.message.command = Some(command);
self
}
pub fn flags(mut self, flags: &'a str) -> Self {
self.message.flags = Some(flags);
self
}
pub fn error(mut self, error: &'a str) -> Self {
self.message.error = Some(error);
self
}
pub fn section(mut self, section: &'a str) -> Self {
self.message.section = Some(section);
self
}
pub fn item(mut self, item: &'a str) -> Self {
self.message.item = Some(item);
self
}
pub fn id(mut self, id: &'a str) -> Self {
self.message.id = Some(id);
self
}
pub fn zone(mut self, zone: &'a str) -> Self {
self.message.zone = Some(zone);
self
}
pub fn owner(mut self, owner: &'a str) -> Self {
self.message.owner = Some(owner);
self
}
pub fn ttl(mut self, ttl: &'a str) -> Self {
self.message.ttl = Some(ttl);
self
}
pub fn ty(mut self, ty: &'a str) -> Self {
self.message.ty = Some(ty);
self
}
pub fn data(mut self, data: &'a str) -> Self {
self.message.data = Some(data);
self
}
pub fn filter(mut self, filter: &'a str) -> Self {
self.message.filter = Some(filter);
self
}
pub fn build(self) -> ControlMessageRef<'a> {
self.message
}
}
pub(crate) fn build_message(things: Vec<(u8, String)>) -> Result<ControlMessage, KnotError> {
let mut msg = ControlMessage {
command: None,
flags: None,
error: None,
section: None,
item: None,
id: None,
zone: None,
owner: None,
ttl: None,
ty: None,
data: None,
filter: None,
};
for (index, payload) in things {
match index {
0 => msg.command = Some(payload),
1 => msg.flags = Some(payload),
2 => msg.error = Some(payload),
3 => msg.section = Some(payload),
4 => msg.item = Some(payload),
5 => msg.id = Some(payload),
6 => msg.zone = Some(payload),
7 => msg.owner = Some(payload),
8 => msg.ttl = Some(payload),
9 => msg.ty = Some(payload),
10 => msg.data = Some(payload),
11 => msg.filter = Some(payload),
_ => return Err(KnotError::InvalidFieldIndex(index)),
}
}
Ok(msg)
}
pub trait KnotSync {
fn in_transaction(&self) -> bool;
fn conf_transaction<F, T, C>(&mut self, cb: C) -> Result<T, F>
where
C: FnOnce(&mut Self) -> Result<T, F>,
F: From<KnotError>;
fn zone_transaction<F, T, C>(&mut self, zone: &str, cb: C) -> Result<T, F>
where
C: FnOnce(&mut Self) -> Result<T, F>,
F: From<KnotError>;
fn conf_set(
&mut self,
section: &str,
id: Option<&str>,
item: Option<&str>,
value: Option<&str>,
) -> Result<(), KnotError>;
fn conf_unset(
&mut self,
section: &str,
id: Option<&str>,
item: Option<&str>,
value: Option<&str>,
) -> Result<(), KnotError>;
fn conf_get<'a>(
&'a mut self,
section: Option<&str>,
id: Option<&str>,
item: Option<&str>,
) -> Result<Vec<ControlMessage>, KnotError>;
fn retrieve_conf(&mut self) -> Result<KnotConfig, KnotError> {
let msgs: Vec<_> = if self.in_transaction() {
self.conf_get(None, None, None)?
} else {
self.conf_transaction::<KnotError, _, _>(|knot| knot.conf_get(None, None, None))?
};
let mut conf = KnotConfig::new();
for msg in msgs {
let msg = msg.as_ref();
match (msg.section, msg.id, msg.item, msg.data) {
(Some(section), None, Some(item), Some(data)) => {
if conf.section_has_id(section)? {
conf.create_id(section, data)?;
} else {
conf.set_without_id(section, item, msg.data)?;
}
}
(Some(section), Some(id), Some(item), Some(data)) => {
conf.add_with_id(section, id, item, data)?;
}
msg => {
panic!("Invalid Message from Knot: {:?}", msg);
}
}
}
Ok(conf)
}
fn apply_diff(&mut self, diff: &[ConfigDifference]) -> Result<(), KnotError> {
for msg in diff {
log::trace!("MSG: {:?}", msg);
match msg {
ConfigDifference::IdCreated { section, id, key } => {
self.conf_set(section, None, Some(key), Some(id))?;
}
ConfigDifference::IdDeleted { section, id, key } => {
self.conf_unset(section, None, Some(key), Some(id))?;
}
ConfigDifference::NonIdSet {
section,
key,
value,
} => {
self.conf_set(section, None, Some(key), Some(value))?;
}
ConfigDifference::IdSet {
section,
id,
key,
value,
} => {
self.conf_set(section, Some(id), Some(key), Some(value))?;
}
ConfigDifference::NonIdUnset {
section,
key,
value,
} => {
self.conf_unset(section, None, Some(key), *value)?;
}
ConfigDifference::IdUnset {
section,
id,
key,
value,
} => {
self.conf_unset(section, Some(id), Some(key), *value)?;
}
}
}
Ok(())
}
}