blob: 411335a59e88ee0465f639f18ed1baf7fb7dfec3 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use fuchsia_async as fasync;
use anyhow::{format_err, Error};
use fidl::endpoints::{RequestStream, ServerEnd};
use fidl_fuchsia_stash::{
FlushError, StoreAccessorMarker, StoreAccessorRequest, StoreAccessorRequestStream,
};
use futures::lock::Mutex;
use futures::{TryFutureExt, TryStreamExt};
use std::sync::Arc;
use tracing::warn;
use crate::accessor;
use crate::store;
/// Instance represents a single instance of the stash service, handling requests from a single
/// client.
#[derive(Clone)]
pub struct Instance {
pub client_name: Option<String>,
pub enable_bytes: bool,
pub store_manager: Arc<Mutex<store::StoreManager>>,
}
impl Instance {
/// identify must be called once at initial connection, to establish which namespace in the
/// store to use. Right now this is honor-system based for preventing clients from accessing
/// each other's data, but once component monikers are implemented they will take the place of
/// this function call.
pub fn identify(&mut self, name: String) -> Result<(), Error> {
if let Some(name) = self.client_name.as_ref() {
return Err(format_err!(format!("client attempted to identify twice: {}", name)));
}
self.client_name = Some(name);
Ok(())
}
/// creates a new accessor for interacting with the store.
pub fn create_accessor(
&mut self,
read_only: bool,
server_end: ServerEnd<StoreAccessorMarker>,
) -> Result<(), Error> {
let mut acc = accessor::Accessor::new(
self.store_manager.clone(),
self.enable_bytes,
read_only,
self.client_name.clone().ok_or(format_err!("identify has not been called"))?,
);
let server_chan = fasync::Channel::from_channel(server_end.into_channel())?;
fasync::Task::spawn(
async move {
let mut stream = StoreAccessorRequestStream::from_channel(server_chan);
while let Some(req) = stream.try_next().await? {
match req {
StoreAccessorRequest::GetValue { key, responder } => {
let mut res = acc.get_value(&key).await?;
responder.send(res.as_mut())?;
}
StoreAccessorRequest::SetValue { key, val, .. } => {
acc.set_value(key, val).await?
}
StoreAccessorRequest::DeleteValue { key, .. } => {
acc.delete_value(key).await?
}
StoreAccessorRequest::ListPrefix { prefix, it, .. } => {
acc.list_prefix(prefix, it).await
}
StoreAccessorRequest::GetPrefix { prefix, it, .. } => {
acc.get_prefix(prefix, it).await?
}
StoreAccessorRequest::DeletePrefix { prefix, .. } => {
acc.delete_prefix(prefix).await?
}
StoreAccessorRequest::Commit { .. } => acc.commit().await?,
StoreAccessorRequest::Flush { responder } => {
if read_only {
responder.send(&mut Err(FlushError::ReadOnly))?;
} else {
responder.send(&mut acc.flush().await)?;
}
}
}
}
Ok(())
}
.unwrap_or_else(|err: anyhow::Error| {
// TODO(fxbug.dev/62386) - This is currently set to warning so that error logs
// aren't produced when a component in test is torn down. This should
// distinguish between channel closed errors and actual stash failures and
// set the appropriate log level.
warn!(?err, "error running accessor interface");
}),
)
.detach();
Ok(())
}
}