Files
tubestation/toolkit/components/xulstore/src/lib.rs

220 lines
5.9 KiB
Rust

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
extern crate crossbeam_utils;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate lmdb;
#[macro_use]
extern crate log;
extern crate moz_task;
extern crate nserror;
extern crate nsstring;
extern crate rkv;
extern crate serde_json;
#[macro_use]
extern crate xpcom;
mod error;
mod ffi;
mod iter;
mod persist;
mod statics;
use crate::{
error::{XULStoreError, XULStoreResult},
iter::XULStoreIterator,
persist::persist,
statics::DATA_CACHE,
};
use nsstring::nsAString;
use std::collections::btree_map::Entry;
use std::fmt::Display;
const SEPARATOR: char = '\u{0009}';
pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
}
pub(crate) fn set_value(
doc: &nsAString,
id: &nsAString,
attr: &nsAString,
value: &nsAString,
) -> XULStoreResult<()> {
debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
// bug 319846 -- don't save really long attributes or values.
if id.len() > 512 || attr.len() > 512 {
return Err(XULStoreError::IdAttrNameTooLong);
}
let value = if value.len() > 4096 {
warn!("XULStore: truncating long attribute value");
String::from_utf16(&value[0..4096])?
} else {
String::from_utf16(value)?
};
let mut cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_mut() {
Some(data) => data,
None => return Ok(()),
};
data.entry(doc.to_string())
.or_default()
.entry(id.to_string())
.or_default()
.insert(attr.to_string(), value.clone());
persist(make_key(doc, id, attr), Some(value))?;
Ok(())
}
pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
debug!("XULStore has value: {} {} {}", doc, id, attr);
let cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_ref() {
Some(data) => data,
None => return Ok(false),
};
match data.get(&doc.to_string()) {
Some(ids) => match ids.get(&id.to_string()) {
Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
None => Ok(false),
},
None => Ok(false),
}
}
pub(crate) fn get_value(
doc: &nsAString,
id: &nsAString,
attr: &nsAString,
) -> XULStoreResult<String> {
debug!("XULStore get value {} {} {}", doc, id, attr);
let cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_ref() {
Some(data) => data,
None => return Ok(String::new()),
};
match data.get(&doc.to_string()) {
Some(ids) => match ids.get(&id.to_string()) {
Some(attrs) => match attrs.get(&attr.to_string()) {
Some(value) => Ok(value.clone()),
None => Ok(String::new()),
},
None => Ok(String::new()),
},
None => Ok(String::new()),
}
}
pub(crate) fn remove_value(
doc: &nsAString,
id: &nsAString,
attr: &nsAString,
) -> XULStoreResult<()> {
debug!("XULStore remove value {} {} {}", doc, id, attr);
let mut cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_mut() {
Some(data) => data,
None => return Ok(()),
};
let mut ids_empty = false;
if let Some(ids) = data.get_mut(&doc.to_string()) {
let mut attrs_empty = false;
if let Some(attrs) = ids.get_mut(&id.to_string()) {
attrs.remove(&attr.to_string());
if attrs.is_empty() {
attrs_empty = true;
}
}
if attrs_empty {
ids.remove(&id.to_string());
if ids.is_empty() {
ids_empty = true;
}
}
};
if ids_empty {
data.remove(&doc.to_string());
}
persist(make_key(doc, id, attr), None)?;
Ok(())
}
pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
debug!("XULStore remove document {}", doc);
let mut cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_mut() {
Some(data) => data,
None => return Ok(()),
};
if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
for (id, attrs) in entry.get() {
for attr in attrs.keys() {
persist(make_key(entry.key(), id, attr), None)?;
}
}
entry.remove_entry();
}
Ok(())
}
pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
debug!("XULStore get IDs for {}", doc);
let cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_ref() {
Some(data) => data,
None => return Ok(XULStoreIterator::new(vec![].into_iter())),
};
match data.get(&doc.to_string()) {
Some(ids) => {
let mut ids: Vec<String> = ids.keys().cloned().collect();
Ok(XULStoreIterator::new(ids.into_iter()))
}
None => Ok(XULStoreIterator::new(vec![].into_iter())),
}
}
pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
let cache_guard = DATA_CACHE.lock()?;
let data = match cache_guard.as_ref() {
Some(data) => data,
None => return Ok(XULStoreIterator::new(vec![].into_iter())),
};
match data.get(&doc.to_string()) {
Some(ids) => match ids.get(&id.to_string()) {
Some(attrs) => {
let mut attrs: Vec<String> = attrs.keys().cloned().collect();
Ok(XULStoreIterator::new(attrs.into_iter()))
}
None => Ok(XULStoreIterator::new(vec![].into_iter())),
},
None => Ok(XULStoreIterator::new(vec![].into_iter())),
}
}