cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

runtime.rs (121958B)


      1 #![allow(dead_code)]
      2 
      3 use std::process::ExitCode;
      4 
      5 use radroots_events::farm::RadrootsFarm;
      6 use radroots_events::ids::RadrootsListingAddress;
      7 use radroots_events::kinds::KIND_LISTING;
      8 use radroots_events::listing::RadrootsListingLocation;
      9 use radroots_events::order::RadrootsOrderEconomics;
     10 use radroots_events::profile::RadrootsProfile;
     11 use radroots_nostr_accounts::prelude::RadrootsNostrAccountRecord;
     12 use serde::Serialize;
     13 
     14 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
     15 pub enum CommandDisposition {
     16     Success,
     17     NotFound,
     18     ValidationFailed,
     19     Unconfigured,
     20     ExternalUnavailable,
     21     Unsupported,
     22     InternalError,
     23 }
     24 
     25 impl CommandDisposition {
     26     pub fn exit_code(self) -> ExitCode {
     27         match self {
     28             Self::Success => ExitCode::SUCCESS,
     29             Self::NotFound => ExitCode::from(4),
     30             Self::ValidationFailed => ExitCode::from(10),
     31             Self::Unconfigured => ExitCode::from(3),
     32             Self::ExternalUnavailable => ExitCode::from(4),
     33             Self::Unsupported => ExitCode::from(5),
     34             Self::InternalError => ExitCode::from(1),
     35         }
     36     }
     37 }
     38 
     39 #[derive(Debug, Clone, Serialize)]
     40 pub struct ConfigShowView {
     41     pub source: String,
     42     pub output: OutputRuntimeView,
     43     pub interaction: InteractionRuntimeView,
     44     pub config_files: ConfigFilesRuntimeView,
     45     pub paths: PathsRuntimeView,
     46     pub migration: MigrationRuntimeView,
     47     pub logging: LoggingRuntimeView,
     48     pub account: AccountRuntimeView,
     49     pub signer: SignerRuntimeView,
     50     pub publish: PublishRuntimeView,
     51     pub relay: RelayRuntimeView,
     52     pub local: LocalRuntimeView,
     53     pub myc: MycRuntimeView,
     54     pub write_plane: WritePlaneRuntimeView,
     55     pub workflow: WorkflowRuntimeView,
     56     pub hyf_provider: HyfProviderRuntimeView,
     57     pub hyf: HyfRuntimeView,
     58     pub rpc: RpcRuntimeView,
     59     pub capability_bindings: Vec<CapabilityBindingRuntimeView>,
     60     pub resolved_providers: Vec<ResolvedProviderRuntimeView>,
     61 }
     62 
     63 #[derive(Debug, Clone, Serialize)]
     64 pub struct RuntimeActionView {
     65     pub action: String,
     66     pub runtime_id: String,
     67     pub instance_id: String,
     68     pub instance_source: String,
     69     pub runtime_group: String,
     70     pub state: String,
     71     pub source: String,
     72     pub detail: String,
     73     pub mutates_bindings: bool,
     74     #[serde(skip_serializing_if = "Option::is_none")]
     75     pub next_step: Option<String>,
     76 }
     77 
     78 #[derive(Debug, Clone, Serialize)]
     79 pub struct RuntimeManagedConfigView {
     80     pub runtime_id: String,
     81     pub instance_id: String,
     82     pub instance_source: String,
     83     pub runtime_group: String,
     84     pub state: String,
     85     pub source: String,
     86     pub detail: String,
     87     #[serde(skip_serializing_if = "Option::is_none")]
     88     pub config_format: Option<String>,
     89     #[serde(skip_serializing_if = "Option::is_none")]
     90     pub config_path: Option<String>,
     91     pub config_present: bool,
     92     #[serde(skip_serializing_if = "Option::is_none")]
     93     pub requires_bootstrap_secret: Option<bool>,
     94     #[serde(skip_serializing_if = "Option::is_none")]
     95     pub requires_config_bootstrap: Option<bool>,
     96     #[serde(skip_serializing_if = "Option::is_none")]
     97     pub requires_signer_provider: Option<bool>,
     98 }
     99 
    100 #[derive(Debug, Clone, Serialize)]
    101 pub struct RuntimeLogsView {
    102     pub runtime_id: String,
    103     pub instance_id: String,
    104     pub instance_source: String,
    105     pub runtime_group: String,
    106     pub state: String,
    107     pub source: String,
    108     pub detail: String,
    109     #[serde(skip_serializing_if = "Option::is_none")]
    110     pub stdout_log_path: Option<String>,
    111     #[serde(skip_serializing_if = "Option::is_none")]
    112     pub stderr_log_path: Option<String>,
    113     pub stdout_log_present: bool,
    114     pub stderr_log_present: bool,
    115 }
    116 
    117 #[derive(Debug, Clone, Serialize)]
    118 pub struct RuntimeStatusView {
    119     pub runtime_id: String,
    120     pub instance_id: String,
    121     pub instance_source: String,
    122     pub runtime_group: String,
    123     pub management_posture: String,
    124     pub state: String,
    125     pub source: String,
    126     pub detail: String,
    127     #[serde(skip_serializing_if = "Option::is_none")]
    128     pub management_mode: Option<String>,
    129     #[serde(skip_serializing_if = "Option::is_none")]
    130     pub service_manager_integration: Option<bool>,
    131     #[serde(skip_serializing_if = "Option::is_none")]
    132     pub uses_absolute_binary_paths: Option<bool>,
    133     #[serde(skip_serializing_if = "Option::is_none")]
    134     pub preferred_cli_binding: Option<bool>,
    135     pub install_state: String,
    136     pub health_state: String,
    137     pub health_source: String,
    138     pub registry_path: String,
    139     pub lifecycle_actions: Vec<String>,
    140     #[serde(skip_serializing_if = "Option::is_none")]
    141     pub instance_paths: Option<RuntimeInstancePathsView>,
    142     #[serde(skip_serializing_if = "Option::is_none")]
    143     pub instance_record: Option<RuntimeInstanceRecordView>,
    144 }
    145 
    146 #[derive(Debug, Clone, Serialize)]
    147 pub struct RuntimeInstancePathsView {
    148     pub install_dir: String,
    149     pub state_dir: String,
    150     pub logs_dir: String,
    151     pub run_dir: String,
    152     pub secrets_dir: String,
    153     pub pid_file_path: String,
    154     pub stdout_log_path: String,
    155     pub stderr_log_path: String,
    156     pub metadata_path: String,
    157 }
    158 
    159 #[derive(Debug, Clone, Serialize)]
    160 pub struct RuntimeInstanceRecordView {
    161     pub management_mode: String,
    162     pub install_state: String,
    163     pub binary_path: String,
    164     pub config_path: String,
    165     pub logs_path: String,
    166     pub run_path: String,
    167     pub installed_version: String,
    168     #[serde(skip_serializing_if = "Option::is_none")]
    169     pub health_endpoint: Option<String>,
    170     #[serde(skip_serializing_if = "Option::is_none")]
    171     pub secret_material_ref: Option<String>,
    172     #[serde(skip_serializing_if = "Option::is_none")]
    173     pub last_started_at: Option<String>,
    174     #[serde(skip_serializing_if = "Option::is_none")]
    175     pub last_stopped_at: Option<String>,
    176     #[serde(skip_serializing_if = "Option::is_none")]
    177     pub notes: Option<String>,
    178 }
    179 
    180 #[derive(Debug, Clone, Serialize)]
    181 pub struct MigrationRuntimeView {
    182     pub posture: String,
    183     pub state: String,
    184     pub silent_startup_relocation: bool,
    185     pub compatibility_window: String,
    186     pub detected_legacy_paths: Vec<LegacyPathRuntimeView>,
    187     pub actions: Vec<String>,
    188 }
    189 
    190 #[derive(Debug, Clone, Serialize)]
    191 pub struct LegacyPathRuntimeView {
    192     pub id: String,
    193     pub description: String,
    194     pub path: String,
    195     #[serde(skip_serializing_if = "Option::is_none")]
    196     pub destination: Option<String>,
    197     pub import_hint: String,
    198 }
    199 
    200 #[derive(Debug, Clone, Serialize)]
    201 pub struct OutputRuntimeView {
    202     pub format: String,
    203     pub verbosity: String,
    204     pub color: bool,
    205     pub dry_run: bool,
    206 }
    207 
    208 #[derive(Debug, Clone, Serialize)]
    209 pub struct InteractionRuntimeView {
    210     pub input_enabled: bool,
    211     pub assume_yes: bool,
    212     pub stdin_tty: bool,
    213     pub stdout_tty: bool,
    214     pub prompts_allowed: bool,
    215     pub confirmations_allowed: bool,
    216 }
    217 
    218 #[derive(Debug, Clone, Serialize)]
    219 pub struct ConfigFilesRuntimeView {
    220     pub user_present: bool,
    221     pub workspace_present: bool,
    222 }
    223 
    224 #[derive(Debug, Clone, Serialize)]
    225 pub struct LoggingRuntimeView {
    226     pub initialized: bool,
    227     pub filter: String,
    228     pub stdout: bool,
    229     pub directory: Option<String>,
    230     pub current_file: Option<String>,
    231 }
    232 
    233 #[derive(Debug, Clone, Serialize)]
    234 pub struct PathsRuntimeView {
    235     pub profile: String,
    236     pub profile_source: String,
    237     pub allowed_profiles: Vec<String>,
    238     pub root_source: String,
    239     #[serde(skip_serializing_if = "Option::is_none")]
    240     pub repo_local_root: Option<String>,
    241     #[serde(skip_serializing_if = "Option::is_none")]
    242     pub repo_local_root_source: Option<String>,
    243     pub subordinate_path_override_source: String,
    244     pub app_namespace: String,
    245     pub shared_accounts_namespace: String,
    246     pub shared_identities_namespace: String,
    247     pub app_config_path: String,
    248     pub workspace_config_enabled: bool,
    249     pub workspace_config_path: Option<String>,
    250     pub app_data_root: String,
    251     pub app_logs_root: String,
    252     pub shared_accounts_data_root: String,
    253     pub shared_accounts_secrets_root: String,
    254     pub default_identity_path: String,
    255 }
    256 
    257 #[derive(Debug, Clone, Serialize)]
    258 pub struct AccountRuntimeView {
    259     #[serde(skip_serializing_if = "Option::is_none")]
    260     pub selector: Option<String>,
    261     pub store_path: String,
    262     pub secrets_dir: String,
    263     pub identity_path: String,
    264     pub secret_backend: AccountSecretRuntimeView,
    265 }
    266 
    267 #[derive(Debug, Clone, Serialize)]
    268 pub struct AccountSecretRuntimeView {
    269     pub contract_default_backend: String,
    270     #[serde(skip_serializing_if = "Option::is_none")]
    271     pub contract_default_fallback: Option<String>,
    272     pub allowed_backends: Vec<String>,
    273     #[serde(skip_serializing_if = "Option::is_none")]
    274     pub host_vault_policy: Option<String>,
    275     pub uses_protected_store: bool,
    276     pub configured_primary: String,
    277     #[serde(skip_serializing_if = "Option::is_none")]
    278     pub configured_fallback: Option<String>,
    279     pub state: String,
    280     #[serde(skip_serializing_if = "Option::is_none")]
    281     pub active_backend: Option<String>,
    282     pub used_fallback: bool,
    283     #[serde(skip_serializing_if = "Option::is_none")]
    284     pub reason: Option<String>,
    285 }
    286 
    287 #[derive(Debug, Clone, Serialize)]
    288 pub struct SignerRuntimeView {
    289     pub mode: String,
    290 }
    291 
    292 #[derive(Debug, Clone, Serialize)]
    293 pub struct RelayRuntimeView {
    294     pub count: usize,
    295     pub urls: Vec<String>,
    296     pub publish_policy: String,
    297     pub source: String,
    298 }
    299 
    300 #[derive(Debug, Clone, Serialize)]
    301 pub struct PublishRuntimeView {
    302     pub transport: String,
    303     pub source: String,
    304     pub transport_family: String,
    305     pub state: String,
    306     pub executable: bool,
    307     #[serde(skip_serializing_if = "Option::is_none")]
    308     pub reason: Option<String>,
    309     pub signed_write_required: bool,
    310     pub relay: PublishRelayRuntimeView,
    311     pub provider: PublishProviderRuntimeView,
    312 }
    313 
    314 #[derive(Debug, Clone, Serialize)]
    315 pub struct PublishRelayRuntimeView {
    316     pub ready: bool,
    317     pub count: usize,
    318     pub source: String,
    319 }
    320 
    321 #[derive(Debug, Clone, Serialize)]
    322 pub struct PublishProviderRuntimeView {
    323     pub provider_runtime_id: String,
    324     pub state: String,
    325     pub source: String,
    326     #[serde(skip_serializing_if = "Option::is_none")]
    327     pub reason: Option<String>,
    328 }
    329 
    330 #[derive(Debug, Clone, Serialize)]
    331 pub struct LocalRuntimeView {
    332     pub root: String,
    333     pub replica_db_path: String,
    334     pub backups_dir: String,
    335     pub exports_dir: String,
    336 }
    337 
    338 #[derive(Debug, Clone, Serialize)]
    339 pub struct MycRuntimeView {
    340     pub executable: String,
    341     pub status_timeout_ms: u64,
    342 }
    343 
    344 #[derive(Debug, Clone, Serialize)]
    345 pub struct WorkflowRuntimeView {
    346     pub provider_runtime_id: String,
    347     pub binding_model: String,
    348     pub state: String,
    349     pub provenance: String,
    350     pub source: String,
    351     #[serde(skip_serializing_if = "Option::is_none")]
    352     pub target_kind: Option<String>,
    353     #[serde(skip_serializing_if = "Option::is_none")]
    354     pub target: Option<String>,
    355     pub hyf_helper_state: String,
    356     pub hyf_helper_detail: String,
    357 }
    358 
    359 #[derive(Debug, Clone, Serialize)]
    360 pub struct HyfRuntimeView {
    361     pub enabled: bool,
    362     pub executable: String,
    363 }
    364 
    365 #[derive(Debug, Clone, Serialize)]
    366 pub struct HyfProviderRuntimeView {
    367     pub provider_runtime_id: String,
    368     pub binding_model: String,
    369     pub state: String,
    370     pub provenance: String,
    371     pub source: String,
    372     #[serde(skip_serializing_if = "Option::is_none")]
    373     pub target_kind: Option<String>,
    374     #[serde(skip_serializing_if = "Option::is_none")]
    375     pub target: Option<String>,
    376     pub executable: String,
    377     #[serde(skip_serializing_if = "Option::is_none")]
    378     pub reason: Option<String>,
    379     #[serde(skip_serializing_if = "Option::is_none")]
    380     pub protocol_version: Option<u64>,
    381     #[serde(skip_serializing_if = "Option::is_none")]
    382     pub deterministic_available: Option<bool>,
    383 }
    384 
    385 #[derive(Debug, Clone, Serialize)]
    386 pub struct WritePlaneRuntimeView {
    387     pub provider_runtime_id: String,
    388     pub binding_model: String,
    389     pub state: String,
    390     pub provenance: String,
    391     pub source: String,
    392     #[serde(skip_serializing_if = "Option::is_none")]
    393     pub target_kind: Option<String>,
    394     #[serde(skip_serializing_if = "Option::is_none")]
    395     pub target: Option<String>,
    396     pub detail: String,
    397 }
    398 
    399 #[derive(Debug, Clone, Serialize)]
    400 pub struct RpcRuntimeView {
    401     pub url: String,
    402 }
    403 
    404 #[derive(Debug, Clone, Serialize)]
    405 pub struct ResolvedProviderRuntimeView {
    406     pub capability_id: String,
    407     pub provider_runtime_id: String,
    408     pub binding_model: String,
    409     pub state: String,
    410     pub provenance: String,
    411     pub source: String,
    412     #[serde(skip_serializing_if = "Option::is_none")]
    413     pub target_kind: Option<String>,
    414     #[serde(skip_serializing_if = "Option::is_none")]
    415     pub target: Option<String>,
    416 }
    417 
    418 #[derive(Debug, Clone, Serialize)]
    419 pub struct CapabilityBindingRuntimeView {
    420     pub capability_id: String,
    421     pub provider_runtime_id: String,
    422     pub binding_model: String,
    423     pub state: String,
    424     pub source: String,
    425     #[serde(skip_serializing_if = "Option::is_none")]
    426     pub target_kind: Option<String>,
    427     #[serde(skip_serializing_if = "Option::is_none")]
    428     pub target: Option<String>,
    429     #[serde(skip_serializing_if = "Option::is_none")]
    430     pub managed_account_ref: Option<String>,
    431     #[serde(skip_serializing_if = "Option::is_none")]
    432     pub signer_session_ref: Option<String>,
    433 }
    434 
    435 #[derive(Debug, Clone, Serialize)]
    436 pub struct DoctorView {
    437     pub ok: bool,
    438     pub state: String,
    439     pub account_resolution: AccountResolutionView,
    440     pub checks: Vec<DoctorCheckView>,
    441     pub source: String,
    442     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    443     pub actions: Vec<String>,
    444 }
    445 
    446 #[derive(Debug, Clone, Serialize)]
    447 pub struct DoctorCheckView {
    448     pub name: String,
    449     pub status: String,
    450     pub detail: String,
    451 }
    452 
    453 #[derive(Debug, Clone, Serialize)]
    454 pub struct IdentityPublicView {
    455     pub id: String,
    456     pub public_key_hex: String,
    457     pub public_key_npub: String,
    458 }
    459 
    460 impl IdentityPublicView {
    461     pub fn from_public_identity(identity: &radroots_identity::RadrootsIdentityPublic) -> Self {
    462         Self {
    463             id: identity.id.to_string(),
    464             public_key_hex: identity.public_key_hex.clone(),
    465             public_key_npub: identity.public_key_npub.clone(),
    466         }
    467     }
    468 }
    469 
    470 #[derive(Debug, Clone, Serialize)]
    471 pub struct AccountSummaryView {
    472     pub id: String,
    473     #[serde(skip_serializing_if = "Option::is_none")]
    474     pub display_name: Option<String>,
    475     pub signer: String,
    476     pub custody: String,
    477     pub write_capable: bool,
    478     pub is_default: bool,
    479 }
    480 
    481 impl AccountSummaryView {
    482     pub fn from_account_runtime(
    483         record: &RadrootsNostrAccountRecord,
    484         signer: &str,
    485         custody: &str,
    486         write_capable: bool,
    487         is_default: bool,
    488     ) -> Self {
    489         Self {
    490             id: record.account_id.to_string(),
    491             display_name: record.label.clone(),
    492             signer: signer.to_owned(),
    493             custody: custody.to_owned(),
    494             write_capable,
    495             is_default,
    496         }
    497     }
    498 }
    499 
    500 #[derive(Debug, Clone, Serialize)]
    501 pub struct AccountResolutionView {
    502     pub status: String,
    503     pub source: String,
    504     #[serde(skip_serializing_if = "Option::is_none")]
    505     pub resolved_account: Option<AccountSummaryView>,
    506     #[serde(skip_serializing_if = "Option::is_none")]
    507     pub default_account: Option<AccountSummaryView>,
    508 }
    509 
    510 #[derive(Debug, Clone, Serialize)]
    511 pub struct AccountWhoamiView {
    512     pub state: String,
    513     pub source: String,
    514     #[serde(skip_serializing_if = "Option::is_none")]
    515     pub reason: Option<String>,
    516     pub account_resolution: AccountResolutionView,
    517     #[serde(skip_serializing_if = "Option::is_none")]
    518     pub public_identity: Option<IdentityPublicView>,
    519     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    520     pub actions: Vec<String>,
    521 }
    522 
    523 impl AccountWhoamiView {
    524     pub fn disposition(&self) -> CommandDisposition {
    525         match self.state.as_str() {
    526             "unconfigured" => CommandDisposition::Unconfigured,
    527             _ => CommandDisposition::Success,
    528         }
    529     }
    530 }
    531 
    532 #[derive(Debug, Clone, Serialize)]
    533 pub struct AccountNewView {
    534     pub state: String,
    535     pub source: String,
    536     pub account: AccountSummaryView,
    537     pub public_identity: IdentityPublicView,
    538     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    539     pub actions: Vec<String>,
    540 }
    541 
    542 #[derive(Debug, Clone, Serialize)]
    543 pub struct AccountImportView {
    544     pub state: String,
    545     pub source: String,
    546     pub account: AccountSummaryView,
    547     pub public_identity: IdentityPublicView,
    548     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    549     pub actions: Vec<String>,
    550 }
    551 
    552 #[derive(Debug, Clone, Serialize)]
    553 pub struct AccountUseView {
    554     pub state: String,
    555     pub source: String,
    556     pub default_account_id: String,
    557     pub account: AccountSummaryView,
    558 }
    559 
    560 #[derive(Debug, Clone, Serialize)]
    561 pub struct AccountClearDefaultView {
    562     pub state: String,
    563     pub source: String,
    564     #[serde(skip_serializing_if = "Option::is_none")]
    565     pub cleared_account: Option<AccountSummaryView>,
    566     pub remaining_account_count: usize,
    567     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    568     pub actions: Vec<String>,
    569 }
    570 
    571 #[derive(Debug, Clone, Serialize)]
    572 pub struct AccountRemoveView {
    573     pub state: String,
    574     pub source: String,
    575     pub removed_account: AccountSummaryView,
    576     pub default_cleared: bool,
    577     pub remaining_account_count: usize,
    578     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    579     pub actions: Vec<String>,
    580 }
    581 
    582 #[derive(Debug, Clone, Serialize)]
    583 pub struct AccountListView {
    584     pub source: String,
    585     pub count: usize,
    586     pub accounts: Vec<AccountSummaryView>,
    587     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    588     pub actions: Vec<String>,
    589 }
    590 
    591 #[derive(Debug, Clone, Serialize)]
    592 pub struct LocalInitView {
    593     pub state: String,
    594     pub source: String,
    595     pub local_root: String,
    596     pub replica_db: String,
    597     pub path: String,
    598     pub replica_db_version: String,
    599     pub backup_format_version: String,
    600 }
    601 
    602 #[derive(Debug, Clone, Serialize)]
    603 pub struct LocalStatusView {
    604     pub state: String,
    605     pub source: String,
    606     pub local_root: String,
    607     pub canonical_store: String,
    608     pub sdk_storage: String,
    609     pub sdk_root: String,
    610     pub sdk_existed_before_open: bool,
    611     pub event_store: SdkEventStoreStatusView,
    612     pub outbox: SdkOutboxStatusView,
    613     pub integrity: SdkIntegrityView,
    614     pub legacy_replica: LocalLegacyReplicaStatusView,
    615     #[serde(skip_serializing_if = "Option::is_none")]
    616     pub reason: Option<String>,
    617     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    618     pub actions: Vec<String>,
    619 }
    620 
    621 impl LocalStatusView {
    622     pub fn disposition(&self) -> CommandDisposition {
    623         match self.state.as_str() {
    624             "unconfigured" => CommandDisposition::Unconfigured,
    625             "needs_attention" => CommandDisposition::ValidationFailed,
    626             _ => CommandDisposition::Success,
    627         }
    628     }
    629 }
    630 
    631 #[derive(Debug, Clone, Serialize)]
    632 pub struct SdkSqliteStatusView {
    633     pub schema_version: i64,
    634     pub journal_mode: String,
    635     pub foreign_keys_enabled: bool,
    636     pub busy_timeout_ms: i64,
    637     pub integrity_ok: bool,
    638     pub integrity_result: String,
    639 }
    640 
    641 #[derive(Debug, Clone, Serialize)]
    642 pub struct SdkEventStoreStatusView {
    643     #[serde(skip_serializing_if = "Option::is_none")]
    644     pub path: Option<String>,
    645     pub store: SdkSqliteStatusView,
    646     pub total_events: i64,
    647     pub projection_eligible_events: i64,
    648     pub relay_observations: i64,
    649     #[serde(skip_serializing_if = "Option::is_none")]
    650     pub last_event_seq: Option<i64>,
    651     #[serde(skip_serializing_if = "Option::is_none")]
    652     pub last_event_updated_at_ms: Option<i64>,
    653 }
    654 
    655 #[derive(Debug, Clone, Serialize)]
    656 pub struct SdkOutboxStatusView {
    657     #[serde(skip_serializing_if = "Option::is_none")]
    658     pub path: Option<String>,
    659     pub store: SdkSqliteStatusView,
    660     pub total_events: i64,
    661     pub pending_events: i64,
    662     pub retryable_events: i64,
    663     pub terminal_events: i64,
    664     pub failed_terminal_events: i64,
    665     pub ready_signed_events: i64,
    666     pub publishing_events: i64,
    667     #[serde(skip_serializing_if = "Option::is_none")]
    668     pub last_attempt_at_ms: Option<i64>,
    669     #[serde(skip_serializing_if = "Option::is_none")]
    670     pub last_error: Option<String>,
    671 }
    672 
    673 #[derive(Debug, Clone, Serialize)]
    674 pub struct SdkIntegrityView {
    675     pub checked_paths: Vec<String>,
    676     pub event_store_ok: bool,
    677     pub outbox_ok: bool,
    678     pub event_store_result: String,
    679     pub outbox_result: String,
    680 }
    681 
    682 #[derive(Debug, Clone, Serialize)]
    683 pub struct LocalLegacyReplicaStatusView {
    684     pub state: String,
    685     pub source: String,
    686     pub replica_db: String,
    687     pub path: String,
    688     pub replica_db_version: String,
    689     pub backup_format_version: String,
    690     pub schema_hash: String,
    691     pub counts: LocalReplicaCountsView,
    692     pub sync: LocalReplicaSyncView,
    693     #[serde(skip_serializing_if = "Option::is_none")]
    694     pub reason: Option<String>,
    695     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    696     pub actions: Vec<String>,
    697 }
    698 
    699 #[derive(Debug, Clone, Serialize)]
    700 pub struct LocalReplicaCountsView {
    701     pub farms: u64,
    702     pub listings: u64,
    703     pub profiles: u64,
    704     pub relays: u64,
    705     pub event_states: u64,
    706 }
    707 
    708 #[derive(Debug, Clone, Serialize)]
    709 pub struct LocalReplicaSyncView {
    710     pub expected_count: usize,
    711     pub pending_count: usize,
    712 }
    713 
    714 #[derive(Debug, Clone, Serialize)]
    715 pub struct SetupView {
    716     pub state: String,
    717     pub source: String,
    718     pub role: String,
    719     pub account_resolution: AccountResolutionView,
    720     pub local_state: String,
    721     pub local_root: String,
    722     pub relay_state: String,
    723     pub relay_count: usize,
    724     pub farm_state: String,
    725     #[serde(default)]
    726     pub ready: Vec<String>,
    727     #[serde(default)]
    728     pub needs_attention: Vec<String>,
    729     #[serde(default)]
    730     pub next: Vec<String>,
    731 }
    732 
    733 impl SetupView {
    734     pub fn disposition(&self) -> CommandDisposition {
    735         match self.state.as_str() {
    736             "unconfigured" => CommandDisposition::Unconfigured,
    737             _ => CommandDisposition::Success,
    738         }
    739     }
    740 }
    741 
    742 #[derive(Debug, Clone, Serialize)]
    743 pub struct StatusView {
    744     pub state: String,
    745     pub source: String,
    746     pub account_resolution: AccountResolutionView,
    747     pub local_state: String,
    748     pub local_root: String,
    749     pub relay_state: String,
    750     pub relay_count: usize,
    751     pub farm_state: String,
    752     #[serde(default)]
    753     pub ready: Vec<String>,
    754     #[serde(default)]
    755     pub needs_attention: Vec<String>,
    756     #[serde(default)]
    757     pub next: Vec<String>,
    758 }
    759 
    760 impl StatusView {
    761     pub fn disposition(&self) -> CommandDisposition {
    762         match self.state.as_str() {
    763             "unconfigured" => CommandDisposition::Unconfigured,
    764             _ => CommandDisposition::Success,
    765         }
    766     }
    767 }
    768 
    769 #[derive(Debug, Clone, Serialize)]
    770 pub struct FarmSetupView {
    771     pub state: String,
    772     pub source: String,
    773     #[serde(skip_serializing_if = "Option::is_none")]
    774     pub config: Option<FarmConfigSummaryView>,
    775     #[serde(skip_serializing_if = "Option::is_none")]
    776     pub reason: Option<String>,
    777     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    778     pub actions: Vec<String>,
    779 }
    780 
    781 impl FarmSetupView {
    782     pub fn disposition(&self) -> CommandDisposition {
    783         match self.state.as_str() {
    784             "unconfigured" => CommandDisposition::Unconfigured,
    785             _ => CommandDisposition::Success,
    786         }
    787     }
    788 }
    789 
    790 #[derive(Debug, Clone, Serialize)]
    791 pub struct FarmSetView {
    792     pub state: String,
    793     pub source: String,
    794     pub field: String,
    795     pub value: String,
    796     #[serde(skip_serializing_if = "Option::is_none")]
    797     pub config: Option<FarmConfigSummaryView>,
    798     #[serde(skip_serializing_if = "Option::is_none")]
    799     pub reason: Option<String>,
    800     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    801     pub actions: Vec<String>,
    802 }
    803 
    804 impl FarmSetView {
    805     pub fn disposition(&self) -> CommandDisposition {
    806         match self.state.as_str() {
    807             "unconfigured" => CommandDisposition::Unconfigured,
    808             _ => CommandDisposition::Success,
    809         }
    810     }
    811 }
    812 
    813 #[derive(Debug, Clone, Serialize)]
    814 pub struct FarmRebindView {
    815     pub state: String,
    816     pub source: String,
    817     pub scope: String,
    818     pub path: String,
    819     pub config_present: bool,
    820     pub dry_run: bool,
    821     pub seller_actor_source: String,
    822     #[serde(skip_serializing_if = "Option::is_none")]
    823     pub from_seller_account_id: Option<String>,
    824     #[serde(skip_serializing_if = "Option::is_none")]
    825     pub from_seller_pubkey: Option<String>,
    826     #[serde(skip_serializing_if = "Option::is_none")]
    827     pub to_seller_account_id: Option<String>,
    828     #[serde(skip_serializing_if = "Option::is_none")]
    829     pub to_seller_pubkey: Option<String>,
    830     #[serde(skip_serializing_if = "Option::is_none")]
    831     pub seller_pubkey_changed: Option<bool>,
    832     #[serde(skip_serializing_if = "Option::is_none")]
    833     pub publication_state_action: Option<String>,
    834     #[serde(skip_serializing_if = "Option::is_none")]
    835     pub config: Option<FarmConfigSummaryView>,
    836     #[serde(skip_serializing_if = "Option::is_none")]
    837     pub reason: Option<String>,
    838     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    839     pub actions: Vec<String>,
    840 }
    841 
    842 impl FarmRebindView {
    843     pub fn disposition(&self) -> CommandDisposition {
    844         match self.state.as_str() {
    845             "unconfigured" => CommandDisposition::Unconfigured,
    846             _ => CommandDisposition::Success,
    847         }
    848     }
    849 }
    850 
    851 #[derive(Debug, Clone, Serialize)]
    852 pub struct FarmStatusView {
    853     pub state: String,
    854     pub source: String,
    855     pub scope: String,
    856     pub path: String,
    857     pub config_present: bool,
    858     pub config_valid: bool,
    859     pub account_state: String,
    860     pub listing_defaults_state: String,
    861     pub publish_transport: String,
    862     pub publish_state: String,
    863     pub publish_executable: bool,
    864     #[serde(skip_serializing_if = "Option::is_none")]
    865     pub publish_reason: Option<String>,
    866     #[serde(skip_serializing_if = "Option::is_none")]
    867     pub config: Option<FarmConfigSummaryView>,
    868     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    869     pub missing: Vec<String>,
    870     #[serde(skip_serializing_if = "Option::is_none")]
    871     pub reason: Option<String>,
    872     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    873     pub actions: Vec<String>,
    874 }
    875 
    876 impl FarmStatusView {
    877     pub fn disposition(&self) -> CommandDisposition {
    878         match self.state.as_str() {
    879             "unconfigured" => CommandDisposition::Unconfigured,
    880             _ => CommandDisposition::Success,
    881         }
    882     }
    883 }
    884 
    885 #[derive(Debug, Clone, Serialize)]
    886 pub struct FarmGetView {
    887     pub state: String,
    888     pub source: String,
    889     pub scope: String,
    890     pub path: String,
    891     pub config_present: bool,
    892     #[serde(skip_serializing_if = "Option::is_none")]
    893     pub document: Option<FarmConfigDocumentView>,
    894     #[serde(skip_serializing_if = "Option::is_none")]
    895     pub reason: Option<String>,
    896     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    897     pub actions: Vec<String>,
    898 }
    899 
    900 impl FarmGetView {
    901     pub fn disposition(&self) -> CommandDisposition {
    902         match self.state.as_str() {
    903             "unconfigured" | "missing" => CommandDisposition::Unconfigured,
    904             _ => CommandDisposition::Success,
    905         }
    906     }
    907 }
    908 
    909 #[derive(Debug, Clone, Serialize)]
    910 pub struct FarmPublishView {
    911     pub state: String,
    912     pub source: String,
    913     pub scope: String,
    914     pub path: String,
    915     pub config_present: bool,
    916     pub dry_run: bool,
    917     pub seller_account_id: String,
    918     pub seller_pubkey: String,
    919     pub seller_actor_source: String,
    920     pub farm_d_tag: String,
    921     pub profile: FarmPublishComponentView,
    922     pub farm: FarmPublishComponentView,
    923     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    924     pub local_replica: Vec<FarmPublishLocalReplicaView>,
    925     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    926     pub missing: Vec<String>,
    927     #[serde(skip_serializing_if = "Option::is_none")]
    928     pub reason: Option<String>,
    929     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    930     pub actions: Vec<String>,
    931 }
    932 
    933 impl FarmPublishView {
    934     pub fn disposition(&self) -> CommandDisposition {
    935         match self.state.as_str() {
    936             "unconfigured" => CommandDisposition::Unconfigured,
    937             "partial" | "unavailable" => CommandDisposition::ExternalUnavailable,
    938             "error" => CommandDisposition::InternalError,
    939             _ => CommandDisposition::Success,
    940         }
    941     }
    942 }
    943 
    944 #[derive(Debug, Clone, Serialize)]
    945 pub struct FarmPublishComponentView {
    946     pub state: String,
    947     pub rpc_method: String,
    948     pub event_kind: u32,
    949     pub deduplicated: bool,
    950     #[serde(default)]
    951     pub target_relays: Vec<String>,
    952     #[serde(default)]
    953     pub connected_relays: Vec<String>,
    954     #[serde(default)]
    955     pub acknowledged_relays: Vec<String>,
    956     #[serde(default)]
    957     pub failed_relays: Vec<RelayFailureView>,
    958     #[serde(skip_serializing_if = "Option::is_none")]
    959     pub job_id: Option<String>,
    960     #[serde(skip_serializing_if = "Option::is_none")]
    961     pub job_status: Option<String>,
    962     #[serde(skip_serializing_if = "Option::is_none")]
    963     pub signer_mode: Option<String>,
    964     #[serde(skip_serializing_if = "Option::is_none")]
    965     pub event_id: Option<String>,
    966     #[serde(skip_serializing_if = "Option::is_none")]
    967     pub event_addr: Option<String>,
    968     #[serde(skip_serializing_if = "Option::is_none")]
    969     pub idempotency_key: Option<String>,
    970     #[serde(skip_serializing_if = "Option::is_none")]
    971     pub reason: Option<String>,
    972     #[serde(skip_serializing_if = "Option::is_none")]
    973     pub job: Option<FarmPublishJobView>,
    974     #[serde(skip_serializing_if = "Option::is_none")]
    975     pub event: Option<FarmPublishEventView>,
    976 }
    977 
    978 #[derive(Debug, Clone, Serialize)]
    979 pub struct FarmPublishLocalReplicaView {
    980     pub component: String,
    981     pub state: String,
    982     pub store_state: String,
    983     #[serde(skip_serializing_if = "Option::is_none")]
    984     pub ingest_outcome: Option<String>,
    985     #[serde(skip_serializing_if = "Option::is_none")]
    986     pub event_id: Option<String>,
    987     #[serde(skip_serializing_if = "Option::is_none")]
    988     pub event_addr: Option<String>,
    989     #[serde(skip_serializing_if = "Option::is_none")]
    990     pub reason: Option<String>,
    991     #[serde(default, skip_serializing_if = "Vec::is_empty")]
    992     pub actions: Vec<String>,
    993 }
    994 
    995 #[derive(Debug, Clone, Serialize)]
    996 pub struct FarmPublishJobView {
    997     pub rpc_method: String,
    998     pub state: String,
    999     #[serde(skip_serializing_if = "Option::is_none")]
   1000     pub job_id: Option<String>,
   1001     #[serde(skip_serializing_if = "Option::is_none")]
   1002     pub idempotency_key: Option<String>,
   1003     #[serde(skip_serializing_if = "Option::is_none")]
   1004     pub signer_mode: Option<String>,
   1005 }
   1006 
   1007 #[derive(Debug, Clone, Serialize)]
   1008 pub struct FarmPublishEventView {
   1009     pub kind: u32,
   1010     pub author: String,
   1011     pub content: String,
   1012     pub tags: Vec<Vec<String>>,
   1013     #[serde(skip_serializing_if = "Option::is_none")]
   1014     pub event_id: Option<String>,
   1015     #[serde(skip_serializing_if = "Option::is_none")]
   1016     pub event_addr: Option<String>,
   1017 }
   1018 
   1019 #[derive(Debug, Clone, Serialize)]
   1020 pub struct RelayFailureView {
   1021     pub relay: String,
   1022     pub reason: String,
   1023 }
   1024 
   1025 #[derive(Debug, Clone, Serialize)]
   1026 pub struct FarmConfigSummaryView {
   1027     pub scope: String,
   1028     pub path: String,
   1029     pub seller_account_id: String,
   1030     #[serde(skip_serializing_if = "Option::is_none")]
   1031     pub seller_pubkey: Option<String>,
   1032     pub seller_actor_source: String,
   1033     pub farm_d_tag: String,
   1034     pub name: String,
   1035     #[serde(skip_serializing_if = "Option::is_none")]
   1036     pub location_primary: Option<String>,
   1037     pub delivery_method: String,
   1038     pub publication: FarmPublicationView,
   1039 }
   1040 
   1041 #[derive(Debug, Clone, Serialize)]
   1042 pub struct FarmConfigDocumentView {
   1043     pub selection: FarmSelectionView,
   1044     pub profile: RadrootsProfile,
   1045     pub farm: RadrootsFarm,
   1046     pub listing_defaults: FarmListingDefaultsView,
   1047     pub publication: FarmPublicationView,
   1048 }
   1049 
   1050 #[derive(Debug, Clone, Serialize)]
   1051 pub struct FarmSelectionView {
   1052     pub scope: String,
   1053     pub seller_account_id: String,
   1054     pub farm_d_tag: String,
   1055 }
   1056 
   1057 #[derive(Debug, Clone, Serialize)]
   1058 pub struct FarmListingDefaultsView {
   1059     pub delivery_method: String,
   1060     pub location: RadrootsListingLocation,
   1061 }
   1062 
   1063 #[derive(Debug, Clone, Serialize)]
   1064 pub struct FarmPublicationView {
   1065     pub profile_state: String,
   1066     pub farm_state: String,
   1067     #[serde(skip_serializing_if = "Option::is_none")]
   1068     pub profile_event_id: Option<String>,
   1069     #[serde(skip_serializing_if = "Option::is_none")]
   1070     pub farm_event_id: Option<String>,
   1071     #[serde(skip_serializing_if = "Option::is_none")]
   1072     pub profile_published_at: Option<u64>,
   1073     #[serde(skip_serializing_if = "Option::is_none")]
   1074     pub farm_published_at: Option<u64>,
   1075 }
   1076 
   1077 #[derive(Debug, Clone, Serialize)]
   1078 pub struct FindView {
   1079     pub state: String,
   1080     pub source: String,
   1081     pub query: String,
   1082     pub count: usize,
   1083     pub relay_count: usize,
   1084     pub replica_db: String,
   1085     pub freshness: SyncFreshnessView,
   1086     pub results: Vec<FindResultView>,
   1087     #[serde(skip_serializing_if = "Option::is_none")]
   1088     pub hyf: Option<FindHyfView>,
   1089     #[serde(skip_serializing_if = "Option::is_none")]
   1090     pub reason: Option<String>,
   1091     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1092     pub actions: Vec<String>,
   1093 }
   1094 
   1095 impl FindView {
   1096     pub fn disposition(&self) -> CommandDisposition {
   1097         match self.state.as_str() {
   1098             "unconfigured" => CommandDisposition::Unconfigured,
   1099             _ => CommandDisposition::Success,
   1100         }
   1101     }
   1102 }
   1103 
   1104 #[derive(Debug, Clone, Serialize)]
   1105 pub struct FindHyfView {
   1106     pub state: String,
   1107     pub source: String,
   1108     pub rewritten_query: String,
   1109     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1110     pub query_terms: Vec<String>,
   1111 }
   1112 
   1113 #[derive(Debug, Clone, Serialize)]
   1114 pub struct MarketReadinessView {
   1115     pub protocol_valid: bool,
   1116     pub marketplace_eligible: bool,
   1117     pub order_request_enabled: bool,
   1118     pub primary_bin_verified: bool,
   1119     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1120     pub reason_codes: Vec<String>,
   1121 }
   1122 
   1123 impl MarketReadinessView {
   1124     pub fn unavailable(reason_code: impl Into<String>) -> Self {
   1125         Self {
   1126             protocol_valid: false,
   1127             marketplace_eligible: false,
   1128             order_request_enabled: false,
   1129             primary_bin_verified: false,
   1130             reason_codes: vec![reason_code.into()],
   1131         }
   1132     }
   1133 
   1134     pub fn from_market_projection(
   1135         listing_addr: Option<&str>,
   1136         primary_bin_id: Option<&str>,
   1137         verified_primary_bin_id: Option<&str>,
   1138         title: Option<&str>,
   1139         category: Option<&str>,
   1140         available_amount: Option<i64>,
   1141         price_amount: f64,
   1142         price_currency: &str,
   1143         price_per_amount: f64,
   1144     ) -> Self {
   1145         let protocol_valid = listing_addr.is_some_and(|listing_addr| {
   1146             RadrootsListingAddress::parse(listing_addr).is_ok_and(|parsed| {
   1147                 parsed
   1148                     .as_str()
   1149                     .split_once(':')
   1150                     .and_then(|(kind, _)| kind.parse::<u32>().ok())
   1151                     == Some(KIND_LISTING)
   1152             })
   1153         });
   1154         let marketplace_eligible = protocol_valid
   1155             && title.is_some_and(|title| !title.trim().is_empty())
   1156             && category.is_some_and(|category| !category.trim().is_empty());
   1157         let inventory_available = available_amount.is_some_and(|amount| amount > 0);
   1158         let primary_bin_available =
   1159             primary_bin_id.is_some_and(|primary_bin_id| !primary_bin_id.trim().is_empty());
   1160         let primary_bin_verified = protocol_valid
   1161             && primary_bin_available
   1162             && primary_bin_id.is_some_and(|primary_bin_id| {
   1163                 verified_primary_bin_id.is_some_and(|verified_primary_bin_id| {
   1164                     verified_primary_bin_id.trim() == primary_bin_id.trim()
   1165                 })
   1166             });
   1167         let price_available = price_amount.is_finite()
   1168             && price_amount > 0.0
   1169             && !price_currency.trim().is_empty()
   1170             && price_per_amount.is_finite()
   1171             && price_per_amount > 0.0;
   1172         let order_request_enabled =
   1173             marketplace_eligible && inventory_available && primary_bin_verified && price_available;
   1174         let mut reason_codes = Vec::new();
   1175         if !protocol_valid {
   1176             reason_codes.push("listing_protocol_invalid".to_owned());
   1177         }
   1178         if protocol_valid && !marketplace_eligible {
   1179             reason_codes.push("listing_marketplace_ineligible".to_owned());
   1180         }
   1181         if marketplace_eligible && !order_request_enabled {
   1182             reason_codes.push("listing_order_request_disabled".to_owned());
   1183             if !inventory_available {
   1184                 reason_codes.push("listing_inventory_unavailable".to_owned());
   1185             }
   1186             if !primary_bin_available {
   1187                 reason_codes.push("listing_primary_bin_missing".to_owned());
   1188             } else if !primary_bin_verified {
   1189                 reason_codes.push("listing_primary_bin_invalid".to_owned());
   1190             }
   1191             if !price_available {
   1192                 reason_codes.push("listing_price_unavailable".to_owned());
   1193             }
   1194         }
   1195         Self {
   1196             protocol_valid,
   1197             marketplace_eligible,
   1198             order_request_enabled,
   1199             primary_bin_verified,
   1200             reason_codes,
   1201         }
   1202     }
   1203 }
   1204 
   1205 #[cfg(test)]
   1206 mod market_readiness_tests {
   1207     use super::MarketReadinessView;
   1208 
   1209     const LISTING_ADDR: &str = "30402:1111111111111111111111111111111111111111111111111111111111111111:AAAAAAAAAAAAAAAAAAAAAg";
   1210 
   1211     #[test]
   1212     fn market_readiness_separates_protocol_marketplace_and_order_request_state() {
   1213         let enabled = MarketReadinessView::from_market_projection(
   1214             Some(LISTING_ADDR),
   1215             Some("bin-1"),
   1216             Some("bin-1"),
   1217             Some("Eggs"),
   1218             Some("eggs"),
   1219             Some(1),
   1220             6.0,
   1221             "USD",
   1222             1.0,
   1223         );
   1224         assert!(enabled.protocol_valid);
   1225         assert!(enabled.marketplace_eligible);
   1226         assert!(enabled.order_request_enabled);
   1227         assert!(enabled.primary_bin_verified);
   1228         assert!(enabled.reason_codes.is_empty());
   1229 
   1230         let invalid = MarketReadinessView::from_market_projection(
   1231             None,
   1232             Some("bin-1"),
   1233             Some("bin-1"),
   1234             Some("Eggs"),
   1235             Some("eggs"),
   1236             Some(1),
   1237             6.0,
   1238             "USD",
   1239             1.0,
   1240         );
   1241         assert!(!invalid.protocol_valid);
   1242         assert!(!invalid.marketplace_eligible);
   1243         assert!(!invalid.order_request_enabled);
   1244         assert!(!invalid.primary_bin_verified);
   1245         assert_eq!(invalid.reason_codes, vec!["listing_protocol_invalid"]);
   1246 
   1247         let ineligible = MarketReadinessView::from_market_projection(
   1248             Some(LISTING_ADDR),
   1249             Some("bin-1"),
   1250             Some("bin-1"),
   1251             Some(" "),
   1252             Some("eggs"),
   1253             Some(1),
   1254             6.0,
   1255             "USD",
   1256             1.0,
   1257         );
   1258         assert!(ineligible.protocol_valid);
   1259         assert!(!ineligible.marketplace_eligible);
   1260         assert!(!ineligible.order_request_enabled);
   1261         assert!(ineligible.primary_bin_verified);
   1262         assert_eq!(
   1263             ineligible.reason_codes,
   1264             vec!["listing_marketplace_ineligible"]
   1265         );
   1266 
   1267         let order_request_disabled = MarketReadinessView::from_market_projection(
   1268             Some(LISTING_ADDR),
   1269             Some("bin-1"),
   1270             Some("bin-1"),
   1271             Some("Eggs"),
   1272             Some("eggs"),
   1273             Some(0),
   1274             6.0,
   1275             "USD",
   1276             1.0,
   1277         );
   1278         assert!(order_request_disabled.protocol_valid);
   1279         assert!(order_request_disabled.marketplace_eligible);
   1280         assert!(!order_request_disabled.order_request_enabled);
   1281         assert!(order_request_disabled.primary_bin_verified);
   1282         assert_eq!(
   1283             order_request_disabled.reason_codes,
   1284             vec![
   1285                 "listing_order_request_disabled",
   1286                 "listing_inventory_unavailable"
   1287             ]
   1288         );
   1289 
   1290         let primary_bin_missing = MarketReadinessView::from_market_projection(
   1291             Some(LISTING_ADDR),
   1292             None,
   1293             Some("bin-1"),
   1294             Some("Eggs"),
   1295             Some("eggs"),
   1296             Some(1),
   1297             6.0,
   1298             "USD",
   1299             1.0,
   1300         );
   1301         assert!(primary_bin_missing.protocol_valid);
   1302         assert!(primary_bin_missing.marketplace_eligible);
   1303         assert!(!primary_bin_missing.order_request_enabled);
   1304         assert!(!primary_bin_missing.primary_bin_verified);
   1305         assert_eq!(
   1306             primary_bin_missing.reason_codes,
   1307             vec![
   1308                 "listing_order_request_disabled",
   1309                 "listing_primary_bin_missing"
   1310             ]
   1311         );
   1312 
   1313         let primary_bin_blank = MarketReadinessView::from_market_projection(
   1314             Some(LISTING_ADDR),
   1315             Some(" "),
   1316             Some("bin-1"),
   1317             Some("Eggs"),
   1318             Some("eggs"),
   1319             Some(1),
   1320             6.0,
   1321             "USD",
   1322             1.0,
   1323         );
   1324         assert_eq!(
   1325             primary_bin_blank.reason_codes,
   1326             vec![
   1327                 "listing_order_request_disabled",
   1328                 "listing_primary_bin_missing"
   1329             ]
   1330         );
   1331 
   1332         let primary_bin_invalid = MarketReadinessView::from_market_projection(
   1333             Some(LISTING_ADDR),
   1334             Some("missing-bin"),
   1335             Some("bin-1"),
   1336             Some("Eggs"),
   1337             Some("eggs"),
   1338             Some(1),
   1339             6.0,
   1340             "USD",
   1341             1.0,
   1342         );
   1343         assert!(primary_bin_invalid.protocol_valid);
   1344         assert!(primary_bin_invalid.marketplace_eligible);
   1345         assert!(!primary_bin_invalid.order_request_enabled);
   1346         assert!(!primary_bin_invalid.primary_bin_verified);
   1347         assert_eq!(
   1348             primary_bin_invalid.reason_codes,
   1349             vec![
   1350                 "listing_order_request_disabled",
   1351                 "listing_primary_bin_invalid"
   1352             ]
   1353         );
   1354     }
   1355 }
   1356 
   1357 #[derive(Debug, Clone, Serialize)]
   1358 pub struct JobListView {
   1359     pub state: String,
   1360     pub source: String,
   1361     pub rpc_url: String,
   1362     pub count: usize,
   1363     #[serde(skip_serializing_if = "Option::is_none")]
   1364     pub reason: Option<String>,
   1365     pub jobs: Vec<JobSummaryView>,
   1366     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1367     pub actions: Vec<String>,
   1368 }
   1369 
   1370 #[derive(Debug, Clone, Serialize)]
   1371 pub struct JobGetView {
   1372     pub state: String,
   1373     pub source: String,
   1374     pub rpc_url: String,
   1375     pub lookup: String,
   1376     #[serde(skip_serializing_if = "Option::is_none")]
   1377     pub reason: Option<String>,
   1378     #[serde(skip_serializing_if = "Option::is_none")]
   1379     pub job: Option<JobDetailView>,
   1380     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1381     pub actions: Vec<String>,
   1382 }
   1383 
   1384 #[derive(Debug, Clone, Serialize)]
   1385 pub struct JobWatchView {
   1386     pub state: String,
   1387     pub source: String,
   1388     pub rpc_url: String,
   1389     pub job_id: String,
   1390     pub interval_ms: u64,
   1391     #[serde(skip_serializing_if = "Option::is_none")]
   1392     pub reason: Option<String>,
   1393     pub frames: Vec<JobWatchFrameView>,
   1394     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1395     pub actions: Vec<String>,
   1396 }
   1397 
   1398 #[derive(Debug, Clone, Serialize)]
   1399 pub struct JobSummaryView {
   1400     pub id: String,
   1401     pub command: String,
   1402     pub state: String,
   1403     pub terminal: bool,
   1404     pub signer: String,
   1405     pub requested_at_unix: u64,
   1406     #[serde(skip_serializing_if = "Option::is_none")]
   1407     pub completed_at_unix: Option<u64>,
   1408     pub recovered_after_restart: bool,
   1409 }
   1410 
   1411 #[derive(Debug, Clone, Serialize)]
   1412 pub struct JobDetailView {
   1413     pub id: String,
   1414     pub command: String,
   1415     pub state: String,
   1416     pub terminal: bool,
   1417     pub signer: String,
   1418     pub requested_at_unix: u64,
   1419     #[serde(skip_serializing_if = "Option::is_none")]
   1420     pub completed_at_unix: Option<u64>,
   1421     pub recovered_after_restart: bool,
   1422     #[serde(skip_serializing_if = "Option::is_none")]
   1423     pub event_id: Option<String>,
   1424     #[serde(skip_serializing_if = "Option::is_none")]
   1425     pub event_addr: Option<String>,
   1426     pub delivery_policy: String,
   1427     #[serde(skip_serializing_if = "Option::is_none")]
   1428     pub delivery_quorum: Option<usize>,
   1429     pub relay_count: usize,
   1430     pub acknowledged_relay_count: usize,
   1431     pub required_acknowledged_relay_count: usize,
   1432     pub attempt_count: usize,
   1433     pub relay_outcome_summary: String,
   1434     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1435     pub attempt_summaries: Vec<String>,
   1436 }
   1437 
   1438 #[derive(Debug, Clone, Serialize)]
   1439 pub struct JobWatchFrameView {
   1440     pub sequence: usize,
   1441     pub observed_at_unix: u64,
   1442     pub state: String,
   1443     pub terminal: bool,
   1444     pub signer: String,
   1445     pub summary: String,
   1446 }
   1447 
   1448 #[derive(Debug, Clone, Serialize)]
   1449 pub struct OrderNewView {
   1450     pub state: String,
   1451     pub source: String,
   1452     pub order_id: String,
   1453     pub file: String,
   1454     #[serde(skip_serializing_if = "Option::is_none")]
   1455     pub listing_lookup: Option<String>,
   1456     #[serde(skip_serializing_if = "Option::is_none")]
   1457     pub listing_addr: Option<String>,
   1458     #[serde(skip_serializing_if = "Option::is_none")]
   1459     pub listing_event_id: Option<String>,
   1460     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1461     pub listing_relays: Vec<String>,
   1462     #[serde(skip_serializing_if = "Option::is_none")]
   1463     pub buyer_account_id: Option<String>,
   1464     #[serde(skip_serializing_if = "Option::is_none")]
   1465     pub buyer_pubkey: Option<String>,
   1466     #[serde(skip_serializing_if = "Option::is_none")]
   1467     pub buyer_actor_source: Option<String>,
   1468     #[serde(skip_serializing_if = "Option::is_none")]
   1469     pub buyer_custody: Option<String>,
   1470     #[serde(skip_serializing_if = "Option::is_none")]
   1471     pub buyer_write_capable: Option<bool>,
   1472     #[serde(skip_serializing_if = "Option::is_none")]
   1473     pub seller_pubkey: Option<String>,
   1474     pub ready_for_submit: bool,
   1475     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1476     pub items: Vec<OrderDraftItemView>,
   1477     #[serde(skip_serializing_if = "Option::is_none")]
   1478     pub economics: Option<RadrootsOrderEconomics>,
   1479     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1480     pub issues: Vec<OrderIssueView>,
   1481     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1482     pub actions: Vec<String>,
   1483 }
   1484 
   1485 impl OrderNewView {
   1486     pub fn disposition(&self) -> CommandDisposition {
   1487         match self.state.as_str() {
   1488             "error" => CommandDisposition::InternalError,
   1489             _ => CommandDisposition::Success,
   1490         }
   1491     }
   1492 }
   1493 
   1494 #[derive(Debug, Clone, Serialize)]
   1495 pub struct OrderGetView {
   1496     pub state: String,
   1497     pub source: String,
   1498     pub lookup: String,
   1499     #[serde(skip_serializing_if = "Option::is_none")]
   1500     pub order_id: Option<String>,
   1501     #[serde(skip_serializing_if = "Option::is_none")]
   1502     pub file: Option<String>,
   1503     #[serde(skip_serializing_if = "Option::is_none")]
   1504     pub listing_lookup: Option<String>,
   1505     #[serde(skip_serializing_if = "Option::is_none")]
   1506     pub listing_addr: Option<String>,
   1507     #[serde(skip_serializing_if = "Option::is_none")]
   1508     pub listing_event_id: Option<String>,
   1509     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1510     pub listing_relays: Vec<String>,
   1511     #[serde(skip_serializing_if = "Option::is_none")]
   1512     pub buyer_account_id: Option<String>,
   1513     #[serde(skip_serializing_if = "Option::is_none")]
   1514     pub buyer_pubkey: Option<String>,
   1515     #[serde(skip_serializing_if = "Option::is_none")]
   1516     pub buyer_actor_source: Option<String>,
   1517     #[serde(skip_serializing_if = "Option::is_none")]
   1518     pub buyer_custody: Option<String>,
   1519     #[serde(skip_serializing_if = "Option::is_none")]
   1520     pub buyer_write_capable: Option<bool>,
   1521     #[serde(skip_serializing_if = "Option::is_none")]
   1522     pub seller_pubkey: Option<String>,
   1523     pub ready_for_submit: bool,
   1524     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1525     pub items: Vec<OrderDraftItemView>,
   1526     #[serde(skip_serializing_if = "Option::is_none")]
   1527     pub economics: Option<RadrootsOrderEconomics>,
   1528     #[serde(skip_serializing_if = "Option::is_none")]
   1529     pub updated_at_unix: Option<u64>,
   1530     #[serde(skip_serializing_if = "Option::is_none")]
   1531     pub job: Option<OrderJobView>,
   1532     #[serde(skip_serializing_if = "Option::is_none")]
   1533     pub workflow: Option<OrderWorkflowView>,
   1534     #[serde(skip_serializing_if = "Option::is_none")]
   1535     pub reason: Option<String>,
   1536     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1537     pub issues: Vec<OrderIssueView>,
   1538     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1539     pub actions: Vec<String>,
   1540 }
   1541 
   1542 impl OrderGetView {
   1543     pub fn disposition(&self) -> CommandDisposition {
   1544         match self.state.as_str() {
   1545             "error" => CommandDisposition::InternalError,
   1546             _ => CommandDisposition::Success,
   1547         }
   1548     }
   1549 }
   1550 
   1551 #[derive(Debug, Clone, Serialize)]
   1552 pub struct OrderListView {
   1553     pub state: String,
   1554     pub source: String,
   1555     pub count: usize,
   1556     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1557     pub orders: Vec<OrderSummaryView>,
   1558     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1559     pub actions: Vec<String>,
   1560 }
   1561 
   1562 impl OrderListView {
   1563     pub fn disposition(&self) -> CommandDisposition {
   1564         match self.state.as_str() {
   1565             "error" => CommandDisposition::InternalError,
   1566             _ => CommandDisposition::Success,
   1567         }
   1568     }
   1569 }
   1570 
   1571 #[derive(Debug, Clone, Serialize)]
   1572 pub struct OrderAppRecordListView {
   1573     pub state: String,
   1574     pub source: String,
   1575     pub count: usize,
   1576     pub limit: u32,
   1577     pub has_more: bool,
   1578     #[serde(skip_serializing_if = "Option::is_none")]
   1579     pub next_before_change_seq: Option<i64>,
   1580     #[serde(skip_serializing_if = "Option::is_none")]
   1581     pub next_before_seq: Option<i64>,
   1582     pub local_events_db: String,
   1583     pub records: Vec<OrderAppRecordSummaryView>,
   1584     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1585     pub actions: Vec<String>,
   1586 }
   1587 
   1588 impl OrderAppRecordListView {
   1589     pub fn disposition(&self) -> CommandDisposition {
   1590         match self.state.as_str() {
   1591             "error" => CommandDisposition::InternalError,
   1592             _ => CommandDisposition::Success,
   1593         }
   1594     }
   1595 }
   1596 
   1597 #[derive(Debug, Clone, Serialize)]
   1598 pub struct OrderAppRecordSummaryView {
   1599     pub record_id: String,
   1600     pub seq: i64,
   1601     pub change_seq: i64,
   1602     pub superseded_count: usize,
   1603     pub record_kind: String,
   1604     pub status: String,
   1605     pub source_runtime: String,
   1606     #[serde(skip_serializing_if = "Option::is_none")]
   1607     pub owner_account_id: Option<String>,
   1608     #[serde(skip_serializing_if = "Option::is_none")]
   1609     pub owner_pubkey: Option<String>,
   1610     #[serde(skip_serializing_if = "Option::is_none")]
   1611     pub farm_id: Option<String>,
   1612     #[serde(skip_serializing_if = "Option::is_none")]
   1613     pub listing_addr: Option<String>,
   1614     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1615     pub listing_relays: Vec<String>,
   1616     #[serde(skip_serializing_if = "Option::is_none")]
   1617     pub order_id: Option<String>,
   1618     #[serde(skip_serializing_if = "Option::is_none")]
   1619     pub buyer_account_id: Option<String>,
   1620     #[serde(skip_serializing_if = "Option::is_none")]
   1621     pub buyer_pubkey: Option<String>,
   1622     #[serde(skip_serializing_if = "Option::is_none")]
   1623     pub seller_pubkey: Option<String>,
   1624     pub ready_for_submit: bool,
   1625     pub exportable: bool,
   1626     #[serde(skip_serializing_if = "Option::is_none")]
   1627     pub reason: Option<String>,
   1628     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1629     pub actions: Vec<String>,
   1630 }
   1631 
   1632 #[derive(Debug, Clone, Serialize)]
   1633 pub struct OrderAppRecordExportView {
   1634     pub state: String,
   1635     pub source: String,
   1636     pub record_id: String,
   1637     pub dry_run: bool,
   1638     pub file: String,
   1639     pub valid: bool,
   1640     #[serde(skip_serializing_if = "Option::is_none")]
   1641     pub order_id: Option<String>,
   1642     #[serde(skip_serializing_if = "Option::is_none")]
   1643     pub listing_addr: Option<String>,
   1644     #[serde(skip_serializing_if = "Option::is_none")]
   1645     pub listing_event_id: Option<String>,
   1646     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1647     pub listing_relays: Vec<String>,
   1648     #[serde(skip_serializing_if = "Option::is_none")]
   1649     pub buyer_account_id: Option<String>,
   1650     #[serde(skip_serializing_if = "Option::is_none")]
   1651     pub buyer_pubkey: Option<String>,
   1652     #[serde(skip_serializing_if = "Option::is_none")]
   1653     pub buyer_actor_source: Option<String>,
   1654     #[serde(skip_serializing_if = "Option::is_none")]
   1655     pub seller_pubkey: Option<String>,
   1656     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1657     pub issues: Vec<OrderIssueView>,
   1658     #[serde(skip_serializing_if = "Option::is_none")]
   1659     pub reason: Option<String>,
   1660     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1661     pub actions: Vec<String>,
   1662 }
   1663 
   1664 impl OrderAppRecordExportView {
   1665     pub fn disposition(&self) -> CommandDisposition {
   1666         match self.state.as_str() {
   1667             "missing" => CommandDisposition::NotFound,
   1668             "already_submitted" | "conflict" | "invalid" | "stale" | "unsupported" => {
   1669                 CommandDisposition::ValidationFailed
   1670             }
   1671             "error" => CommandDisposition::InternalError,
   1672             _ => CommandDisposition::Success,
   1673         }
   1674     }
   1675 }
   1676 
   1677 #[derive(Debug, Clone, Serialize)]
   1678 pub struct OrderSubmitView {
   1679     pub state: String,
   1680     pub source: String,
   1681     pub order_id: String,
   1682     pub file: String,
   1683     #[serde(skip_serializing_if = "Option::is_none")]
   1684     pub listing_lookup: Option<String>,
   1685     #[serde(skip_serializing_if = "Option::is_none")]
   1686     pub listing_addr: Option<String>,
   1687     #[serde(skip_serializing_if = "Option::is_none")]
   1688     pub listing_event_id: Option<String>,
   1689     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1690     pub listing_relays: Vec<String>,
   1691     #[serde(skip_serializing_if = "Option::is_none")]
   1692     pub buyer_account_id: Option<String>,
   1693     #[serde(skip_serializing_if = "Option::is_none")]
   1694     pub buyer_pubkey: Option<String>,
   1695     #[serde(skip_serializing_if = "Option::is_none")]
   1696     pub buyer_actor_source: Option<String>,
   1697     #[serde(skip_serializing_if = "Option::is_none")]
   1698     pub buyer_custody: Option<String>,
   1699     #[serde(skip_serializing_if = "Option::is_none")]
   1700     pub buyer_write_capable: Option<bool>,
   1701     #[serde(skip_serializing_if = "Option::is_none")]
   1702     pub seller_pubkey: Option<String>,
   1703     #[serde(skip_serializing_if = "Option::is_none")]
   1704     pub event_id: Option<String>,
   1705     #[serde(skip_serializing_if = "Option::is_none")]
   1706     pub event_kind: Option<u32>,
   1707     #[serde(default)]
   1708     pub dry_run: bool,
   1709     #[serde(default)]
   1710     pub deduplicated: bool,
   1711     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1712     pub target_relays: Vec<String>,
   1713     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1714     pub connected_relays: Vec<String>,
   1715     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1716     pub acknowledged_relays: Vec<String>,
   1717     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1718     pub failed_relays: Vec<RelayFailureView>,
   1719     #[serde(skip_serializing_if = "Option::is_none")]
   1720     pub idempotency_key: Option<String>,
   1721     #[serde(skip_serializing_if = "Option::is_none")]
   1722     pub signer_mode: Option<String>,
   1723     #[serde(skip_serializing_if = "Option::is_none")]
   1724     pub reason: Option<String>,
   1725     #[serde(skip_serializing_if = "Option::is_none")]
   1726     pub job: Option<OrderJobView>,
   1727     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1728     pub issues: Vec<OrderIssueView>,
   1729     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1730     pub actions: Vec<String>,
   1731 }
   1732 
   1733 impl OrderSubmitView {
   1734     pub fn disposition(&self) -> CommandDisposition {
   1735         match self.state.as_str() {
   1736             "missing" => CommandDisposition::NotFound,
   1737             "invalid" => CommandDisposition::ValidationFailed,
   1738             "unconfigured" => CommandDisposition::Unconfigured,
   1739             "unavailable" => CommandDisposition::ExternalUnavailable,
   1740             "error" => CommandDisposition::InternalError,
   1741             _ => CommandDisposition::Success,
   1742         }
   1743     }
   1744 }
   1745 
   1746 #[derive(Debug, Clone, Serialize)]
   1747 pub struct OrderRebindView {
   1748     pub state: String,
   1749     pub source: String,
   1750     pub lookup: String,
   1751     pub file: String,
   1752     pub dry_run: bool,
   1753     pub from_order_id: String,
   1754     pub to_order_id: String,
   1755     pub order_id_changed: bool,
   1756     #[serde(skip_serializing_if = "Option::is_none")]
   1757     pub from_buyer_account_id: Option<String>,
   1758     #[serde(skip_serializing_if = "Option::is_none")]
   1759     pub from_buyer_pubkey: Option<String>,
   1760     #[serde(skip_serializing_if = "Option::is_none")]
   1761     pub from_buyer_actor_source: Option<String>,
   1762     pub to_buyer_account_id: String,
   1763     pub to_buyer_pubkey: String,
   1764     pub to_buyer_actor_source: String,
   1765     pub buyer_pubkey_changed: bool,
   1766     pub existing_request_check: String,
   1767     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1768     pub existing_request_event_ids: Vec<String>,
   1769     #[serde(skip_serializing_if = "Option::is_none")]
   1770     pub reason: Option<String>,
   1771     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1772     pub actions: Vec<String>,
   1773 }
   1774 
   1775 impl OrderRebindView {
   1776     pub fn disposition(&self) -> CommandDisposition {
   1777         match self.state.as_str() {
   1778             "missing" => CommandDisposition::NotFound,
   1779             "invalid" => CommandDisposition::ValidationFailed,
   1780             "error" => CommandDisposition::InternalError,
   1781             _ => CommandDisposition::Success,
   1782         }
   1783     }
   1784 }
   1785 
   1786 #[derive(Debug, Clone, Serialize)]
   1787 pub struct OrderDecisionView {
   1788     pub state: String,
   1789     pub source: String,
   1790     pub order_id: String,
   1791     #[serde(skip_serializing_if = "Option::is_none")]
   1792     pub listing_addr: Option<String>,
   1793     #[serde(skip_serializing_if = "Option::is_none")]
   1794     pub buyer_pubkey: Option<String>,
   1795     #[serde(skip_serializing_if = "Option::is_none")]
   1796     pub seller_pubkey: Option<String>,
   1797     pub decision: String,
   1798     #[serde(skip_serializing_if = "Option::is_none")]
   1799     pub request_event_id: Option<String>,
   1800     #[serde(skip_serializing_if = "Option::is_none")]
   1801     pub listing_event_id: Option<String>,
   1802     #[serde(skip_serializing_if = "Option::is_none")]
   1803     pub root_event_id: Option<String>,
   1804     #[serde(skip_serializing_if = "Option::is_none")]
   1805     pub prev_event_id: Option<String>,
   1806     #[serde(skip_serializing_if = "Option::is_none")]
   1807     pub event_id: Option<String>,
   1808     #[serde(skip_serializing_if = "Option::is_none")]
   1809     pub event_kind: Option<u32>,
   1810     #[serde(skip_serializing_if = "Option::is_none")]
   1811     pub inventory: Option<OrderInventoryView>,
   1812     #[serde(default)]
   1813     pub dry_run: bool,
   1814     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1815     pub target_relays: Vec<String>,
   1816     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1817     pub connected_relays: Vec<String>,
   1818     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1819     pub acknowledged_relays: Vec<String>,
   1820     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1821     pub failed_relays: Vec<RelayFailureView>,
   1822     #[serde(default)]
   1823     pub fetched_count: usize,
   1824     #[serde(default)]
   1825     pub decoded_count: usize,
   1826     #[serde(default)]
   1827     pub skipped_count: usize,
   1828     #[serde(skip_serializing_if = "Option::is_none")]
   1829     pub idempotency_key: Option<String>,
   1830     #[serde(skip_serializing_if = "Option::is_none")]
   1831     pub signer_mode: Option<String>,
   1832     #[serde(skip_serializing_if = "Option::is_none")]
   1833     pub reason: Option<String>,
   1834     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1835     pub issues: Vec<OrderIssueView>,
   1836     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1837     pub actions: Vec<String>,
   1838 }
   1839 
   1840 impl OrderDecisionView {
   1841     pub fn disposition(&self) -> CommandDisposition {
   1842         match self.state.as_str() {
   1843             "missing" => CommandDisposition::NotFound,
   1844             "invalid" | "already_decided" | "terminal" => CommandDisposition::ValidationFailed,
   1845             "unconfigured" => CommandDisposition::Unconfigured,
   1846             "unavailable" => CommandDisposition::ExternalUnavailable,
   1847             "error" => CommandDisposition::InternalError,
   1848             _ => CommandDisposition::Success,
   1849         }
   1850     }
   1851 }
   1852 
   1853 #[derive(Debug, Clone, Serialize)]
   1854 pub struct OrderCancellationView {
   1855     pub state: String,
   1856     pub source: String,
   1857     pub order_id: String,
   1858     #[serde(skip_serializing_if = "Option::is_none")]
   1859     pub listing_addr: Option<String>,
   1860     #[serde(skip_serializing_if = "Option::is_none")]
   1861     pub buyer_pubkey: Option<String>,
   1862     #[serde(skip_serializing_if = "Option::is_none")]
   1863     pub seller_pubkey: Option<String>,
   1864     #[serde(skip_serializing_if = "Option::is_none")]
   1865     pub request_event_id: Option<String>,
   1866     #[serde(skip_serializing_if = "Option::is_none")]
   1867     pub decision_event_id: Option<String>,
   1868     #[serde(skip_serializing_if = "Option::is_none")]
   1869     pub root_event_id: Option<String>,
   1870     #[serde(skip_serializing_if = "Option::is_none")]
   1871     pub prev_event_id: Option<String>,
   1872     #[serde(skip_serializing_if = "Option::is_none")]
   1873     pub event_id: Option<String>,
   1874     #[serde(skip_serializing_if = "Option::is_none")]
   1875     pub event_kind: Option<u32>,
   1876     #[serde(skip_serializing_if = "Option::is_none")]
   1877     pub cancellation_reason: Option<String>,
   1878     #[serde(default)]
   1879     pub dry_run: bool,
   1880     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1881     pub target_relays: Vec<String>,
   1882     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1883     pub connected_relays: Vec<String>,
   1884     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1885     pub acknowledged_relays: Vec<String>,
   1886     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1887     pub failed_relays: Vec<RelayFailureView>,
   1888     #[serde(default)]
   1889     pub fetched_count: usize,
   1890     #[serde(default)]
   1891     pub decoded_count: usize,
   1892     #[serde(default)]
   1893     pub skipped_count: usize,
   1894     #[serde(skip_serializing_if = "Option::is_none")]
   1895     pub idempotency_key: Option<String>,
   1896     #[serde(skip_serializing_if = "Option::is_none")]
   1897     pub signer_mode: Option<String>,
   1898     #[serde(skip_serializing_if = "Option::is_none")]
   1899     pub reason: Option<String>,
   1900     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1901     pub issues: Vec<OrderIssueView>,
   1902     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1903     pub actions: Vec<String>,
   1904 }
   1905 
   1906 impl OrderCancellationView {
   1907     pub fn disposition(&self) -> CommandDisposition {
   1908         match self.state.as_str() {
   1909             "missing" => CommandDisposition::NotFound,
   1910             "invalid" | "declined" | "fulfilled" | "terminal" | "forked" => {
   1911                 CommandDisposition::ValidationFailed
   1912             }
   1913             "unconfigured" => CommandDisposition::Unconfigured,
   1914             "unavailable" => CommandDisposition::ExternalUnavailable,
   1915             "error" => CommandDisposition::InternalError,
   1916             _ => CommandDisposition::Success,
   1917         }
   1918     }
   1919 }
   1920 
   1921 #[derive(Debug, Clone, Serialize)]
   1922 pub struct OrderRevisionProposalView {
   1923     pub state: String,
   1924     pub source: String,
   1925     pub order_id: String,
   1926     #[serde(skip_serializing_if = "Option::is_none")]
   1927     pub revision_id: Option<String>,
   1928     #[serde(skip_serializing_if = "Option::is_none")]
   1929     pub listing_addr: Option<String>,
   1930     #[serde(skip_serializing_if = "Option::is_none")]
   1931     pub buyer_pubkey: Option<String>,
   1932     #[serde(skip_serializing_if = "Option::is_none")]
   1933     pub seller_pubkey: Option<String>,
   1934     #[serde(skip_serializing_if = "Option::is_none")]
   1935     pub request_event_id: Option<String>,
   1936     #[serde(skip_serializing_if = "Option::is_none")]
   1937     pub decision_event_id: Option<String>,
   1938     #[serde(skip_serializing_if = "Option::is_none")]
   1939     pub root_event_id: Option<String>,
   1940     #[serde(skip_serializing_if = "Option::is_none")]
   1941     pub prev_event_id: Option<String>,
   1942     #[serde(skip_serializing_if = "Option::is_none")]
   1943     pub event_id: Option<String>,
   1944     #[serde(skip_serializing_if = "Option::is_none")]
   1945     pub event_kind: Option<u32>,
   1946     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1947     pub items: Vec<OrderDraftItemView>,
   1948     #[serde(skip_serializing_if = "Option::is_none")]
   1949     pub economics: Option<RadrootsOrderEconomics>,
   1950     #[serde(skip_serializing_if = "Option::is_none")]
   1951     pub inventory: Option<OrderInventoryView>,
   1952     #[serde(default)]
   1953     pub dry_run: bool,
   1954     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1955     pub target_relays: Vec<String>,
   1956     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1957     pub connected_relays: Vec<String>,
   1958     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1959     pub acknowledged_relays: Vec<String>,
   1960     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1961     pub failed_relays: Vec<RelayFailureView>,
   1962     #[serde(default)]
   1963     pub fetched_count: usize,
   1964     #[serde(default)]
   1965     pub decoded_count: usize,
   1966     #[serde(default)]
   1967     pub skipped_count: usize,
   1968     #[serde(skip_serializing_if = "Option::is_none")]
   1969     pub idempotency_key: Option<String>,
   1970     #[serde(skip_serializing_if = "Option::is_none")]
   1971     pub signer_mode: Option<String>,
   1972     #[serde(skip_serializing_if = "Option::is_none")]
   1973     pub reason: Option<String>,
   1974     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1975     pub issues: Vec<OrderIssueView>,
   1976     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   1977     pub actions: Vec<String>,
   1978 }
   1979 
   1980 impl OrderRevisionProposalView {
   1981     pub fn disposition(&self) -> CommandDisposition {
   1982         match self.state.as_str() {
   1983             "missing" => CommandDisposition::NotFound,
   1984             "invalid" | "requested" | "declined" | "order_declined" | "fulfilled" | "terminal"
   1985             | "forked" => CommandDisposition::ValidationFailed,
   1986             "unconfigured" => CommandDisposition::Unconfigured,
   1987             "unavailable" => CommandDisposition::ExternalUnavailable,
   1988             "error" => CommandDisposition::InternalError,
   1989             _ => CommandDisposition::Success,
   1990         }
   1991     }
   1992 }
   1993 
   1994 #[derive(Debug, Clone, Serialize)]
   1995 pub struct OrderRevisionDecisionView {
   1996     pub state: String,
   1997     pub source: String,
   1998     pub order_id: String,
   1999     #[serde(skip_serializing_if = "Option::is_none")]
   2000     pub revision_id: Option<String>,
   2001     #[serde(skip_serializing_if = "Option::is_none")]
   2002     pub decision: Option<String>,
   2003     #[serde(skip_serializing_if = "Option::is_none")]
   2004     pub listing_addr: Option<String>,
   2005     #[serde(skip_serializing_if = "Option::is_none")]
   2006     pub buyer_pubkey: Option<String>,
   2007     #[serde(skip_serializing_if = "Option::is_none")]
   2008     pub seller_pubkey: Option<String>,
   2009     #[serde(skip_serializing_if = "Option::is_none")]
   2010     pub request_event_id: Option<String>,
   2011     #[serde(skip_serializing_if = "Option::is_none")]
   2012     pub decision_event_id: Option<String>,
   2013     #[serde(skip_serializing_if = "Option::is_none")]
   2014     pub agreement_event_id: Option<String>,
   2015     #[serde(skip_serializing_if = "Option::is_none")]
   2016     pub root_event_id: Option<String>,
   2017     #[serde(skip_serializing_if = "Option::is_none")]
   2018     pub prev_event_id: Option<String>,
   2019     #[serde(skip_serializing_if = "Option::is_none")]
   2020     pub event_id: Option<String>,
   2021     #[serde(skip_serializing_if = "Option::is_none")]
   2022     pub event_kind: Option<u32>,
   2023     #[serde(skip_serializing_if = "Option::is_none")]
   2024     pub economics: Option<RadrootsOrderEconomics>,
   2025     #[serde(skip_serializing_if = "Option::is_none")]
   2026     pub inventory: Option<OrderInventoryView>,
   2027     #[serde(default)]
   2028     pub dry_run: bool,
   2029     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2030     pub target_relays: Vec<String>,
   2031     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2032     pub connected_relays: Vec<String>,
   2033     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2034     pub acknowledged_relays: Vec<String>,
   2035     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2036     pub failed_relays: Vec<RelayFailureView>,
   2037     #[serde(default)]
   2038     pub fetched_count: usize,
   2039     #[serde(default)]
   2040     pub decoded_count: usize,
   2041     #[serde(default)]
   2042     pub skipped_count: usize,
   2043     #[serde(skip_serializing_if = "Option::is_none")]
   2044     pub idempotency_key: Option<String>,
   2045     #[serde(skip_serializing_if = "Option::is_none")]
   2046     pub signer_mode: Option<String>,
   2047     #[serde(skip_serializing_if = "Option::is_none")]
   2048     pub reason: Option<String>,
   2049     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2050     pub issues: Vec<OrderIssueView>,
   2051     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2052     pub actions: Vec<String>,
   2053 }
   2054 
   2055 impl OrderRevisionDecisionView {
   2056     pub fn disposition(&self) -> CommandDisposition {
   2057         match self.state.as_str() {
   2058             "missing" => CommandDisposition::NotFound,
   2059             "invalid" | "requested" | "order_declined" | "fulfilled" | "terminal" | "forked" => {
   2060                 CommandDisposition::ValidationFailed
   2061             }
   2062             "unconfigured" => CommandDisposition::Unconfigured,
   2063             "unavailable" => CommandDisposition::ExternalUnavailable,
   2064             "error" => CommandDisposition::InternalError,
   2065             _ => CommandDisposition::Success,
   2066         }
   2067     }
   2068 }
   2069 
   2070 #[derive(Debug, Clone, Serialize)]
   2071 pub struct OrderStatusView {
   2072     pub state: String,
   2073     pub source: String,
   2074     pub order_id: String,
   2075     pub actor_context_source: String,
   2076     #[serde(skip_serializing_if = "Option::is_none")]
   2077     pub request_event_id: Option<String>,
   2078     #[serde(skip_serializing_if = "Option::is_none")]
   2079     pub decision_event_id: Option<String>,
   2080     #[serde(skip_serializing_if = "Option::is_none")]
   2081     pub agreement_event_id: Option<String>,
   2082     #[serde(skip_serializing_if = "Option::is_none")]
   2083     pub listing_event_id: Option<String>,
   2084     #[serde(skip_serializing_if = "Option::is_none")]
   2085     pub listing_addr: Option<String>,
   2086     #[serde(skip_serializing_if = "Option::is_none")]
   2087     pub buyer_pubkey: Option<String>,
   2088     #[serde(skip_serializing_if = "Option::is_none")]
   2089     pub seller_pubkey: Option<String>,
   2090     #[serde(skip_serializing_if = "Option::is_none")]
   2091     pub economics: Option<RadrootsOrderEconomics>,
   2092     #[serde(skip_serializing_if = "Option::is_none")]
   2093     pub last_event_id: Option<String>,
   2094     #[serde(skip_serializing_if = "Option::is_none")]
   2095     pub revision: Option<OrderStatusRevisionView>,
   2096     #[serde(skip_serializing_if = "Option::is_none")]
   2097     pub inventory: Option<OrderInventoryView>,
   2098     pub lifecycle: Option<OrderStatusLifecycleView>,
   2099     #[serde(skip_serializing_if = "Option::is_none")]
   2100     pub sdk_receipt: Option<OrderStatusSdkReceiptView>,
   2101     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2102     pub reducer_issues: Vec<OrderIssueView>,
   2103     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2104     pub target_relays: Vec<String>,
   2105     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2106     pub connected_relays: Vec<String>,
   2107     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2108     pub failed_relays: Vec<RelayFailureView>,
   2109     #[serde(default)]
   2110     pub fetched_count: usize,
   2111     #[serde(default)]
   2112     pub decoded_count: usize,
   2113     #[serde(default)]
   2114     pub skipped_count: usize,
   2115     #[serde(skip_serializing_if = "Option::is_none")]
   2116     pub reason: Option<String>,
   2117     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2118     pub actions: Vec<String>,
   2119 }
   2120 
   2121 #[derive(Debug, Clone, Serialize)]
   2122 pub struct OrderStatusSdkReceiptView {
   2123     pub next_action: String,
   2124     pub evidence: OrderStatusEvidenceSummaryView,
   2125     pub eligibility: OrderStatusEligibilityView,
   2126 }
   2127 
   2128 #[derive(Debug, Clone, Serialize)]
   2129 pub struct OrderStatusEvidenceSummaryView {
   2130     pub event_count: usize,
   2131     pub limit_applied: u32,
   2132     pub has_request: bool,
   2133     pub has_decision: bool,
   2134     pub has_agreement: bool,
   2135     pub has_pending_revision: bool,
   2136     pub has_cancellation: bool,
   2137     pub has_issues: bool,
   2138 }
   2139 
   2140 #[derive(Debug, Clone, Serialize)]
   2141 pub struct OrderStatusEligibilityView {
   2142     pub can_decide: bool,
   2143     pub can_propose_revision: bool,
   2144     pub can_decide_revision: bool,
   2145     pub can_cancel: bool,
   2146 }
   2147 
   2148 #[derive(Debug, Clone, Serialize)]
   2149 pub struct OrderStatusRevisionView {
   2150     pub state: String,
   2151     #[serde(skip_serializing_if = "Option::is_none")]
   2152     pub revision_id: Option<String>,
   2153     #[serde(skip_serializing_if = "Option::is_none")]
   2154     pub proposal_event_id: Option<String>,
   2155     #[serde(skip_serializing_if = "Option::is_none")]
   2156     pub decision_event_id: Option<String>,
   2157     #[serde(skip_serializing_if = "Option::is_none")]
   2158     pub root_event_id: Option<String>,
   2159     #[serde(skip_serializing_if = "Option::is_none")]
   2160     pub prev_event_id: Option<String>,
   2161     #[serde(skip_serializing_if = "Option::is_none")]
   2162     pub agreement_event_id: Option<String>,
   2163     #[serde(skip_serializing_if = "Option::is_none")]
   2164     pub reason: Option<String>,
   2165 }
   2166 
   2167 #[derive(Debug, Clone, Serialize)]
   2168 pub struct OrderStatusLifecycleView {
   2169     pub phase: String,
   2170     #[serde(default)]
   2171     pub terminal: bool,
   2172     #[serde(skip_serializing_if = "Option::is_none")]
   2173     pub event_id: Option<String>,
   2174     #[serde(skip_serializing_if = "Option::is_none")]
   2175     pub root_event_id: Option<String>,
   2176     #[serde(skip_serializing_if = "Option::is_none")]
   2177     pub prev_event_id: Option<String>,
   2178     #[serde(skip_serializing_if = "Option::is_none")]
   2179     pub cancellation: Option<OrderStatusLifecycleCancellationView>,
   2180     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2181     pub issues: Vec<OrderIssueView>,
   2182 }
   2183 
   2184 #[derive(Debug, Clone, Serialize)]
   2185 pub struct OrderStatusLifecycleCancellationView {
   2186     pub event_id: String,
   2187     #[serde(skip_serializing_if = "Option::is_none")]
   2188     pub root_event_id: Option<String>,
   2189     #[serde(skip_serializing_if = "Option::is_none")]
   2190     pub prev_event_id: Option<String>,
   2191     #[serde(skip_serializing_if = "Option::is_none")]
   2192     pub reason: Option<String>,
   2193 }
   2194 
   2195 #[derive(Debug, Clone, Serialize)]
   2196 pub struct OrderInventoryView {
   2197     pub state: String,
   2198     #[serde(skip_serializing_if = "Option::is_none")]
   2199     pub listing_event_id: Option<String>,
   2200     #[serde(default)]
   2201     pub commitment_valid: bool,
   2202     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2203     pub bins: Vec<OrderInventoryBinView>,
   2204     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2205     pub issues: Vec<OrderIssueView>,
   2206 }
   2207 
   2208 #[derive(Debug, Clone, Serialize)]
   2209 pub struct OrderInventoryBinView {
   2210     pub bin_id: String,
   2211     #[serde(default)]
   2212     pub committed_count: u64,
   2213     #[serde(skip_serializing_if = "Option::is_none")]
   2214     pub available_count: Option<u64>,
   2215     #[serde(skip_serializing_if = "Option::is_none")]
   2216     pub remaining_count: Option<u64>,
   2217     #[serde(default)]
   2218     pub over_reserved: bool,
   2219 }
   2220 
   2221 impl OrderStatusView {
   2222     pub fn disposition(&self) -> CommandDisposition {
   2223         match self.state.as_str() {
   2224             "unconfigured" => CommandDisposition::Unconfigured,
   2225             "unavailable" => CommandDisposition::ExternalUnavailable,
   2226             "error" => CommandDisposition::InternalError,
   2227             _ => CommandDisposition::Success,
   2228         }
   2229     }
   2230 }
   2231 
   2232 #[derive(Debug, Clone, Serialize)]
   2233 pub struct OrderWorkflowView {
   2234     pub state: String,
   2235     pub source: String,
   2236     pub order_id: String,
   2237     #[serde(skip_serializing_if = "Option::is_none")]
   2238     pub listing_addr: Option<String>,
   2239     #[serde(skip_serializing_if = "Option::is_none")]
   2240     pub validated_listing_event_id: Option<String>,
   2241     #[serde(skip_serializing_if = "Option::is_none")]
   2242     pub root_event_id: Option<String>,
   2243     #[serde(skip_serializing_if = "Option::is_none")]
   2244     pub last_event_id: Option<String>,
   2245     #[serde(skip_serializing_if = "Option::is_none")]
   2246     pub reason: Option<String>,
   2247 }
   2248 
   2249 #[derive(Debug, Clone, Serialize)]
   2250 pub struct OrderEventListView {
   2251     pub state: String,
   2252     pub source: String,
   2253     pub actor_context_source: String,
   2254     #[serde(skip_serializing_if = "Option::is_none")]
   2255     pub seller_pubkey: Option<String>,
   2256     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2257     pub target_relays: Vec<String>,
   2258     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2259     pub connected_relays: Vec<String>,
   2260     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2261     pub failed_relays: Vec<RelayFailureView>,
   2262     #[serde(default)]
   2263     pub fetched_count: usize,
   2264     #[serde(default)]
   2265     pub decoded_count: usize,
   2266     #[serde(default)]
   2267     pub skipped_count: usize,
   2268     pub count: usize,
   2269     #[serde(skip_serializing_if = "Option::is_none")]
   2270     pub reason: Option<String>,
   2271     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2272     pub orders: Vec<OrderEventListEntryView>,
   2273     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2274     pub actions: Vec<String>,
   2275 }
   2276 
   2277 impl OrderEventListView {
   2278     pub fn disposition(&self) -> CommandDisposition {
   2279         match self.state.as_str() {
   2280             "unconfigured" => CommandDisposition::Unconfigured,
   2281             "unavailable" => CommandDisposition::ExternalUnavailable,
   2282             "error" => CommandDisposition::InternalError,
   2283             _ => CommandDisposition::Success,
   2284         }
   2285     }
   2286 }
   2287 
   2288 #[derive(Debug, Clone, Serialize)]
   2289 pub struct OrderEventListEntryView {
   2290     pub id: String,
   2291     pub state: String,
   2292     #[serde(skip_serializing_if = "Option::is_none")]
   2293     pub event_id: Option<String>,
   2294     #[serde(skip_serializing_if = "Option::is_none")]
   2295     pub event_kind: Option<u32>,
   2296     #[serde(skip_serializing_if = "Option::is_none")]
   2297     pub listing_lookup: Option<String>,
   2298     #[serde(skip_serializing_if = "Option::is_none")]
   2299     pub listing_addr: Option<String>,
   2300     #[serde(skip_serializing_if = "Option::is_none")]
   2301     pub listing_event_id: Option<String>,
   2302     #[serde(skip_serializing_if = "Option::is_none")]
   2303     pub buyer_account_id: Option<String>,
   2304     #[serde(skip_serializing_if = "Option::is_none")]
   2305     pub buyer_pubkey: Option<String>,
   2306     #[serde(skip_serializing_if = "Option::is_none")]
   2307     pub seller_pubkey: Option<String>,
   2308     #[serde(skip_serializing_if = "Option::is_none")]
   2309     pub item_count: Option<usize>,
   2310     #[serde(skip_serializing_if = "Option::is_none")]
   2311     pub created_at_unix: Option<u64>,
   2312     #[serde(skip_serializing_if = "Option::is_none")]
   2313     pub submitted_at_unix: Option<u64>,
   2314     pub updated_at_unix: u64,
   2315     #[serde(skip_serializing_if = "Option::is_none")]
   2316     pub job: Option<OrderJobView>,
   2317     #[serde(skip_serializing_if = "Option::is_none")]
   2318     pub workflow: Option<OrderWorkflowView>,
   2319     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2320     pub issues: Vec<OrderIssueView>,
   2321 }
   2322 
   2323 #[derive(Debug, Clone, Serialize)]
   2324 pub struct OrderSummaryView {
   2325     pub id: String,
   2326     pub state: String,
   2327     pub ready_for_submit: bool,
   2328     pub file: String,
   2329     #[serde(skip_serializing_if = "Option::is_none")]
   2330     pub listing_lookup: Option<String>,
   2331     #[serde(skip_serializing_if = "Option::is_none")]
   2332     pub listing_addr: Option<String>,
   2333     #[serde(skip_serializing_if = "Option::is_none")]
   2334     pub listing_event_id: Option<String>,
   2335     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2336     pub listing_relays: Vec<String>,
   2337     #[serde(skip_serializing_if = "Option::is_none")]
   2338     pub buyer_account_id: Option<String>,
   2339     #[serde(skip_serializing_if = "Option::is_none")]
   2340     pub buyer_pubkey: Option<String>,
   2341     #[serde(skip_serializing_if = "Option::is_none")]
   2342     pub buyer_actor_source: Option<String>,
   2343     #[serde(skip_serializing_if = "Option::is_none")]
   2344     pub buyer_custody: Option<String>,
   2345     #[serde(skip_serializing_if = "Option::is_none")]
   2346     pub buyer_write_capable: Option<bool>,
   2347     pub item_count: usize,
   2348     #[serde(skip_serializing_if = "Option::is_none")]
   2349     pub economics: Option<RadrootsOrderEconomics>,
   2350     pub updated_at_unix: u64,
   2351     #[serde(skip_serializing_if = "Option::is_none")]
   2352     pub job: Option<OrderJobView>,
   2353     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2354     pub issues: Vec<OrderIssueView>,
   2355 }
   2356 
   2357 #[derive(Debug, Clone, Serialize)]
   2358 pub struct OrderDraftItemView {
   2359     pub bin_id: String,
   2360     pub bin_count: u32,
   2361 }
   2362 
   2363 #[derive(Debug, Clone, Serialize)]
   2364 pub struct OrderIssueView {
   2365     pub code: String,
   2366     pub field: String,
   2367     pub message: String,
   2368     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2369     pub event_ids: Vec<String>,
   2370 }
   2371 
   2372 #[derive(Debug, Clone, Serialize)]
   2373 pub struct OrderJobView {
   2374     pub job_id: String,
   2375     pub state: String,
   2376     #[serde(skip_serializing_if = "Option::is_none")]
   2377     pub command: Option<String>,
   2378     #[serde(skip_serializing_if = "Option::is_none")]
   2379     pub signer_mode: Option<String>,
   2380     #[serde(skip_serializing_if = "Option::is_none")]
   2381     pub event_id: Option<String>,
   2382     #[serde(skip_serializing_if = "Option::is_none")]
   2383     pub event_addr: Option<String>,
   2384     #[serde(skip_serializing_if = "Option::is_none")]
   2385     pub reason: Option<String>,
   2386 }
   2387 
   2388 #[derive(Debug, Clone, Serialize)]
   2389 pub struct ListingNewView {
   2390     pub state: String,
   2391     pub source: String,
   2392     pub file: String,
   2393     pub listing_id: String,
   2394     #[serde(skip_serializing_if = "Option::is_none")]
   2395     pub key: Option<String>,
   2396     #[serde(skip_serializing_if = "Option::is_none")]
   2397     pub seller_account_id: Option<String>,
   2398     #[serde(skip_serializing_if = "Option::is_none")]
   2399     pub seller_pubkey: Option<String>,
   2400     #[serde(skip_serializing_if = "Option::is_none")]
   2401     pub seller_actor_source: Option<String>,
   2402     #[serde(skip_serializing_if = "Option::is_none")]
   2403     pub farm_d_tag: Option<String>,
   2404     #[serde(skip_serializing_if = "Option::is_none")]
   2405     pub delivery_method: Option<String>,
   2406     #[serde(skip_serializing_if = "Option::is_none")]
   2407     pub location_primary: Option<String>,
   2408     #[serde(skip_serializing_if = "Option::is_none")]
   2409     pub reason: Option<String>,
   2410     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2411     pub actions: Vec<String>,
   2412 }
   2413 
   2414 impl ListingNewView {
   2415     pub fn disposition(&self) -> CommandDisposition {
   2416         match self.state.as_str() {
   2417             "unconfigured" => CommandDisposition::Unconfigured,
   2418             "error" => CommandDisposition::InternalError,
   2419             _ => CommandDisposition::Success,
   2420         }
   2421     }
   2422 }
   2423 
   2424 #[derive(Debug, Clone, Serialize)]
   2425 pub struct ListingValidateView {
   2426     pub state: String,
   2427     pub source: String,
   2428     pub file: String,
   2429     pub valid: bool,
   2430     #[serde(skip_serializing_if = "Option::is_none")]
   2431     pub listing_id: Option<String>,
   2432     #[serde(skip_serializing_if = "Option::is_none")]
   2433     pub seller_account_id: Option<String>,
   2434     #[serde(skip_serializing_if = "Option::is_none")]
   2435     pub seller_pubkey: Option<String>,
   2436     #[serde(skip_serializing_if = "Option::is_none")]
   2437     pub seller_actor_source: Option<String>,
   2438     #[serde(skip_serializing_if = "Option::is_none")]
   2439     pub farm_d_tag: Option<String>,
   2440     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2441     pub issues: Vec<ListingValidationIssueView>,
   2442     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2443     pub actions: Vec<String>,
   2444 }
   2445 
   2446 #[derive(Debug, Clone, Serialize)]
   2447 pub struct ListingValidationIssueView {
   2448     pub field: String,
   2449     pub message: String,
   2450     #[serde(skip_serializing_if = "Option::is_none")]
   2451     pub line: Option<usize>,
   2452 }
   2453 
   2454 #[derive(Debug, Clone, Serialize)]
   2455 pub struct ListingListView {
   2456     pub state: String,
   2457     pub source: String,
   2458     pub count: usize,
   2459     pub draft_dir: String,
   2460     pub listings: Vec<ListingSummaryView>,
   2461     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2462     pub actions: Vec<String>,
   2463 }
   2464 
   2465 impl ListingListView {
   2466     pub fn disposition(&self) -> CommandDisposition {
   2467         match self.state.as_str() {
   2468             "error" => CommandDisposition::InternalError,
   2469             _ => CommandDisposition::Success,
   2470         }
   2471     }
   2472 }
   2473 
   2474 #[derive(Debug, Clone, Serialize)]
   2475 pub struct ListingAppRecordListView {
   2476     pub state: String,
   2477     pub source: String,
   2478     pub count: usize,
   2479     pub limit: u32,
   2480     pub has_more: bool,
   2481     #[serde(skip_serializing_if = "Option::is_none")]
   2482     pub next_before_change_seq: Option<i64>,
   2483     #[serde(skip_serializing_if = "Option::is_none")]
   2484     pub next_before_seq: Option<i64>,
   2485     pub local_events_db: String,
   2486     pub records: Vec<ListingAppRecordSummaryView>,
   2487     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2488     pub actions: Vec<String>,
   2489 }
   2490 
   2491 impl ListingAppRecordListView {
   2492     pub fn disposition(&self) -> CommandDisposition {
   2493         match self.state.as_str() {
   2494             "error" => CommandDisposition::InternalError,
   2495             _ => CommandDisposition::Success,
   2496         }
   2497     }
   2498 }
   2499 
   2500 #[derive(Debug, Clone, Serialize)]
   2501 pub struct ListingAppRecordSummaryView {
   2502     pub record_id: String,
   2503     pub seq: i64,
   2504     pub change_seq: i64,
   2505     pub superseded_count: usize,
   2506     pub record_kind: String,
   2507     pub status: String,
   2508     pub source_runtime: String,
   2509     #[serde(skip_serializing_if = "Option::is_none")]
   2510     pub owner_account_id: Option<String>,
   2511     #[serde(skip_serializing_if = "Option::is_none")]
   2512     pub owner_pubkey: Option<String>,
   2513     #[serde(skip_serializing_if = "Option::is_none")]
   2514     pub farm_id: Option<String>,
   2515     #[serde(skip_serializing_if = "Option::is_none")]
   2516     pub listing_addr: Option<String>,
   2517     #[serde(skip_serializing_if = "Option::is_none")]
   2518     pub listing_id: Option<String>,
   2519     #[serde(skip_serializing_if = "Option::is_none")]
   2520     pub title: Option<String>,
   2521     pub exportable: bool,
   2522     #[serde(skip_serializing_if = "Option::is_none")]
   2523     pub reason: Option<String>,
   2524     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2525     pub actions: Vec<String>,
   2526 }
   2527 
   2528 #[derive(Debug, Clone, Serialize)]
   2529 pub struct ListingAppRecordExportView {
   2530     pub state: String,
   2531     pub source: String,
   2532     pub record_id: String,
   2533     pub dry_run: bool,
   2534     pub file: String,
   2535     pub valid: bool,
   2536     #[serde(skip_serializing_if = "Option::is_none")]
   2537     pub listing_id: Option<String>,
   2538     #[serde(skip_serializing_if = "Option::is_none")]
   2539     pub listing_addr: Option<String>,
   2540     #[serde(skip_serializing_if = "Option::is_none")]
   2541     pub seller_account_id: Option<String>,
   2542     #[serde(skip_serializing_if = "Option::is_none")]
   2543     pub seller_pubkey: Option<String>,
   2544     #[serde(skip_serializing_if = "Option::is_none")]
   2545     pub seller_actor_source: Option<String>,
   2546     #[serde(skip_serializing_if = "Option::is_none")]
   2547     pub farm_d_tag: Option<String>,
   2548     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2549     pub issues: Vec<ListingValidationIssueView>,
   2550     #[serde(skip_serializing_if = "Option::is_none")]
   2551     pub reason: Option<String>,
   2552     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2553     pub actions: Vec<String>,
   2554 }
   2555 
   2556 impl ListingAppRecordExportView {
   2557     pub fn disposition(&self) -> CommandDisposition {
   2558         match self.state.as_str() {
   2559             "missing" => CommandDisposition::NotFound,
   2560             "invalid" | "stale" | "unsupported" => CommandDisposition::ValidationFailed,
   2561             "error" => CommandDisposition::InternalError,
   2562             _ => CommandDisposition::Success,
   2563         }
   2564     }
   2565 }
   2566 
   2567 #[derive(Debug, Clone, Serialize)]
   2568 pub struct ListingSummaryView {
   2569     pub id: String,
   2570     pub state: String,
   2571     pub file: String,
   2572     #[serde(skip_serializing_if = "Option::is_none")]
   2573     pub product_key: Option<String>,
   2574     #[serde(skip_serializing_if = "Option::is_none")]
   2575     pub title: Option<String>,
   2576     #[serde(skip_serializing_if = "Option::is_none")]
   2577     pub category: Option<String>,
   2578     #[serde(skip_serializing_if = "Option::is_none")]
   2579     pub seller_account_id: Option<String>,
   2580     #[serde(skip_serializing_if = "Option::is_none")]
   2581     pub seller_pubkey: Option<String>,
   2582     #[serde(skip_serializing_if = "Option::is_none")]
   2583     pub seller_actor_source: Option<String>,
   2584     #[serde(skip_serializing_if = "Option::is_none")]
   2585     pub farm_d_tag: Option<String>,
   2586     #[serde(skip_serializing_if = "Option::is_none")]
   2587     pub location_primary: Option<String>,
   2588     pub updated_at_unix: u64,
   2589     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2590     pub issues: Vec<ListingValidationIssueView>,
   2591 }
   2592 
   2593 #[derive(Debug, Clone, Serialize)]
   2594 pub struct SellAddView {
   2595     pub state: String,
   2596     pub source: String,
   2597     pub file: String,
   2598     #[serde(skip_serializing_if = "Option::is_none")]
   2599     pub product_key: Option<String>,
   2600     #[serde(skip_serializing_if = "Option::is_none")]
   2601     pub title: Option<String>,
   2602     #[serde(skip_serializing_if = "Option::is_none")]
   2603     pub offer: Option<String>,
   2604     #[serde(skip_serializing_if = "Option::is_none")]
   2605     pub price: Option<String>,
   2606     #[serde(skip_serializing_if = "Option::is_none")]
   2607     pub stock: Option<String>,
   2608     #[serde(skip_serializing_if = "Option::is_none")]
   2609     pub farm_name: Option<String>,
   2610     #[serde(skip_serializing_if = "Option::is_none")]
   2611     pub delivery_method: Option<String>,
   2612     #[serde(skip_serializing_if = "Option::is_none")]
   2613     pub location_primary: Option<String>,
   2614     #[serde(skip_serializing_if = "Option::is_none")]
   2615     pub reason: Option<String>,
   2616     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2617     pub actions: Vec<String>,
   2618 }
   2619 
   2620 impl SellAddView {
   2621     pub fn disposition(&self) -> CommandDisposition {
   2622         match self.state.as_str() {
   2623             "unconfigured" => CommandDisposition::Unconfigured,
   2624             "error" => CommandDisposition::InternalError,
   2625             _ => CommandDisposition::Success,
   2626         }
   2627     }
   2628 }
   2629 
   2630 #[derive(Debug, Clone, Serialize)]
   2631 pub struct SellShowView {
   2632     pub state: String,
   2633     pub source: String,
   2634     pub file: String,
   2635     #[serde(skip_serializing_if = "Option::is_none")]
   2636     pub product_key: Option<String>,
   2637     #[serde(skip_serializing_if = "Option::is_none")]
   2638     pub title: Option<String>,
   2639     #[serde(skip_serializing_if = "Option::is_none")]
   2640     pub category: Option<String>,
   2641     #[serde(skip_serializing_if = "Option::is_none")]
   2642     pub offer: Option<String>,
   2643     #[serde(skip_serializing_if = "Option::is_none")]
   2644     pub price: Option<String>,
   2645     #[serde(skip_serializing_if = "Option::is_none")]
   2646     pub stock: Option<String>,
   2647     #[serde(skip_serializing_if = "Option::is_none")]
   2648     pub delivery_method: Option<String>,
   2649     #[serde(skip_serializing_if = "Option::is_none")]
   2650     pub location_primary: Option<String>,
   2651     #[serde(skip_serializing_if = "Option::is_none")]
   2652     pub reason: Option<String>,
   2653     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2654     pub actions: Vec<String>,
   2655 }
   2656 
   2657 impl SellShowView {
   2658     pub fn disposition(&self) -> CommandDisposition {
   2659         match self.state.as_str() {
   2660             "error" => CommandDisposition::InternalError,
   2661             _ => CommandDisposition::Success,
   2662         }
   2663     }
   2664 }
   2665 
   2666 #[derive(Debug, Clone, Serialize)]
   2667 pub struct SellCheckView {
   2668     pub state: String,
   2669     pub source: String,
   2670     pub file: String,
   2671     pub valid: bool,
   2672     #[serde(skip_serializing_if = "Option::is_none")]
   2673     pub product_key: Option<String>,
   2674     #[serde(skip_serializing_if = "Option::is_none")]
   2675     pub seller_pubkey: Option<String>,
   2676     #[serde(skip_serializing_if = "Option::is_none")]
   2677     pub farm_ref: Option<String>,
   2678     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2679     pub issues: Vec<ListingValidationIssueView>,
   2680     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2681     pub actions: Vec<String>,
   2682 }
   2683 
   2684 impl SellCheckView {
   2685     pub fn disposition(&self) -> CommandDisposition {
   2686         CommandDisposition::Success
   2687     }
   2688 }
   2689 
   2690 #[derive(Debug, Clone, Serialize)]
   2691 pub struct SellMutationView {
   2692     pub state: String,
   2693     pub operation: String,
   2694     pub source: String,
   2695     pub file: String,
   2696     #[serde(skip_serializing_if = "Option::is_none")]
   2697     pub product_key: Option<String>,
   2698     pub listing_addr: String,
   2699     #[serde(default)]
   2700     pub dry_run: bool,
   2701     #[serde(default)]
   2702     pub deduplicated: bool,
   2703     #[serde(skip_serializing_if = "Option::is_none")]
   2704     pub publish_transport: Option<String>,
   2705     #[serde(skip_serializing_if = "Option::is_none")]
   2706     pub job_id: Option<String>,
   2707     #[serde(skip_serializing_if = "Option::is_none")]
   2708     pub job_status: Option<String>,
   2709     #[serde(skip_serializing_if = "Option::is_none")]
   2710     pub event_id: Option<String>,
   2711     #[serde(skip_serializing_if = "Option::is_none")]
   2712     pub reason: Option<String>,
   2713     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2714     pub actions: Vec<String>,
   2715 }
   2716 
   2717 impl SellMutationView {
   2718     pub fn disposition(&self) -> CommandDisposition {
   2719         match self.state.as_str() {
   2720             "unconfigured" => CommandDisposition::Unconfigured,
   2721             "unavailable" => CommandDisposition::ExternalUnavailable,
   2722             "error" => CommandDisposition::InternalError,
   2723             _ => CommandDisposition::Success,
   2724         }
   2725     }
   2726 }
   2727 
   2728 #[derive(Debug, Clone, Serialize)]
   2729 pub struct SellDraftMutationView {
   2730     pub state: String,
   2731     pub operation: String,
   2732     pub source: String,
   2733     pub file: String,
   2734     #[serde(skip_serializing_if = "Option::is_none")]
   2735     pub product_key: Option<String>,
   2736     pub changed_label: String,
   2737     pub changed_value: String,
   2738     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2739     pub actions: Vec<String>,
   2740 }
   2741 
   2742 impl SellDraftMutationView {
   2743     pub fn disposition(&self) -> CommandDisposition {
   2744         match self.state.as_str() {
   2745             "error" => CommandDisposition::InternalError,
   2746             _ => CommandDisposition::Success,
   2747         }
   2748     }
   2749 }
   2750 
   2751 #[derive(Debug, Clone, Serialize)]
   2752 pub struct ListingGetView {
   2753     pub state: String,
   2754     pub source: String,
   2755     pub lookup: String,
   2756     #[serde(flatten)]
   2757     pub readiness: MarketReadinessView,
   2758     #[serde(skip_serializing_if = "Option::is_none")]
   2759     pub listing_id: Option<String>,
   2760     #[serde(skip_serializing_if = "Option::is_none")]
   2761     pub product_key: Option<String>,
   2762     #[serde(skip_serializing_if = "Option::is_none")]
   2763     pub listing_addr: Option<String>,
   2764     #[serde(skip_serializing_if = "Option::is_none")]
   2765     pub primary_bin_id: Option<String>,
   2766     #[serde(skip_serializing_if = "Option::is_none")]
   2767     pub title: Option<String>,
   2768     #[serde(skip_serializing_if = "Option::is_none")]
   2769     pub category: Option<String>,
   2770     #[serde(skip_serializing_if = "Option::is_none")]
   2771     pub description: Option<String>,
   2772     #[serde(skip_serializing_if = "Option::is_none")]
   2773     pub location_primary: Option<String>,
   2774     #[serde(skip_serializing_if = "Option::is_none")]
   2775     pub available: Option<FindQuantityView>,
   2776     #[serde(skip_serializing_if = "Option::is_none")]
   2777     pub price: Option<FindPriceView>,
   2778     pub provenance: FindResultProvenanceView,
   2779     #[serde(skip_serializing_if = "Option::is_none")]
   2780     pub reason: Option<String>,
   2781     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2782     pub actions: Vec<String>,
   2783 }
   2784 
   2785 impl ListingGetView {
   2786     pub fn disposition(&self) -> CommandDisposition {
   2787         match self.state.as_str() {
   2788             "unconfigured" => CommandDisposition::Unconfigured,
   2789             _ => CommandDisposition::Success,
   2790         }
   2791     }
   2792 }
   2793 
   2794 #[derive(Debug, Clone, Serialize)]
   2795 pub struct ListingMutationView {
   2796     pub state: String,
   2797     pub operation: String,
   2798     pub source: String,
   2799     pub file: String,
   2800     pub listing_id: String,
   2801     pub listing_addr: String,
   2802     pub seller_account_id: String,
   2803     pub seller_pubkey: String,
   2804     pub seller_actor_source: String,
   2805     pub event_kind: u32,
   2806     #[serde(default)]
   2807     pub dry_run: bool,
   2808     #[serde(default)]
   2809     pub deduplicated: bool,
   2810     #[serde(default)]
   2811     pub target_relays: Vec<String>,
   2812     #[serde(default)]
   2813     pub connected_relays: Vec<String>,
   2814     #[serde(default)]
   2815     pub acknowledged_relays: Vec<String>,
   2816     #[serde(default)]
   2817     pub failed_relays: Vec<RelayFailureView>,
   2818     #[serde(skip_serializing_if = "Option::is_none")]
   2819     pub job_id: Option<String>,
   2820     #[serde(skip_serializing_if = "Option::is_none")]
   2821     pub job_status: Option<String>,
   2822     #[serde(skip_serializing_if = "Option::is_none")]
   2823     pub signer_mode: Option<String>,
   2824     #[serde(skip_serializing_if = "Option::is_none")]
   2825     pub event_id: Option<String>,
   2826     #[serde(skip_serializing_if = "Option::is_none")]
   2827     pub event_addr: Option<String>,
   2828     #[serde(skip_serializing_if = "Option::is_none")]
   2829     pub idempotency_key: Option<String>,
   2830     #[serde(skip_serializing_if = "Option::is_none")]
   2831     pub local_replica: Option<ListingMutationLocalReplicaView>,
   2832     #[serde(skip_serializing_if = "Option::is_none")]
   2833     pub reason: Option<String>,
   2834     #[serde(skip_serializing_if = "Option::is_none")]
   2835     pub job: Option<ListingMutationJobView>,
   2836     #[serde(skip_serializing_if = "Option::is_none")]
   2837     pub event: Option<ListingMutationEventView>,
   2838     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2839     pub actions: Vec<String>,
   2840 }
   2841 
   2842 impl ListingMutationView {
   2843     pub fn disposition(&self) -> CommandDisposition {
   2844         match self.state.as_str() {
   2845             "unconfigured" => CommandDisposition::Unconfigured,
   2846             "unavailable" => CommandDisposition::ExternalUnavailable,
   2847             "error" => CommandDisposition::InternalError,
   2848             _ => CommandDisposition::Success,
   2849         }
   2850     }
   2851 }
   2852 
   2853 #[derive(Debug, Clone, Serialize)]
   2854 pub struct ListingRebindView {
   2855     pub state: String,
   2856     pub source: String,
   2857     pub file: String,
   2858     pub listing_id: String,
   2859     pub dry_run: bool,
   2860     #[serde(skip_serializing_if = "Option::is_none")]
   2861     pub from_seller_account_id: Option<String>,
   2862     #[serde(skip_serializing_if = "Option::is_none")]
   2863     pub from_seller_pubkey: Option<String>,
   2864     #[serde(skip_serializing_if = "Option::is_none")]
   2865     pub from_seller_actor_source: Option<String>,
   2866     pub to_seller_account_id: String,
   2867     pub to_seller_pubkey: String,
   2868     pub to_seller_actor_source: String,
   2869     #[serde(skip_serializing_if = "Option::is_none")]
   2870     pub seller_pubkey_changed: Option<bool>,
   2871     #[serde(skip_serializing_if = "Option::is_none")]
   2872     pub from_listing_addr: Option<String>,
   2873     pub to_listing_addr: String,
   2874     #[serde(skip_serializing_if = "Option::is_none")]
   2875     pub listing_addr_changed: Option<bool>,
   2876     #[serde(skip_serializing_if = "Option::is_none")]
   2877     pub from_farm_d_tag: Option<String>,
   2878     pub to_farm_d_tag: String,
   2879     #[serde(skip_serializing_if = "Option::is_none")]
   2880     pub farm_d_tag_changed: Option<bool>,
   2881     #[serde(skip_serializing_if = "Option::is_none")]
   2882     pub reason: Option<String>,
   2883     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2884     pub actions: Vec<String>,
   2885 }
   2886 
   2887 impl ListingRebindView {
   2888     pub fn disposition(&self) -> CommandDisposition {
   2889         match self.state.as_str() {
   2890             "unconfigured" => CommandDisposition::Unconfigured,
   2891             "error" => CommandDisposition::InternalError,
   2892             _ => CommandDisposition::Success,
   2893         }
   2894     }
   2895 }
   2896 
   2897 #[derive(Debug, Clone, Serialize)]
   2898 pub struct ListingMutationLocalReplicaView {
   2899     pub state: String,
   2900     pub store_state: String,
   2901     #[serde(skip_serializing_if = "Option::is_none")]
   2902     pub ingest_outcome: Option<String>,
   2903     #[serde(skip_serializing_if = "Option::is_none")]
   2904     pub event_id: Option<String>,
   2905     #[serde(skip_serializing_if = "Option::is_none")]
   2906     pub event_addr: Option<String>,
   2907     #[serde(skip_serializing_if = "Option::is_none")]
   2908     pub reason: Option<String>,
   2909     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2910     pub actions: Vec<String>,
   2911 }
   2912 
   2913 #[derive(Debug, Clone, Serialize)]
   2914 pub struct ListingMutationJobView {
   2915     pub rpc_method: String,
   2916     pub state: String,
   2917     #[serde(skip_serializing_if = "Option::is_none")]
   2918     pub job_id: Option<String>,
   2919     #[serde(skip_serializing_if = "Option::is_none")]
   2920     pub idempotency_key: Option<String>,
   2921     #[serde(skip_serializing_if = "Option::is_none")]
   2922     pub signer_mode: Option<String>,
   2923     #[serde(skip_serializing_if = "Option::is_none")]
   2924     pub relay_count: Option<usize>,
   2925     #[serde(skip_serializing_if = "Option::is_none")]
   2926     pub acknowledged_relay_count: Option<usize>,
   2927 }
   2928 
   2929 #[derive(Debug, Clone, Serialize)]
   2930 pub struct ListingMutationEventView {
   2931     pub kind: u32,
   2932     pub author: String,
   2933     #[serde(skip_serializing_if = "Option::is_none")]
   2934     pub created_at: Option<u32>,
   2935     pub content: String,
   2936     pub tags: Vec<Vec<String>>,
   2937     #[serde(skip_serializing_if = "Option::is_none")]
   2938     pub event_id: Option<String>,
   2939     #[serde(skip_serializing_if = "Option::is_none")]
   2940     pub signature: Option<String>,
   2941     pub event_addr: String,
   2942 }
   2943 
   2944 #[derive(Debug, Clone, Serialize)]
   2945 pub struct FindResultView {
   2946     pub id: String,
   2947     pub product_key: String,
   2948     #[serde(flatten)]
   2949     pub readiness: MarketReadinessView,
   2950     #[serde(skip_serializing_if = "Option::is_none")]
   2951     pub listing_addr: Option<String>,
   2952     #[serde(skip_serializing_if = "Option::is_none")]
   2953     pub primary_bin_id: Option<String>,
   2954     pub title: String,
   2955     pub category: String,
   2956     #[serde(skip_serializing_if = "Option::is_none")]
   2957     pub summary: Option<String>,
   2958     #[serde(skip_serializing_if = "Option::is_none")]
   2959     pub location_primary: Option<String>,
   2960     pub available: FindQuantityView,
   2961     pub price: FindPriceView,
   2962     pub provenance: FindResultProvenanceView,
   2963     #[serde(skip_serializing_if = "Option::is_none")]
   2964     pub hyf: Option<FindResultHyfView>,
   2965 }
   2966 
   2967 #[derive(Debug, Clone, Serialize)]
   2968 pub struct FindResultHyfView {
   2969     pub state: String,
   2970     pub rewritten_query: String,
   2971     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   2972     pub query_terms: Vec<String>,
   2973 }
   2974 
   2975 #[derive(Debug, Clone, Serialize)]
   2976 pub struct FindQuantityView {
   2977     pub total_amount: f64,
   2978     pub total_unit: String,
   2979     #[serde(skip_serializing_if = "Option::is_none")]
   2980     pub label: Option<String>,
   2981     #[serde(skip_serializing_if = "Option::is_none")]
   2982     pub available_amount: Option<i64>,
   2983 }
   2984 
   2985 #[derive(Debug, Clone, Serialize)]
   2986 pub struct FindPriceView {
   2987     pub amount: f64,
   2988     pub currency: String,
   2989     pub per_amount: f64,
   2990     pub per_unit: String,
   2991 }
   2992 
   2993 #[derive(Debug, Clone, Serialize)]
   2994 pub struct FindResultProvenanceView {
   2995     pub origin: String,
   2996     pub freshness: String,
   2997     pub relay_count: usize,
   2998 }
   2999 
   3000 #[derive(Debug, Clone, Serialize)]
   3001 pub struct SyncFreshnessView {
   3002     pub state: String,
   3003     pub display: String,
   3004     #[serde(skip_serializing_if = "Option::is_none")]
   3005     pub age_seconds: Option<u64>,
   3006     #[serde(skip_serializing_if = "Option::is_none")]
   3007     pub last_event_at: Option<u64>,
   3008     #[serde(skip_serializing_if = "Option::is_none")]
   3009     pub run: Option<SyncRunFreshnessView>,
   3010 }
   3011 
   3012 #[derive(Debug, Clone, Serialize)]
   3013 pub struct SyncRunFreshnessView {
   3014     pub scope: String,
   3015     pub relay_set_fingerprint: String,
   3016     pub relay_set_current: bool,
   3017     pub last_state: String,
   3018     #[serde(skip_serializing_if = "Option::is_none")]
   3019     pub last_attempted_at: Option<u64>,
   3020     #[serde(skip_serializing_if = "Option::is_none")]
   3021     pub last_successful_at: Option<u64>,
   3022     #[serde(skip_serializing_if = "Option::is_none")]
   3023     pub last_completed_at: Option<u64>,
   3024     #[serde(skip_serializing_if = "Option::is_none")]
   3025     pub stale_after_seconds: Option<u64>,
   3026     #[serde(skip_serializing_if = "Option::is_none")]
   3027     pub fetched_count: Option<usize>,
   3028     #[serde(skip_serializing_if = "Option::is_none")]
   3029     pub ingested_count: Option<usize>,
   3030     #[serde(skip_serializing_if = "Option::is_none")]
   3031     pub skipped_count: Option<usize>,
   3032     #[serde(skip_serializing_if = "Option::is_none")]
   3033     pub unsupported_count: Option<usize>,
   3034     #[serde(skip_serializing_if = "Option::is_none")]
   3035     pub failed_count: Option<usize>,
   3036     #[serde(skip_serializing_if = "Option::is_none")]
   3037     pub failure_reason: Option<String>,
   3038 }
   3039 
   3040 #[derive(Debug, Clone, Serialize)]
   3041 pub struct SyncQueueView {
   3042     pub expected_count: usize,
   3043     pub pending_count: usize,
   3044     #[serde(skip_serializing_if = "Option::is_none")]
   3045     pub total_count: Option<usize>,
   3046     #[serde(skip_serializing_if = "Option::is_none")]
   3047     pub retryable_count: Option<usize>,
   3048     #[serde(skip_serializing_if = "Option::is_none")]
   3049     pub terminal_count: Option<usize>,
   3050     #[serde(skip_serializing_if = "Option::is_none")]
   3051     pub failed_terminal_count: Option<usize>,
   3052     #[serde(skip_serializing_if = "Option::is_none")]
   3053     pub ready_signed_count: Option<usize>,
   3054     #[serde(skip_serializing_if = "Option::is_none")]
   3055     pub publishing_count: Option<usize>,
   3056     #[serde(skip_serializing_if = "Option::is_none")]
   3057     pub last_attempt_at_ms: Option<i64>,
   3058     #[serde(skip_serializing_if = "Option::is_none")]
   3059     pub last_error: Option<String>,
   3060 }
   3061 
   3062 #[derive(Debug, Clone, Serialize)]
   3063 pub struct LocalBackupView {
   3064     pub state: String,
   3065     pub source: String,
   3066     pub backup_kind: String,
   3067     pub canonical_store: String,
   3068     pub destination: String,
   3069     pub file: String,
   3070     #[serde(skip_serializing_if = "Option::is_none")]
   3071     pub event_store_file: Option<String>,
   3072     #[serde(skip_serializing_if = "Option::is_none")]
   3073     pub outbox_file: Option<String>,
   3074     #[serde(skip_serializing_if = "Option::is_none")]
   3075     pub manifest_file: Option<String>,
   3076     pub size_bytes: u64,
   3077     pub manifest: serde_json::Value,
   3078     #[serde(skip_serializing_if = "Option::is_none")]
   3079     pub reason: Option<String>,
   3080     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3081     pub actions: Vec<String>,
   3082 }
   3083 
   3084 impl LocalBackupView {
   3085     pub fn disposition(&self) -> CommandDisposition {
   3086         match self.state.as_str() {
   3087             "unconfigured" => CommandDisposition::Unconfigured,
   3088             _ => CommandDisposition::Success,
   3089         }
   3090     }
   3091 }
   3092 
   3093 #[derive(Debug, Clone, Serialize)]
   3094 pub struct LocalRestoreView {
   3095     pub state: String,
   3096     pub source: String,
   3097     pub restore_kind: String,
   3098     pub canonical_store: String,
   3099     pub backup_source: String,
   3100     pub destination: String,
   3101     pub event_store_file: String,
   3102     pub outbox_file: String,
   3103     pub manifest_file: String,
   3104     #[serde(skip_serializing_if = "Option::is_none")]
   3105     pub destination_event_store_file: Option<String>,
   3106     #[serde(skip_serializing_if = "Option::is_none")]
   3107     pub destination_outbox_file: Option<String>,
   3108     #[serde(skip_serializing_if = "Option::is_none")]
   3109     pub restored_event_store_file: Option<String>,
   3110     #[serde(skip_serializing_if = "Option::is_none")]
   3111     pub restored_outbox_file: Option<String>,
   3112     pub manifest: serde_json::Value,
   3113     pub verification: serde_json::Value,
   3114     pub overwrite: bool,
   3115     pub dry_run: bool,
   3116     #[serde(skip_serializing_if = "Option::is_none")]
   3117     pub reason: Option<String>,
   3118     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3119     pub actions: Vec<String>,
   3120 }
   3121 
   3122 impl LocalRestoreView {
   3123     pub fn disposition(&self) -> CommandDisposition {
   3124         match self.state.as_str() {
   3125             "unconfigured" => CommandDisposition::Unconfigured,
   3126             _ => CommandDisposition::Success,
   3127         }
   3128     }
   3129 }
   3130 
   3131 #[derive(Debug, Clone, Serialize)]
   3132 pub struct LocalExportView {
   3133     pub state: String,
   3134     pub source: String,
   3135     pub format: String,
   3136     pub file: String,
   3137     pub records: usize,
   3138     pub export_version: String,
   3139     pub schema_hash: String,
   3140     #[serde(skip_serializing_if = "Option::is_none")]
   3141     pub reason: Option<String>,
   3142     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3143     pub actions: Vec<String>,
   3144 }
   3145 
   3146 impl LocalExportView {
   3147     pub fn disposition(&self) -> CommandDisposition {
   3148         match self.state.as_str() {
   3149             "unconfigured" => CommandDisposition::Unconfigured,
   3150             _ => CommandDisposition::Success,
   3151         }
   3152     }
   3153 }
   3154 
   3155 #[derive(Debug, Clone, Serialize)]
   3156 pub struct RelayListView {
   3157     pub state: String,
   3158     pub source: String,
   3159     pub publish_policy: String,
   3160     pub count: usize,
   3161     #[serde(skip_serializing_if = "Option::is_none")]
   3162     pub reason: Option<String>,
   3163     pub relays: Vec<RelayEntryView>,
   3164     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3165     pub actions: Vec<String>,
   3166 }
   3167 
   3168 impl RelayListView {
   3169     pub fn disposition(&self) -> CommandDisposition {
   3170         match self.state.as_str() {
   3171             "unconfigured" => CommandDisposition::Unconfigured,
   3172             _ => CommandDisposition::Success,
   3173         }
   3174     }
   3175 }
   3176 
   3177 #[derive(Debug, Clone, Serialize)]
   3178 pub struct RelayEntryView {
   3179     pub url: String,
   3180     pub read: bool,
   3181     pub write: bool,
   3182 }
   3183 
   3184 #[derive(Debug, Clone, Serialize)]
   3185 pub struct NetStatusView {
   3186     pub state: String,
   3187     pub source: String,
   3188     pub session: String,
   3189     pub relay_count: usize,
   3190     pub publish_policy: String,
   3191     pub signer_mode: String,
   3192     pub account_resolution: AccountResolutionView,
   3193     #[serde(skip_serializing_if = "Option::is_none")]
   3194     pub reason: Option<String>,
   3195     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3196     pub actions: Vec<String>,
   3197 }
   3198 
   3199 impl NetStatusView {
   3200     pub fn disposition(&self) -> CommandDisposition {
   3201         match self.state.as_str() {
   3202             "unconfigured" => CommandDisposition::Unconfigured,
   3203             _ => CommandDisposition::Success,
   3204         }
   3205     }
   3206 }
   3207 
   3208 #[derive(Debug, Clone, Serialize)]
   3209 pub struct RpcStatusView {
   3210     pub state: String,
   3211     pub source: String,
   3212     pub url: String,
   3213     #[serde(skip_serializing_if = "Option::is_none")]
   3214     pub reason: Option<String>,
   3215     #[serde(skip_serializing_if = "Option::is_none")]
   3216     pub auth_mode: Option<String>,
   3217     #[serde(skip_serializing_if = "Option::is_none")]
   3218     pub signer_mode: Option<String>,
   3219     #[serde(skip_serializing_if = "Option::is_none")]
   3220     pub default_signer_mode: Option<String>,
   3221     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3222     pub supported_signer_modes: Vec<String>,
   3223     #[serde(skip_serializing_if = "Option::is_none")]
   3224     pub bridge_enabled: Option<bool>,
   3225     #[serde(skip_serializing_if = "Option::is_none")]
   3226     pub bridge_ready: Option<bool>,
   3227     #[serde(skip_serializing_if = "Option::is_none")]
   3228     pub relay_count: Option<usize>,
   3229     #[serde(skip_serializing_if = "Option::is_none")]
   3230     pub available_nip46_signer_sessions: Option<usize>,
   3231     #[serde(skip_serializing_if = "Option::is_none")]
   3232     pub job_status_retention: Option<usize>,
   3233     #[serde(skip_serializing_if = "Option::is_none")]
   3234     pub retained_jobs: Option<usize>,
   3235     #[serde(skip_serializing_if = "Option::is_none")]
   3236     pub accepted_jobs: Option<usize>,
   3237     #[serde(skip_serializing_if = "Option::is_none")]
   3238     pub published_jobs: Option<usize>,
   3239     #[serde(skip_serializing_if = "Option::is_none")]
   3240     pub failed_jobs: Option<usize>,
   3241     #[serde(skip_serializing_if = "Option::is_none")]
   3242     pub recovered_failed_jobs: Option<usize>,
   3243     pub session_surface_enabled: bool,
   3244     pub methods_count: usize,
   3245     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3246     pub actions: Vec<String>,
   3247 }
   3248 
   3249 #[derive(Debug, Clone, Serialize)]
   3250 pub struct RpcSessionsView {
   3251     pub state: String,
   3252     pub source: String,
   3253     pub url: String,
   3254     pub count: usize,
   3255     #[serde(skip_serializing_if = "Option::is_none")]
   3256     pub reason: Option<String>,
   3257     pub sessions: Vec<RpcSessionView>,
   3258     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3259     pub actions: Vec<String>,
   3260 }
   3261 
   3262 #[derive(Debug, Clone, Serialize)]
   3263 pub struct RpcSessionView {
   3264     pub session_id: String,
   3265     pub role: String,
   3266     pub client_pubkey: String,
   3267     pub signer_pubkey: String,
   3268     #[serde(skip_serializing_if = "Option::is_none")]
   3269     pub user_pubkey: Option<String>,
   3270     pub relay_count: usize,
   3271     pub permissions_count: usize,
   3272     pub auth_required: bool,
   3273     pub authorized: bool,
   3274     #[serde(skip_serializing_if = "Option::is_none")]
   3275     pub expires_in_secs: Option<u64>,
   3276 }
   3277 
   3278 #[derive(Debug, Clone, Serialize)]
   3279 pub struct SyncStatusView {
   3280     pub state: String,
   3281     pub source: String,
   3282     pub local_root: String,
   3283     pub replica_db: String,
   3284     pub relay_count: usize,
   3285     pub publish_policy: String,
   3286     pub freshness: SyncFreshnessView,
   3287     pub queue: SyncQueueView,
   3288     #[serde(skip_serializing_if = "Option::is_none")]
   3289     pub reason: Option<String>,
   3290     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3291     pub actions: Vec<String>,
   3292 }
   3293 
   3294 impl SyncStatusView {
   3295     pub fn disposition(&self) -> CommandDisposition {
   3296         match self.state.as_str() {
   3297             "unconfigured" => CommandDisposition::Unconfigured,
   3298             "unavailable" => CommandDisposition::ExternalUnavailable,
   3299             "error" => CommandDisposition::InternalError,
   3300             _ => CommandDisposition::Success,
   3301         }
   3302     }
   3303 }
   3304 
   3305 #[derive(Debug, Clone, Serialize)]
   3306 pub struct SyncActionView {
   3307     pub direction: String,
   3308     pub state: String,
   3309     pub source: String,
   3310     pub local_root: String,
   3311     pub replica_db: String,
   3312     pub relay_count: usize,
   3313     pub publish_policy: String,
   3314     pub freshness: SyncFreshnessView,
   3315     pub queue: SyncQueueView,
   3316     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3317     pub target_relays: Vec<String>,
   3318     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3319     pub connected_relays: Vec<String>,
   3320     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3321     pub acknowledged_relays: Vec<String>,
   3322     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3323     pub failed_relays: Vec<RelayFailureView>,
   3324     #[serde(skip_serializing_if = "Option::is_none")]
   3325     pub fetched_count: Option<usize>,
   3326     #[serde(skip_serializing_if = "Option::is_none")]
   3327     pub ingested_count: Option<usize>,
   3328     #[serde(skip_serializing_if = "Option::is_none")]
   3329     pub publishable_count: Option<usize>,
   3330     #[serde(skip_serializing_if = "Option::is_none")]
   3331     pub published_count: Option<usize>,
   3332     #[serde(skip_serializing_if = "Option::is_none")]
   3333     pub skipped_count: Option<usize>,
   3334     #[serde(skip_serializing_if = "Option::is_none")]
   3335     pub unsupported_count: Option<usize>,
   3336     #[serde(skip_serializing_if = "Option::is_none")]
   3337     pub failed_count: Option<usize>,
   3338     #[serde(skip_serializing_if = "Option::is_none")]
   3339     pub publish_plan: Option<SyncPublishPlanView>,
   3340     #[serde(skip_serializing_if = "Option::is_none")]
   3341     pub reason_code: Option<String>,
   3342     #[serde(skip_serializing_if = "Option::is_none")]
   3343     pub reason: Option<String>,
   3344     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3345     pub actions: Vec<String>,
   3346 }
   3347 
   3348 impl SyncActionView {
   3349     pub fn disposition(&self) -> CommandDisposition {
   3350         match self.state.as_str() {
   3351             "unconfigured" => CommandDisposition::Unconfigured,
   3352             "unavailable" => CommandDisposition::ExternalUnavailable,
   3353             "partial" => CommandDisposition::ExternalUnavailable,
   3354             "error" => CommandDisposition::InternalError,
   3355             _ => CommandDisposition::Success,
   3356         }
   3357     }
   3358 }
   3359 
   3360 #[derive(Debug, Clone, Serialize)]
   3361 pub struct SyncPublishPlanView {
   3362     pub selected_author: String,
   3363     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3364     pub event_kinds: Vec<SyncPublishPlanKindView>,
   3365     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3366     pub authors: Vec<SyncPublishPlanAuthorView>,
   3367 }
   3368 
   3369 #[derive(Debug, Clone, Serialize)]
   3370 pub struct SyncPublishPlanKindView {
   3371     pub kind: u32,
   3372     pub pending_count: usize,
   3373     pub publishable_count: usize,
   3374     pub skipped_count: usize,
   3375     pub unsupported_count: usize,
   3376     pub failed_count: usize,
   3377 }
   3378 
   3379 #[derive(Debug, Clone, Serialize)]
   3380 pub struct SyncPublishPlanAuthorView {
   3381     pub author: String,
   3382     pub eligibility: String,
   3383     pub pending_count: usize,
   3384     pub publishable_count: usize,
   3385     pub skipped_count: usize,
   3386 }
   3387 
   3388 #[derive(Debug, Clone, Serialize)]
   3389 pub struct SyncWatchView {
   3390     pub state: String,
   3391     pub source: String,
   3392     pub interval_ms: u64,
   3393     pub frames: Vec<SyncWatchFrameView>,
   3394     #[serde(skip_serializing_if = "Option::is_none")]
   3395     pub reason: Option<String>,
   3396     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3397     pub actions: Vec<String>,
   3398 }
   3399 
   3400 impl SyncWatchView {
   3401     pub fn disposition(&self) -> CommandDisposition {
   3402         match self.state.as_str() {
   3403             "unconfigured" => CommandDisposition::Unconfigured,
   3404             "unavailable" => CommandDisposition::ExternalUnavailable,
   3405             "error" => CommandDisposition::InternalError,
   3406             _ => CommandDisposition::Success,
   3407         }
   3408     }
   3409 }
   3410 
   3411 #[derive(Debug, Clone, Serialize)]
   3412 pub struct SyncWatchFrameView {
   3413     pub sequence: usize,
   3414     pub observed_at: u64,
   3415     pub state: String,
   3416     pub relay_count: usize,
   3417     pub freshness: SyncFreshnessView,
   3418     pub queue: SyncQueueView,
   3419 }
   3420 
   3421 #[derive(Debug, Clone, Serialize)]
   3422 pub struct SignerStatusView {
   3423     pub mode: String,
   3424     pub state: String,
   3425     pub source: String,
   3426     #[serde(skip_serializing_if = "Option::is_none")]
   3427     pub signer_account_id: Option<String>,
   3428     pub account_resolution: AccountResolutionView,
   3429     #[serde(skip_serializing_if = "Option::is_none")]
   3430     pub reason: Option<String>,
   3431     pub binding: SignerBindingStatusView,
   3432     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3433     pub write_kinds: Vec<SignerWriteKindReadinessView>,
   3434     #[serde(skip_serializing_if = "Option::is_none")]
   3435     pub local: Option<LocalSignerStatusView>,
   3436     #[serde(skip_serializing_if = "Option::is_none")]
   3437     pub myc: Option<MycStatusView>,
   3438 }
   3439 
   3440 #[derive(Debug, Clone, Serialize)]
   3441 pub struct SignerWriteKindReadinessView {
   3442     pub command: String,
   3443     pub event_kind: u32,
   3444     pub permission: String,
   3445     pub ready: bool,
   3446     #[serde(skip_serializing_if = "Option::is_none")]
   3447     pub reason: Option<String>,
   3448 }
   3449 
   3450 #[derive(Debug, Clone, Serialize)]
   3451 pub struct SignerSessionActionView {
   3452     pub action: String,
   3453     pub state: String,
   3454     pub source: String,
   3455     #[serde(skip_serializing_if = "Option::is_none")]
   3456     pub session_id: Option<String>,
   3457     #[serde(skip_serializing_if = "Option::is_none")]
   3458     pub mode: Option<String>,
   3459     #[serde(skip_serializing_if = "Option::is_none")]
   3460     pub remote_signer_pubkey: Option<String>,
   3461     #[serde(skip_serializing_if = "Option::is_none")]
   3462     pub client_pubkey: Option<String>,
   3463     #[serde(skip_serializing_if = "Option::is_none")]
   3464     pub signer_pubkey: Option<String>,
   3465     #[serde(skip_serializing_if = "Option::is_none")]
   3466     pub user_pubkey: Option<String>,
   3467     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3468     pub relays: Vec<String>,
   3469     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3470     pub permissions: Vec<String>,
   3471     #[serde(skip_serializing_if = "Option::is_none")]
   3472     pub auth_required: Option<bool>,
   3473     #[serde(skip_serializing_if = "Option::is_none")]
   3474     pub authorized: Option<bool>,
   3475     #[serde(skip_serializing_if = "Option::is_none")]
   3476     pub auth_url: Option<String>,
   3477     #[serde(skip_serializing_if = "Option::is_none")]
   3478     pub expires_in_secs: Option<u64>,
   3479     #[serde(skip_serializing_if = "Option::is_none")]
   3480     pub pubkey: Option<String>,
   3481     #[serde(skip_serializing_if = "Option::is_none")]
   3482     pub replayed: Option<bool>,
   3483     #[serde(skip_serializing_if = "Option::is_none")]
   3484     pub required: Option<bool>,
   3485     #[serde(skip_serializing_if = "Option::is_none")]
   3486     pub closed: Option<bool>,
   3487     #[serde(skip_serializing_if = "Option::is_none")]
   3488     pub reason: Option<String>,
   3489 }
   3490 
   3491 impl SignerStatusView {
   3492     pub fn disposition(&self) -> CommandDisposition {
   3493         match self.state.as_str() {
   3494             "unconfigured" => CommandDisposition::Unconfigured,
   3495             "degraded" => CommandDisposition::ExternalUnavailable,
   3496             "unavailable" => CommandDisposition::ExternalUnavailable,
   3497             "error" => CommandDisposition::InternalError,
   3498             _ => CommandDisposition::Success,
   3499         }
   3500     }
   3501 }
   3502 
   3503 #[derive(Debug, Clone, Serialize)]
   3504 pub struct LocalSignerStatusView {
   3505     pub account_id: String,
   3506     pub public_identity: IdentityPublicView,
   3507     pub availability: String,
   3508     pub secret_backed: bool,
   3509     pub backend: String,
   3510     pub used_fallback: bool,
   3511 }
   3512 
   3513 #[derive(Debug, Clone, Serialize)]
   3514 pub struct SignerBindingStatusView {
   3515     pub capability_id: String,
   3516     pub provider_runtime_id: String,
   3517     pub binding_model: String,
   3518     pub state: String,
   3519     pub source: String,
   3520     #[serde(skip_serializing_if = "Option::is_none")]
   3521     pub target_kind: Option<String>,
   3522     #[serde(skip_serializing_if = "Option::is_none")]
   3523     pub target: Option<String>,
   3524     #[serde(skip_serializing_if = "Option::is_none")]
   3525     pub managed_account_ref: Option<String>,
   3526     #[serde(skip_serializing_if = "Option::is_none")]
   3527     pub signer_session_ref: Option<String>,
   3528     #[serde(skip_serializing_if = "Option::is_none")]
   3529     pub resolved_session_ref: Option<String>,
   3530     #[serde(skip_serializing_if = "Option::is_none")]
   3531     pub matched_session_count: Option<usize>,
   3532     #[serde(skip_serializing_if = "Option::is_none")]
   3533     pub reason: Option<String>,
   3534 }
   3535 
   3536 #[derive(Debug, Clone, Serialize)]
   3537 pub struct MycStatusView {
   3538     pub executable: String,
   3539     pub state: String,
   3540     pub source: String,
   3541     pub service_status: Option<String>,
   3542     pub ready: bool,
   3543     #[serde(skip_serializing_if = "Option::is_none")]
   3544     pub reason: Option<String>,
   3545     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3546     pub reasons: Vec<String>,
   3547     pub remote_session_count: usize,
   3548     #[serde(skip_serializing_if = "Option::is_none")]
   3549     pub local_signer: Option<LocalSignerStatusView>,
   3550     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3551     pub remote_sessions: Vec<MycRemoteSessionView>,
   3552     #[serde(skip_serializing_if = "Option::is_none")]
   3553     pub custody: Option<MycCustodyView>,
   3554 }
   3555 
   3556 impl MycStatusView {
   3557     pub fn disposition(&self) -> CommandDisposition {
   3558         match self.state.as_str() {
   3559             "unconfigured" => CommandDisposition::Unconfigured,
   3560             "degraded" => CommandDisposition::ExternalUnavailable,
   3561             "unavailable" => CommandDisposition::ExternalUnavailable,
   3562             _ => CommandDisposition::Success,
   3563         }
   3564     }
   3565 }
   3566 
   3567 #[derive(Debug, Clone, Serialize)]
   3568 pub struct MycRemoteSessionView {
   3569     pub connection_id: String,
   3570     pub signer_identity: IdentityPublicView,
   3571     pub user_identity: IdentityPublicView,
   3572     pub relay_count: usize,
   3573     #[serde(default, skip_serializing_if = "Vec::is_empty")]
   3574     pub permissions: Vec<String>,
   3575 }
   3576 
   3577 #[derive(Debug, Clone, Serialize)]
   3578 pub struct MycCustodyView {
   3579     pub signer: MycCustodyIdentityView,
   3580     pub user: MycCustodyIdentityView,
   3581     #[serde(skip_serializing_if = "Option::is_none")]
   3582     pub discovery_app: Option<MycCustodyIdentityView>,
   3583 }
   3584 
   3585 #[derive(Debug, Clone, Serialize)]
   3586 pub struct MycCustodyIdentityView {
   3587     pub resolved: bool,
   3588     pub selected_account_id: Option<String>,
   3589     pub selected_account_state: Option<String>,
   3590     pub identity_id: Option<String>,
   3591     pub public_key_hex: Option<String>,
   3592     pub error: Option<String>,
   3593 }