use super::*;
impl<State> Lockbox<State> {
pub fn define_form(
&mut self,
alias: &str,
name: &str,
fields: Vec<FormFieldDefinition>,
) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
self.define_form_with_description(alias, name, "", fields)
}
pub fn define_form_with_description(
&mut self,
alias: &str,
name: &str,
description: &str,
fields: Vec<FormFieldDefinition>,
) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
let alias = FormDefinition::validated_alias(alias)?;
match self.resolve_form_definition(&alias) {
Ok(existing) => {
self.revise_form_definition(&existing.type_id, name, description, fields)
}
Err(Error::NotFound(_)) => {
let type_id = FormTypeId::new_random()?;
self.define_form_with_type_id_and_description(
type_id,
&alias,
name,
description,
fields,
)
}
Err(err) => Err(err),
}
}
pub fn define_form_with_type_id(
&mut self,
type_id: FormTypeId,
alias: &str,
name: &str,
fields: Vec<FormFieldDefinition>,
) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
self.define_form_with_type_id_and_description(type_id, alias, name, "", fields)
}
pub fn define_form_with_type_id_and_description(
&mut self,
type_id: FormTypeId,
alias: &str,
name: &str,
description: &str,
fields: Vec<FormFieldDefinition>,
) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
let alias = FormDefinition::validated_alias(alias)?;
if self.latest_form_definition_by_type(&type_id)?.is_some() {
return self.revise_form_definition(&type_id, name, description, fields);
}
let definition = FormDefinition::validated(type_id, alias, 1, name, description, fields)?;
self.ensure_forms_loaded()?;
self.forms.put_definition(definition.clone())?;
Ok(definition)
}
pub fn revise_form_definition(
&mut self,
type_id: &FormTypeId,
name: &str,
description: &str,
fields: Vec<FormFieldDefinition>,
) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
let previous = self
.latest_form_definition_by_type(type_id)?
.ok_or_else(|| Error::NotFound(format!("form type {type_id}")))?;
for previous_field in &previous.fields {
if previous_field.kind.is_secret()
&& fields
.iter()
.any(|field| field.id == previous_field.id && !field.kind.is_secret())
{
return Err(Error::InvalidOperation(format!(
"form field {} is secret; remove it in one definition revision before recreating it as non-secret",
previous_field.id
)));
}
}
let definition = FormDefinition::validated(
type_id.clone(),
previous.alias.clone(),
previous.revision + 1,
name,
description,
fields,
)?;
self.forms.put_definition(definition.clone())?;
Ok(definition)
}
pub fn resolve_form_definition(&self, reference: &str) -> Result<FormDefinition> {
self.ensure_forms_loaded()?;
if let Ok(type_id) = FormTypeId::new(reference) {
return self
.latest_form_definition_by_type(&type_id)?
.ok_or_else(|| Error::NotFound(format!("form type {type_id}")));
}
let alias = FormDefinition::validated_alias(reference)?;
let matches = self
.latest_form_definitions()?
.into_iter()
.filter(|definition| definition.alias == alias)
.collect::<Vec<_>>();
match matches.as_slice() {
[definition] => Ok(definition.clone()),
[] => Err(Error::NotFound(format!("form alias {alias}"))),
_ => Err(Error::InvalidOperation(format!(
"form alias {alias} is ambiguous; use a form type id"
))),
}
}
pub fn list_form_definitions(&self) -> Result<Vec<FormDefinition>> {
self.latest_form_definitions()
}
pub fn list_form_definition_revisions(
&self,
type_id: &FormTypeId,
) -> Result<Vec<FormDefinition>> {
self.ensure_forms_loaded()?;
let mut definitions = self
.forms
.definitions
.borrow()
.as_ref()
.ok_or(Error::CorruptRecord)?
.values()
.filter(|definition| definition.type_id == *type_id)
.cloned()
.collect::<Vec<_>>();
definitions.sort_by_key(|definition| definition.revision);
Ok(definitions)
}
pub fn import_form_definition(&mut self, definition: FormDefinition) -> Result<FormDefinition>
where
State: crate::WritableLockboxState,
{
if definition.revision == 0 {
return Err(Error::InvalidInput(
"form definition revision must be greater than zero".to_string(),
));
}
let definition = FormDefinition::validated(
definition.type_id,
definition.alias,
definition.revision,
&definition.name,
&definition.description,
definition.fields,
)?;
self.ensure_forms_loaded()?;
let key = definition_key(&definition.type_id, definition.revision);
let mut definitions = self.staged.forms.definitions.borrow_mut();
let definitions = definitions.as_mut().ok_or(Error::CorruptRecord)?;
if let Some(existing) = definitions.get(&key) {
if existing == &definition {
return Ok(existing.clone());
}
return Err(Error::InvalidOperation(format!(
"form definition {} revision {} already exists with different content",
definition.type_id, definition.revision
)));
}
definitions.insert(key.clone(), definition.clone());
self.staged.forms.tree.dirty_keys.insert(key);
self.staged.forms.dirty = true;
Ok(definition)
}
}