use super::{xorurl::SafeContentType, Safe};
use crate::{
xorurl::{SafeDataType, XorUrl, XorUrlEncoder},
Error, Result,
};
use log::debug;
use xor_name::XorName;
impl Safe {
pub async fn sequence_create(
&mut self,
data: &[u8],
name: Option<XorName>,
type_tag: u64,
private: bool,
) -> Result<XorUrl> {
let xorname = self
.safe_client
.store_sequence(data, name, type_tag, None, private)
.await?;
XorUrlEncoder::encode_sequence_data(
xorname,
type_tag,
SafeContentType::Raw,
self.xorurl_base,
private,
)
}
pub async fn sequence_get(&mut self, url: &str) -> Result<(u64, Vec<u8>)> {
debug!("Getting Public Sequence data from: {:?}", url);
let (xorurl_encoder, _) = self.parse_and_resolve_url(url).await?;
self.fetch_sequence(&xorurl_encoder).await
}
pub(crate) async fn fetch_sequence(
&mut self,
xorurl_encoder: &XorUrlEncoder,
) -> Result<(u64, Vec<u8>)> {
let is_private = xorurl_encoder.data_type() == SafeDataType::PrivateSequence;
let data = match xorurl_encoder.content_version() {
Some(version) => {
let data = self
.safe_client
.sequence_get_entry(
xorurl_encoder.xorname(),
xorurl_encoder.type_tag(),
version,
is_private,
)
.await?;
Ok((version, data))
}
None => {
self.safe_client
.sequence_get_last_entry(
xorurl_encoder.xorname(),
xorurl_encoder.type_tag(),
is_private,
)
.await
}
};
match data {
Ok((version, value)) => {
debug!("Sequence retrieved... v{}", &version);
Ok((version, value))
}
Err(Error::EmptyContent(_)) => Err(Error::EmptyContent(format!(
"Sequence found at \"{}\" was empty",
xorurl_encoder
))),
Err(Error::ContentNotFound(_)) => Err(Error::ContentNotFound(
"No Sequence found at this address".to_string(),
)),
other => other,
}
}
pub async fn append_to_sequence(&mut self, url: &str, data: &[u8]) -> Result<()> {
let xorurl_encoder = Safe::parse_url(url)?;
if xorurl_encoder.content_version().is_some() {
return Err(Error::InvalidInput(format!(
"The target URL cannot contain a version: {}",
url
)));
};
let (xorurl_encoder, _) = self.parse_and_resolve_url(url).await?;
let xorname = xorurl_encoder.xorname();
let type_tag = xorurl_encoder.type_tag();
let is_private = xorurl_encoder.data_type() == SafeDataType::PrivateSequence;
self.safe_client
.append_to_sequence(data, xorname, type_tag, is_private)
.await
}
}
#[cfg(test)]
mod tests {
use crate::api::app::test_helpers::new_safe_instance;
use anyhow::Result;
#[tokio::test]
async fn test_sequence_create() -> Result<()> {
let mut safe = new_safe_instance().await?;
let initial_data = b"initial data";
let xorurl = safe
.sequence_create(initial_data, None, 25_000, false)
.await?;
let xorurl_priv = safe
.sequence_create(initial_data, None, 25_000, true)
.await?;
let received_data = safe.sequence_get(&xorurl).await?;
let received_data_priv = safe.sequence_get(&xorurl_priv).await?;
assert_eq!(received_data, (0, initial_data.to_vec()));
assert_eq!(received_data_priv, (0, initial_data.to_vec()));
Ok(())
}
#[tokio::test]
async fn test_sequence_append() -> Result<()> {
let mut safe = new_safe_instance().await?;
let data_v0 = b"First in the sequence";
let data_v1 = b"Second in the sequence";
let xorurl = safe.sequence_create(data_v0, None, 25_000, false).await?;
safe.append_to_sequence(&xorurl, data_v1).await?;
let xorurl_priv = safe.sequence_create(data_v0, None, 25_000, true).await?;
safe.append_to_sequence(&xorurl_priv, data_v1).await?;
let received_data_v0 = safe.sequence_get(&format!("{}?v=0", xorurl)).await?;
let received_data_v1 = safe.sequence_get(&xorurl).await?;
assert_eq!(received_data_v0, (0, data_v0.to_vec()));
assert_eq!(received_data_v1, (1, data_v1.to_vec()));
let received_data_v0_priv = safe.sequence_get(&format!("{}?v=0", xorurl)).await?;
let received_data_v1_priv = safe.sequence_get(&xorurl).await?;
assert_eq!(received_data_v0_priv, (0, data_v0.to_vec()));
assert_eq!(received_data_v1_priv, (1, data_v1.to_vec()));
Ok(())
}
#[tokio::test]
async fn test_sequence_read_from_second_client() -> Result<()> {
let mut client1 = new_safe_instance().await?;
let data_v0 = b"First in the sequence";
let data_v1 = b"Second in the sequence";
let xorurl = client1
.sequence_create(data_v0, None, 25_000, false)
.await?;
client1.append_to_sequence(&xorurl, data_v1).await?;
let mut client2 = new_safe_instance().await?;
let received_data_v0 = client2.sequence_get(&format!("{}?v=0", xorurl)).await?;
let received_data_v1 = client2.sequence_get(&xorurl).await?;
assert_eq!(received_data_v0, (0, data_v0.to_vec()));
assert_eq!(received_data_v1, (1, data_v1.to_vec()));
Ok(())
}
#[tokio::test]
async fn test_sequence_append_concurrently_from_second_client() -> Result<()> {
let mut client1 = new_safe_instance().await?;
let mut client2 = new_safe_instance().await?;
let data_v0 = b"First from client1";
let data_v1 = b"First from client2";
let xorurl = client1
.sequence_create(data_v0, None, 25_000, false)
.await?;
client2.append_to_sequence(&xorurl, data_v1).await?;
let received_client1 = client1.sequence_get(&xorurl).await?;
let received_client2 = client2.sequence_get(&xorurl).await?;
assert_eq!(received_client1, (0, data_v0.to_vec()));
assert_eq!(received_client2, (1, data_v1.to_vec()));
let mut client3 = new_safe_instance().await?;
let received_v0 = client3.sequence_get(&format!("{}?v=0", xorurl)).await?;
assert_eq!((0, data_v0.to_vec()), received_v0);
let received_v1 = client3.sequence_get(&xorurl).await?;
assert_eq!((1, data_v1.to_vec()), received_v1);
Ok(())
}
}