pub mod adapter;
mod add;
mod artifact;
mod delete;
mod delete_referrers;
mod doc_adapter;
mod doc_target;
pub mod engine;
mod get;
pub(crate) mod matching;
pub mod path;
mod refs;
mod remove;
mod request;
pub mod rules;
pub mod runtime;
mod set;
mod tags;
mod target_doc;
mod target_doc_remove;
mod tick;
mod toml_adapter;
mod toml_target;
mod value_codec;
use self::add::{AddFieldRequest, add_to_field};
pub use self::artifact::ArtifactType;
pub use self::get::get_field;
pub use self::remove::remove_from_field;
pub use self::request::{EditFieldRequest, OwnedEditAction};
use self::set::apply_set_field;
pub(crate) use self::set::set_field_direct;
pub use self::tick::tick_item;
use self::{engine as edit_engine, path::FieldPath, rules as edit_rules};
use crate::diagnostic::{Diagnostic, DiagnosticCode, DiagnosticResult};
use crate::ui;
pub use delete::{delete_clause, delete_work_item};
pub use matching::{MatchOptions, MatchOptionsOwned};
use self::request::resolve_owned_value;
pub(crate) use value_codec::parse_requirement_binding as parse_conformance_requirement;
pub(super) fn serialize_edit_doc<T: serde::Serialize>(
value: &T,
id: &str,
) -> DiagnosticResult<serde_json::Value> {
serde_json::to_value(value).map_err(|err| {
Diagnostic::new(
DiagnosticCode::E0903UnexpectedError,
format!("Failed to serialize editable document: {err}"),
id,
)
})
}
pub(super) fn deserialize_edit_doc<T: serde::de::DeserializeOwned>(
value: serde_json::Value,
id: &str,
) -> DiagnosticResult<T> {
serde_json::from_value(value).map_err(|err| {
Diagnostic::new(
DiagnosticCode::E0820InvalidFieldValue,
format!("Edited document failed schema conversion: {err}"),
id,
)
})
}
pub(super) fn unexpected_edit_state(id: &str, message: impl Into<String>) -> Diagnostic {
Diagnostic::new(DiagnosticCode::E0903UnexpectedError, message, id)
}
pub(super) struct PlannedMutation {
pub(super) artifact: ArtifactType,
pub(super) field_path: FieldPath,
pub(super) target: edit_engine::ResolvedTarget,
}
pub(super) fn plan_mutation_target(
id: &str,
field: &str,
verb: edit_rules::Verb,
) -> DiagnosticResult<PlannedMutation> {
let plan = edit_engine::plan_mutation_request(id, field, verb)?;
let field_path = plan.field_path.ok_or_else(|| {
Diagnostic::new(
DiagnosticCode::E0801MissingRequiredArg,
"Field path required",
id,
)
})?;
let target = plan
.target
.ok_or_else(|| unexpected_edit_state(id, "mutation planning should produce target"))?;
Ok(PlannedMutation {
artifact: plan.artifact,
field_path,
target,
})
}
pub fn edit_field(request: EditFieldRequest<'_>) -> DiagnosticResult<Vec<Diagnostic>> {
let EditFieldRequest {
config,
id,
path,
action,
op,
} = request;
match action {
OwnedEditAction::Set { value, stdin } => {
let value = resolve_owned_value(value.as_ref(), *stdin)?;
let plan = plan_mutation_target(id, path, edit_rules::Verb::Set)?;
apply_set_field(
config,
id,
&plan.target,
plan.artifact,
value.as_str(),
op,
true,
)?;
if !op.is_preview() {
ui::field_set(id, &plan.target.display_path(), value.as_str());
}
Ok(vec![])
}
OwnedEditAction::Add { value, stdin } => {
let value = resolve_owned_value(value.as_ref(), *stdin)?;
add_to_field(AddFieldRequest {
config,
id,
field: path,
value: value.as_str(),
op,
})
}
OwnedEditAction::Remove { match_opts } => {
remove_from_field(config, id, path, &match_opts.as_match_options(), op)
}
OwnedEditAction::Tick { match_opts, status } => tick_item(
config,
id,
path,
&match_opts.as_match_options(),
*status,
op,
),
}
}
fn reject_match_flags_for_indexed_target(
id: &str,
target: &edit_engine::ResolvedTarget,
opts: &MatchOptions,
) -> DiagnosticResult<()> {
let pattern_provided = opts.pattern.is_some_and(|pattern| !pattern.is_empty());
let edit_engine::ResolvedTarget::IndexedItem { .. } = target else {
return Ok(());
};
if pattern_provided || opts.regex || opts.all {
return Err(Diagnostic::new(
DiagnosticCode::E0818PathIndexConflict,
"Cannot combine an indexed path with a value, --regex, or --all",
id,
));
}
Ok(())
}