use std::path::PathBuf;
use std::str::FromStr;
use clap::ValueEnum;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::surface::error::CommandError;
use crate::surface::output::{
diagnostics_from_entry_report, display_path, display_paths, format_gen_link_report,
format_query_json, query_result_records,
};
use crate::{
CheckMode, EntryAddress, EntryAddressError, EntryAtom, EntryDirectoryReport,
EntryStructuralMatcher, GenLinkDirectoryReport, StructuralEdgeSettings, Tide, TideStatus,
TideWorkitem, UpstreamSettings, WitnessRecord,
};
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
pub enum StructuredOutputFormat {
Json,
#[default]
Human,
}
pub(crate) type QueryOutputFormat = StructuredOutputFormat;
pub(crate) type TideOutputFormat = StructuredOutputFormat;
pub(crate) type AnchorOutputFormat = StructuredOutputFormat;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize, ValueEnum)]
#[serde(rename_all = "kebab-case")]
pub enum TideStatusMode {
#[default]
Review,
Full,
All,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum AnchorRippleKind {
Added,
Changed,
Deleted,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnchorRippleRecord {
pub id: EntryAddress,
pub kind: AnchorRippleKind,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnchorStatusResult {
pub ok: bool,
pub initialized: bool,
pub anchor_path: String,
pub lake_path: String,
pub entry_count: usize,
pub ripples: Vec<AnchorRippleRecord>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnchorCheckResult {
pub ok: bool,
pub initialized: bool,
pub anchor_path: String,
pub lake_path: String,
pub entry_count: usize,
pub ripples: Vec<AnchorRippleRecord>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnchorUpdateResult {
pub ok: bool,
pub anchor_path: String,
pub lake_path: String,
pub entry_count: usize,
pub cleared_tide_resolutions: usize,
pub message: String,
}
impl TideStatusMode {
pub(crate) fn includes_workitems(self) -> bool {
matches!(self, Self::Full | Self::All)
}
pub(crate) fn includes_resolved(self) -> bool {
matches!(self, Self::All)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct CwdResult {
pub ok: bool,
pub changed: bool,
pub path: String,
pub message: String,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ConfigCommentResult {
pub ok: bool,
pub changed: bool,
pub config_path: String,
pub missing_comments: Vec<String>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UpstreamAddRequest {
pub domain: EntryAtom,
pub settings: UpstreamSettings,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct UpstreamCrystallizeRequest {
pub domains: Vec<EntryAtom>,
pub locked: bool,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryColumns {
pub(crate) columns: Vec<QueryColumn>,
}
impl QueryColumns {
pub fn new(columns: Vec<QueryColumn>) -> Self {
Self { columns }
}
pub fn columns(&self) -> &[QueryColumn] {
&self.columns
}
pub fn labels(&self) -> Vec<String> {
self.columns.iter().map(|column| column.label().to_owned()).collect()
}
pub(crate) fn structural_fields(&self) -> impl Iterator<Item = &str> {
self.columns.iter().filter_map(QueryColumn::structural_field)
}
pub fn default_output() -> Self {
Self { columns: vec![QueryColumn::Id, QueryColumn::Name] }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub enum QueryColumnSelection {
#[default]
Default,
Options,
Selected(QueryColumns),
}
impl FromStr for QueryColumns {
type Err = QueryColumnsParseError;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
if raw.trim().is_empty() {
return Err(QueryColumnsParseError::Empty);
}
let mut columns = Vec::new();
for raw_column in raw.split(',') {
let column = raw_column.trim();
if column.is_empty() {
return Err(QueryColumnsParseError::EmptyColumn);
}
columns.push(column.parse()?);
}
Ok(Self { columns })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum QueryColumn {
Id,
Name,
Path,
Desc,
Structural {
field: String,
},
}
impl FromStr for QueryColumn {
type Err = QueryColumnsParseError;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
match raw {
| "id" => Ok(Self::Id),
| "name" => Ok(Self::Name),
| "path" => Ok(Self::Path),
| "desc" => Ok(Self::Desc),
| column => Ok(Self::Structural { field: column.to_owned() }),
}
}
}
impl QueryColumn {
pub fn label(&self) -> &str {
match self {
| Self::Id => "id",
| Self::Name => "name",
| Self::Path => "path",
| Self::Desc => "desc",
| Self::Structural { field } => field,
}
}
pub fn structural_field(&self) -> Option<&str> {
match self {
| Self::Structural { field } => Some(field),
| Self::Id | Self::Name | Self::Path | Self::Desc => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum QueryValue {
Text(String),
Targets(Option<Vec<String>>),
}
impl QueryValue {
pub fn text(value: impl Into<String>) -> Self {
Self::Text(value.into())
}
pub fn targets(targets: Option<&[EntryAddress]>) -> Self {
Self::Targets(
targets.map(|targets| targets.iter().map(ToString::to_string).collect::<Vec<_>>()),
)
}
pub(crate) fn display(&self) -> String {
match self {
| Self::Text(value) => value.clone(),
| Self::Targets(Some(targets)) => targets.join(", "),
| Self::Targets(None) => String::new(),
}
}
}
impl From<String> for QueryValue {
fn from(value: String) -> Self {
Self::Text(value)
}
}
#[derive(Debug, Error)]
pub enum QueryColumnsParseError {
#[error("query columns must include at least one column")]
Empty,
#[error("query columns contain an empty column")]
EmptyColumn,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructuralFilter {
pub field: String,
pub targets: Vec<EntryAddress>,
}
impl FromStr for StructuralFilter {
type Err = StructuralFilterParseError;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
let Some((field, targets)) = raw.split_once('=') else {
return Err(StructuralFilterParseError::MissingEquals);
};
let field = field.trim();
if field.is_empty() {
return Err(StructuralFilterParseError::EmptyField);
}
let targets = parse_structural_filter_targets(targets)?;
Ok(Self { field: field.to_owned(), targets })
}
}
fn parse_structural_filter_targets(
raw: &str,
) -> Result<Vec<EntryAddress>, StructuralFilterParseError> {
let mut targets = Vec::new();
for raw_target in raw.split(',') {
let target = raw_target.trim();
if target.is_empty() {
return Err(StructuralFilterParseError::EmptyTarget);
}
targets.push(EntryAddress::new(target)?);
}
Ok(targets)
}
#[derive(Debug, Error)]
pub enum StructuralFilterParseError {
#[error("expected FIELD=ENTRY_ADDRESS[,ENTRY_ADDRESS]")]
MissingEquals,
#[error("link relation name must not be empty")]
EmptyField,
#[error("structural filter contains an empty target")]
EmptyTarget,
#[error(transparent)]
EntryAddress(#[from] EntryAddressError),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StructuralStateFilter {
pub field: String,
pub state: StructuralFieldState,
}
impl FromStr for StructuralStateFilter {
type Err = StructuralStateFilterParseError;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
let Some((field, state)) = raw.split_once('=') else {
return Err(StructuralStateFilterParseError::MissingEquals);
};
let field = field.trim();
if field.is_empty() {
return Err(StructuralStateFilterParseError::EmptyField);
}
Ok(Self { field: field.to_owned(), state: state.trim().parse()? })
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum StructuralFieldState {
Present,
Empty,
Missing,
}
impl FromStr for StructuralFieldState {
type Err = StructuralStateFilterParseError;
fn from_str(raw: &str) -> Result<Self, Self::Err> {
match raw {
| "present" => Ok(Self::Present),
| "empty" => Ok(Self::Empty),
| "missing" => Ok(Self::Missing),
| state => Err(StructuralStateFilterParseError::UnknownState(state.to_owned())),
}
}
}
impl From<StructuralFieldState> for EntryStructuralMatcher {
fn from(value: StructuralFieldState) -> Self {
match value {
| StructuralFieldState::Present => Self::Present,
| StructuralFieldState::Empty => Self::Empty,
| StructuralFieldState::Missing => Self::Missing,
}
}
}
#[derive(Debug, Error)]
pub enum StructuralStateFilterParseError {
#[error("expected FIELD=present, FIELD=empty, or FIELD=missing")]
MissingEquals,
#[error("link relation name must not be empty")]
EmptyField,
#[error("unknown structural link state `{0}`; expected present, empty, or missing")]
UnknownState(String),
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct QueryRequest {
pub terms: Vec<String>,
pub exact_terms: Vec<String>,
pub has: Vec<StructuralFilter>,
pub is: Vec<StructuralStateFilter>,
pub columns: QueryColumnSelection,
}
#[derive(Debug)]
pub enum QueryRun {
ColumnOptions(QueryColumns),
InvalidLake {
columns: QueryColumns,
report: Box<EntryDirectoryReport>,
},
Results(QueryResults),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct QueryResults {
pub(crate) columns: QueryColumns,
pub(crate) rows: Vec<Vec<QueryValue>>,
}
impl QueryResults {
pub fn new(columns: QueryColumns, rows: Vec<Vec<QueryValue>>) -> Self {
Self { columns, rows }
}
pub fn columns(&self) -> &QueryColumns {
&self.columns
}
pub fn rows(&self) -> &[Vec<QueryValue>] {
&self.rows
}
pub fn records(&self) -> Vec<IndexMap<String, QueryValue>> {
query_result_records(&self.columns, &self.rows)
}
pub fn to_json(&self) -> Result<String, CommandError> {
format_query_json(&self.columns, &self.rows)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct EntryPathsRequest {
pub id: EntryAddress,
pub selection: PathSelection,
pub absolute: bool,
}
impl EntryPathsRequest {
pub fn new(id: EntryAddress, selection: PathSelection, absolute: bool) -> Self {
Self { id, selection, absolute }
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct LakeInitRequest {
pub lake: Option<PathBuf>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LakeInitResult {
pub ok: bool,
pub config_path: String,
pub lake_path: String,
pub entry_count: usize,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructuralTarget {
pub field: String,
pub target: EntryAddress,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntryNewRequest {
pub id: EntryAddress,
pub name: Option<String>,
pub desc: String,
#[serde(default)]
pub structural: Vec<StructuralTarget>,
pub body: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntryFileResult {
pub ok: bool,
pub id: String,
pub path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LocalProtectionResult {
pub ok: bool,
pub dry_run: bool,
pub lake_path: String,
pub paths: Vec<String>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntryReadResult {
pub ok: bool,
pub id: String,
pub path: String,
pub name: String,
pub desc: String,
pub body: String,
pub source: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct EntryRenameResult {
pub ok: bool,
pub old_id: String,
pub new_id: String,
pub updated_paths: Vec<String>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct QueryResponse {
pub ok: bool,
pub columns: Vec<String>,
pub records: Vec<IndexMap<String, QueryValue>>,
pub diagnostics: Vec<DiagnosticRecord>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RgRequest {
#[serde(default)]
pub with_generated_footer: bool,
pub args: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RgResult {
pub ok: bool,
pub exit_code: u8,
pub stdout: String,
pub stderr: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct WitnessSpanResult {
pub start_line: usize,
pub start_column: usize,
pub end_line: usize,
pub end_column: usize,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum WitnessRecordResult {
Compact(CompactWitnessRecordResult),
Verbose(VerboseWitnessRecordResult),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CompactWitnessRecordResult {
pub entry: String,
pub location: String,
pub body: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct VerboseWitnessRecordResult {
pub entry: String,
pub path: String,
pub region: WitnessSpanResult,
pub body: String,
}
impl WitnessRecordResult {
pub(crate) fn from_record(record: &WitnessRecord, verbose_json: bool) -> Self {
if verbose_json {
return Self::Verbose(VerboseWitnessRecordResult {
entry: record.entry.to_string(),
path: display_path(&record.path),
region: WitnessSpanResult::from(record.region),
body: record.body.clone(),
});
}
Self::Compact(CompactWitnessRecordResult {
entry: record.entry.to_string(),
location: format_witness_location(record),
body: record.body.clone(),
})
}
}
fn format_witness_location(record: &WitnessRecord) -> String {
format!(
"{}:{}:{}-{}:{}",
display_path(&record.path),
record.region.start_line,
record.region.start_column,
record.region.end_line,
record.region.end_column
)
}
impl From<crate::witness::WitnessSpan> for WitnessSpanResult {
fn from(value: crate::witness::WitnessSpan) -> Self {
Self {
start_line: value.start_line,
start_column: value.start_column,
end_line: value.end_line,
end_column: value.end_column,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct WitnessResult {
pub ok: bool,
pub id: String,
pub records: Vec<WitnessRecordResult>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactListResult {
pub ok: bool,
pub id: String,
pub artifacts: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactAddRequest {
pub id: EntryAddress,
pub source: PathBuf,
pub artifact_path: Option<PathBuf>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactRenameRequest {
pub id: EntryAddress,
pub old_path: PathBuf,
pub new_path: PathBuf,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactRemoveRequest {
pub id: EntryAddress,
pub artifact_path: PathBuf,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct ArtifactChangeResult {
pub ok: bool,
pub id: String,
pub artifact_path: String,
pub path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmRecord {
pub id: String,
pub name: String,
pub enabled: bool,
pub kind: String,
pub manifest_path: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmListResult {
pub ok: bool,
pub charms: Vec<CharmRecord>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmShowResult {
pub ok: bool,
pub id: String,
pub name: String,
pub enabled: bool,
pub kind: String,
pub manifest_path: String,
pub artifact_root: String,
pub spell_cache_path: String,
pub spell_command: Vec<String>,
pub has_setup: bool,
pub has_check: bool,
pub has_build: bool,
pub hooks: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmEnablementResult {
pub ok: bool,
pub changed: bool,
pub id: String,
pub config_path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmProcessResult {
pub ok: bool,
pub id: String,
pub phase: String,
pub skipped: bool,
pub exit_code: Option<i32>,
pub stdout: String,
pub stderr: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct CharmCleanResult {
pub ok: bool,
pub removed: bool,
pub id: String,
pub path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SpellRecord {
pub id: String,
pub name: String,
pub kind: String,
pub spell_cache_path: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SpellListResult {
pub ok: bool,
pub spells: Vec<SpellRecord>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SkillWrapperRecord {
pub entry_id: String,
pub name: String,
pub wrapper_path: String,
pub full_path: String,
pub target_path: String,
pub status: String,
pub changed: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SkillWrapperResult {
pub ok: bool,
pub records: Vec<SkillWrapperRecord>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct MovePathResult {
pub ok: bool,
pub moved: bool,
pub old_path: String,
pub new_path: String,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct DiagnosticRecord {
pub severity: String,
pub path: Option<String>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct LakeCheckResult {
pub ok: bool,
pub root: String,
pub has_errors: bool,
pub diagnostics: Vec<DiagnosticRecord>,
}
impl LakeCheckResult {
pub(crate) fn from_report(report: &EntryDirectoryReport) -> Self {
let has_errors = report.has_errors();
Self {
ok: !has_errors,
root: display_path(report.root()),
has_errors,
diagnostics: diagnostics_from_entry_report(report),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct RenderResult {
pub ok: bool,
pub dry: bool,
pub root: String,
pub entry_count: usize,
pub changed_paths: Vec<String>,
pub diagnostics: Vec<DiagnosticRecord>,
pub message: String,
}
impl RenderResult {
pub(crate) fn from_report(report: &GenLinkDirectoryReport, dry: bool) -> Self {
Self::from_report_with_extra_changed_paths(report, dry, &[])
}
pub(crate) fn from_report_with_extra_changed_paths(
report: &GenLinkDirectoryReport, dry: bool, extra_changed_paths: &[PathBuf],
) -> Self {
let mut changed_path_bufs = report.changed_paths().to_vec();
changed_path_bufs.extend_from_slice(extra_changed_paths);
changed_path_bufs.sort();
changed_path_bufs.dedup();
let changed_paths = display_paths(&changed_path_bufs);
Self {
ok: true,
dry,
root: display_path(report.root()),
entry_count: report.entry_count(),
changed_paths,
diagnostics: Vec::new(),
message: format_gen_link_report(
report.root(),
report.entry_count(),
report.changed_entry_count(),
&changed_path_bufs,
),
}
}
pub(crate) fn blocked(report: &EntryDirectoryReport) -> Self {
Self {
ok: false,
dry: false,
root: display_path(report.root()),
entry_count: report.entries().len(),
changed_paths: Vec::new(),
diagnostics: diagnostics_from_entry_report(report),
message: format!("render blocked by check errors in {}", report.root().display()),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct MistStatusResult {
pub ok: bool,
pub manifest_present: bool,
pub mist: String,
pub spec_path: String,
pub reservoir_path: String,
pub projection_path: String,
pub editable: bool,
pub entry_count: usize,
pub changed_entries: Vec<String>,
pub stale_entries: Vec<String>,
pub missing_entries: Vec<String>,
pub staged_paths: Vec<String>,
pub message: String,
}
impl MistStatusResult {
pub fn has_ripples_or_blockers(&self) -> bool {
!self.changed_entries.is_empty()
|| !self.stale_entries.is_empty()
|| !self.missing_entries.is_empty()
|| !self.staged_paths.is_empty()
|| !self.manifest_present
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct MistIntakeResult {
pub ok: bool,
pub mist: String,
pub reservoir_path: String,
pub projection_path: String,
pub updated_entries: Vec<String>,
pub changed_paths: Vec<String>,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructuralEdgeStatus {
pub render: bool,
pub ripple_lake: bool,
pub ripple_anchor: bool,
}
impl StructuralEdgeStatus {
pub(crate) fn from_settings(settings: &StructuralEdgeSettings) -> Self {
Self {
render: settings.render,
ripple_lake: settings.ripple.lake,
ripple_anchor: settings.ripple.anchor,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StructuralFieldStatus {
pub field: String,
pub entry: String,
pub to: StructuralEdgeStatus,
pub from: StructuralEdgeStatus,
pub clique: StructuralEdgeStatus,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusCheckPolicy {
pub mode: CheckMode,
pub render: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusTide {
pub clear: bool,
pub open_workitems: usize,
pub open_waves: usize,
pub review_entries: usize,
}
impl StatusTide {
pub(crate) fn from_tide(tide: &Tide) -> Self {
let open_statuses = tide.open_statuses().collect::<Vec<_>>();
let open_waves = open_statuses
.iter()
.map(|status| &status.workitem.ripple)
.collect::<std::collections::BTreeSet<_>>()
.len();
Self {
clear: open_statuses.is_empty(),
open_workitems: open_statuses.len(),
open_waves,
review_entries: tide.review_entries().len(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct StatusResult {
pub ok: bool,
pub config_path: String,
pub lake_path: String,
pub entry_count: usize,
pub check_policy: StatusCheckPolicy,
pub structural_fields: Vec<StructuralFieldStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tide: Option<StatusTide>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mist: Option<MistStatusResult>,
pub check: LakeCheckResult,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct TideSelectionRequest {
#[serde(default)]
pub neighbors: Vec<EntryAddress>,
#[serde(default)]
pub workitems: Vec<TideWorkitem>,
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct TideResolveRequest {
#[serde(default)]
pub infer: bool,
#[serde(default)]
pub neighbors: Vec<EntryAddress>,
#[serde(default)]
pub workitems: Vec<TideWorkitem>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct TideChangeResult {
pub ok: bool,
pub count: usize,
pub message: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct TideStatusResult {
pub ok: bool,
pub review_entries: Vec<EntryAddress>,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub statuses: Vec<TideStatus>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PathSelection {
pub(crate) entry: bool,
pub(crate) artifact: bool,
}
impl PathSelection {
pub fn all() -> Self {
Self { entry: true, artifact: true }
}
pub fn new(entry: bool, artifact: bool) -> Self {
Self { entry, artifact }
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize)]
pub struct PathRecord {
pub kind: &'static str,
pub path: String,
}
impl PathRecord {
pub(crate) fn new(kind: &'static str, path: PathBuf) -> Self {
Self { kind, path: path.display().to_string() }
}
}