use std::path::PathBuf;
use clap::{ArgAction, Parser, Subcommand};
#[derive(Debug, Parser)]
#[command(
name = "ign",
version,
propagate_version = true,
about = "Operate Ignition 8.3+ gateways from the terminal"
)]
pub struct Cli {
#[arg(long, global = true, value_name = "NAME")]
pub profile: Option<String>,
#[arg(long, global = true, action = ArgAction::SetTrue)]
pub json: bool,
#[arg(long, global = true, action = ArgAction::SetTrue)]
pub compact: bool,
#[arg(long, short = 'y', global = true, action = ArgAction::SetTrue)]
pub yes: bool,
#[arg(long, short = 'v', global = true, action = ArgAction::Count)]
pub verbose: u8,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
Version,
#[command(arg_required_else_help = true)]
Completions {
#[arg(value_enum)]
shell: clap_complete::aot::Shell,
},
Status,
Modules {
#[arg(long)]
quarantined: bool,
},
Metrics {
#[arg(long)]
history: bool,
},
Sessions(SessionsArgs),
Connections {
#[arg(long, value_enum)]
r#type: Option<ConnectionType>,
},
Logs(LogsArgs),
Restart {
#[arg(long)]
wait: bool,
#[arg(long, value_name = "SECS")]
timeout: Option<u64>,
#[arg(long, value_name = "SECS")]
interval: Option<u64>,
},
#[command(arg_required_else_help = true)]
Wait(WaitArgs),
Doctor {
#[arg(long)]
check_write: bool,
#[arg(long, value_name = "NAME")]
webdev_route: Option<String>,
},
#[command(arg_required_else_help = true)]
Project(ProjectArgs),
#[command(arg_required_else_help = true)]
Resource(ResourceArgs),
#[command(arg_required_else_help = true)]
Webdev(WebdevArgs),
#[command(arg_required_else_help = true)]
Tags(TagsArgs),
#[command(arg_required_else_help = true)]
Rig(RigArgs),
#[command(arg_required_else_help = true)]
Backup(BackupArgs),
#[command(arg_required_else_help = true)]
Eam(EamArgs),
#[command(arg_required_else_help = true)]
Script(ScriptArgs),
Lint(LintArgs),
#[command(arg_required_else_help = true)]
Profile(ProfileArgs),
#[cfg(feature = "tui")]
Tui,
}
#[derive(Debug, clap::Args)]
pub struct WaitArgs {
#[command(subcommand)]
pub command: WaitCmd,
}
#[derive(Debug, Subcommand)]
pub enum WaitCmd {
Gateway {
#[arg(long, default_value_t = 2, value_name = "SECS")]
interval: u64,
#[arg(long, default_value_t = 120, value_name = "SECS")]
timeout: u64,
},
Restart {
#[arg(long, default_value_t = 2, value_name = "SECS")]
interval: u64,
#[arg(long, default_value_t = 300, value_name = "SECS")]
timeout: u64,
},
Module {
id: String,
#[arg(long, default_value_t = 2, value_name = "SECS")]
interval: u64,
#[arg(long, default_value_t = 120, value_name = "SECS")]
timeout: u64,
},
}
#[derive(Debug, clap::Args)]
pub struct ProjectArgs {
#[command(subcommand)]
pub command: ProjectCommand,
}
#[derive(Debug, Subcommand)]
pub enum ProjectCommand {
List,
New {
name: String,
#[arg(long, value_name = "TEXT")]
title: Option<String>,
#[arg(long, value_name = "TEXT")]
description: Option<String>,
#[arg(long, value_name = "NAME")]
parent: Option<String>,
#[arg(long)]
inheritable: bool,
#[arg(long)]
disabled: bool,
},
Copy {
src: String,
dst: String,
},
Rename {
old_name: String,
new_name: String,
},
#[command(group(
clap::ArgGroup::new("set_fields")
.required(true)
.multiple(true)
.args(&["title", "description", "parent", "set_enabled", "disabled", "inheritable"])
))]
Set {
name: String,
#[arg(long, value_name = "TEXT")]
title: Option<String>,
#[arg(long, value_name = "TEXT")]
description: Option<String>,
#[arg(long, value_name = "NAME")]
parent: Option<String>,
#[arg(long, conflicts_with = "disabled")]
set_enabled: bool,
#[arg(long)]
disabled: bool,
#[arg(long, value_name = "BOOL")]
inheritable: Option<bool>,
},
Delete {
name: String,
},
Export {
name: String,
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
#[arg(long)]
decode_scripts: bool,
},
Import {
name: String,
#[arg(long, value_name = "PATH")]
file: String,
#[arg(long, value_enum, default_value_t = CollisionPolicy::Abort)]
collision_policy: CollisionPolicy,
#[arg(long)]
encode_scripts: bool,
},
Diff {
profile_a: String,
profile_b: String,
#[arg(long, value_name = "NAME")]
project: String,
},
Sync {
profile_a: String,
profile_b: String,
#[arg(long, value_name = "NAME")]
project: String,
#[arg(long, value_name = "PATH")]
resource: Vec<String>,
#[arg(long)]
all_changed: bool,
#[arg(long)]
delete: bool,
},
}
#[derive(Debug, clap::Args)]
pub struct ResourceArgs {
#[command(subcommand)]
pub command: ResourceCommand,
}
#[derive(Debug, Subcommand)]
pub enum ResourceCommand {
List {
project: String,
#[arg(long, value_name = "PREFIX")]
prefix: Option<String>,
},
Get {
project: String,
path: String,
},
Put {
project: String,
path: String,
#[arg(long, value_name = "PATH")]
file: String,
},
Delete {
project: String,
path: String,
},
}
#[derive(Debug, clap::Args)]
pub struct WebdevArgs {
#[command(subcommand)]
pub command: WebdevCommand,
}
#[derive(Debug, Subcommand)]
pub enum WebdevCommand {
Deploy {
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
#[arg(long)]
with_script_exec: bool,
#[arg(long, requires = "with_script_exec")]
rotate_secret: bool,
},
Status {
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, clap::Args)]
pub struct TagsArgs {
#[command(subcommand)]
pub command: TagsCommand,
}
#[derive(Debug, Subcommand)]
pub enum TagsCommand {
#[command(subcommand)]
Provider(TagsProviderCommand),
Browse {
path: Option<String>,
#[arg(long, value_name = "SUBSTR")]
filter: Option<String>,
#[arg(long)]
include_properties: bool,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
#[arg(long, value_name = "PATH", conflicts_with_all = ["path"])]
from_export: Option<PathBuf>,
},
Read {
#[arg(value_name = "PATH", required = true)]
paths: Vec<String>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Write {
path: String,
#[arg(long, value_name = "V")]
value: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
#[command(subcommand)]
Config(TagsConfigCommand),
#[command(subcommand)]
Udt(TagsUdtCommand),
#[command(subcommand)]
Alarms(TagsAlarmsCommand),
#[command(subcommand)]
History(TagsHistoryCommand),
Export {
#[arg(value_name = "PATH", required = true)]
paths: Vec<String>,
#[arg(short = 'o', long, value_name = "FILE")]
output: Option<PathBuf>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Import {
#[arg(long, value_name = "FILE")]
file: PathBuf,
#[arg(long, value_name = "NAME")]
provider: String,
#[arg(long, value_enum, default_value_t = CollisionPolicy::Abort)]
collision_policy: CollisionPolicy,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, Subcommand)]
pub enum TagsConfigCommand {
Get {
path: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Create {
path: String,
#[arg(long, value_name = "FILE")]
file: PathBuf,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Edit {
path: String,
#[arg(long, value_name = "FILE")]
file: PathBuf,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Delete {
#[arg(value_name = "PATH", required = true)]
paths: Vec<String>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, Subcommand)]
pub enum TagsUdtCommand {
Types {
#[arg(long, default_value = "default", value_name = "NAME")]
provider: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Def {
name: String,
#[arg(long, default_value = "default", value_name = "NAME")]
provider: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, Subcommand)]
pub enum TagsProviderCommand {
List,
Create {
name: String,
},
Delete {
name: String,
},
}
#[derive(Debug, Subcommand)]
pub enum TagsAlarmsCommand {
Active {
#[arg(long, value_name = "SOURCE")]
source: Option<String>,
#[arg(long, value_name = "PRIORITY")]
priority: Option<String>,
#[arg(long, value_name = "STATE")]
state: Option<String>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
History {
#[arg(long, value_name = "T")]
start: String,
#[arg(long, value_name = "T")]
end: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
Ack {
#[arg(value_name = "ID", required = true)]
ids: Vec<String>,
#[arg(long, value_name = "NOTE")]
note: Option<String>,
#[arg(long, value_name = "NAME")]
username: String,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, Subcommand)]
pub enum TagsHistoryCommand {
Query {
#[arg(value_name = "PATH", required = true)]
paths: Vec<String>,
#[arg(long, value_name = "T")]
start: String,
#[arg(long, value_name = "T")]
end: String,
#[arg(long, value_name = "N")]
return_size: Option<i64>,
#[arg(long, value_name = "MODE")]
aggregation: Option<String>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, clap::Args)]
pub struct RigArgs {
#[arg(long, value_name = "NAME")]
pub rig: Option<String>,
#[command(subcommand)]
pub command: RigCommand,
}
#[derive(Debug, Subcommand)]
pub enum RigCommand {
Up {
#[arg(long, default_value_t = 300, value_name = "SECS")]
timeout: u64,
},
Down,
Reset {
#[arg(long, default_value_t = 300, value_name = "SECS")]
timeout: u64,
},
Status,
Logs {
#[arg(long, default_value_t = 200, value_name = "N")]
tail: u32,
#[arg(short = 'f', long)]
follow: bool,
service: Option<String>,
},
Trial(TrialArgs),
Snapshot {
#[arg(short = 'o', long, value_name = "DIR")]
output: Option<PathBuf>,
},
Restore {
#[arg(long, value_name = "PATH")]
file: PathBuf,
#[arg(long, default_value_t = 300, value_name = "SECS")]
timeout: u64,
},
}
#[derive(Debug, clap::Args)]
pub struct TrialArgs {
#[command(subcommand)]
pub command: TrialCommand,
}
#[derive(Debug, Clone, Subcommand)]
pub enum TrialCommand {
Status,
Reset {
#[arg(long, value_name = "NAME")]
user: Option<String>,
},
}
#[derive(Debug, clap::Args)]
pub struct BackupArgs {
#[command(subcommand)]
pub command: BackupCommand,
}
#[derive(Debug, Subcommand)]
pub enum BackupCommand {
Download {
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
#[arg(long, value_enum, default_value_t = CliBackupType::Roaming)]
r#type: CliBackupType,
},
Restore {
file: PathBuf,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum CliBackupType {
Roaming,
All,
}
impl From<CliBackupType> for ignition_core::client::backup::BackupType {
fn from(value: CliBackupType) -> Self {
match value {
CliBackupType::Roaming => Self::Roaming,
CliBackupType::All => Self::All,
}
}
}
#[derive(Debug, clap::Args)]
pub struct EamArgs {
#[command(subcommand)]
pub command: EamCommand,
}
#[derive(Debug, Subcommand)]
pub enum EamCommand {
History {
#[arg(long, value_name = "N")]
limit: Option<u32>,
#[arg(long, value_name = "TEXT")]
search: Option<String>,
},
Tasks {
name: Option<String>,
},
#[command(subcommand)]
Task(EamTaskCommand),
}
#[derive(Debug, Subcommand)]
pub enum EamTaskCommand {
New {
name: String,
r#type: String,
#[arg(long, value_name = "NAME")]
target: Vec<String>,
#[arg(long, value_name = "K=V")]
setting: Vec<String>,
#[arg(long, value_name = "PATH", conflicts_with = "setting")]
definition: Option<PathBuf>,
#[arg(long, value_enum, default_value_t = ScheduleMode::OnDemand)]
schedule_mode: ScheduleMode,
},
Force {
name: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ScheduleMode {
OnDemand,
Immediate,
Scheduled,
AtTime,
AtDelay,
}
impl ScheduleMode {
pub fn wire(self) -> &'static str {
match self {
Self::OnDemand => "OnDemand",
Self::Immediate => "Immediate",
Self::Scheduled => "Scheduled",
Self::AtTime => "AtTime",
Self::AtDelay => "AtDelay",
}
}
}
#[derive(Debug, clap::Args)]
pub struct ScriptArgs {
#[command(subcommand)]
pub command: ScriptCommand,
}
#[derive(Debug, clap::Args)]
pub struct LintArgs {
#[arg(value_name = "PATH", required = true)]
pub paths: Vec<String>,
#[arg(long)]
pub strict: bool,
#[arg(last = true, value_name = "ARGS")]
pub passthrough: Vec<String>,
}
#[derive(Debug, Subcommand)]
pub enum ScriptCommand {
Run {
#[arg(long, value_name = "PY")]
code: Option<String>,
#[arg(long, value_name = "PATH")]
file: Option<String>,
#[arg(long, default_value = "ign-cli", value_name = "NAME")]
project: String,
},
}
#[derive(Debug, clap::Args)]
pub struct ProfileArgs {
#[command(subcommand)]
pub command: ProfileCmd,
}
#[derive(Debug, clap::Args)]
pub struct SessionsArgs {
#[arg(long, value_enum)]
pub r#type: Option<SessionType>,
#[command(subcommand)]
pub command: Option<SessionsCmd>,
}
#[derive(Debug, Subcommand)]
pub enum SessionsCmd {
Terminate {
#[arg(long, value_enum)]
r#type: SessionType,
#[arg(long)]
id: String,
#[arg(long)]
message: Option<String>,
},
}
#[derive(Debug, clap::Args)]
pub struct LogsArgs {
#[arg(long, value_name = "NAME")]
pub logger: Option<String>,
#[arg(long, value_enum, value_name = "LEVEL")]
pub min_level: Option<LogLevel>,
#[arg(
long,
value_name = "EPOCH_MS|Nms|Ns|Nmin|Nh",
value_parser = parse_since_arg
)]
pub since: Option<i64>,
#[arg(long, default_value_t = 200)]
pub limit: i64,
#[arg(short = 'f', long)]
pub follow: bool,
#[arg(long, default_value_t = 2, value_name = "SECS")]
pub interval: u64,
#[arg(long, value_name = "SECS")]
pub timeout: Option<u64>,
#[command(subcommand)]
pub command: Option<LogsCmd>,
}
#[derive(Debug, Subcommand)]
pub enum LogsCmd {
Download {
#[arg(short, long, value_name = "FILE")]
output: Option<PathBuf>,
},
Loggers(LoggersArgs),
}
#[derive(Debug, clap::Args)]
pub struct LoggersArgs {
#[arg(long, value_name = "TEXT")]
pub search: Option<String>,
#[command(subcommand)]
pub command: Option<LoggersCmd>,
}
#[derive(Debug, Subcommand)]
pub enum LoggersCmd {
Set {
name: String,
#[arg(value_enum)]
level: LogLevel,
},
Reset,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum LogLevel {
Trace,
Debug,
Info,
Warn,
Error,
Fatal,
Off,
}
impl LogLevel {
pub fn wire(self) -> &'static str {
match self {
Self::Trace => "TRACE",
Self::Debug => "DEBUG",
Self::Info => "INFO",
Self::Warn => "WARN",
Self::Error => "ERROR",
Self::Fatal => "FATAL",
Self::Off => "OFF",
}
}
}
fn parse_since_arg(spec: &str) -> Result<i64, String> {
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("system clock is after the unix epoch")
.as_millis() as i64;
ignition_core::actions::logs::parse_since(spec, now_ms)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum SessionType {
Designer,
Perspective,
Vision,
}
impl From<SessionType> for ignition_core::actions::sessions::SessionType {
fn from(value: SessionType) -> Self {
match value {
SessionType::Designer => Self::Designer,
SessionType::Perspective => Self::Perspective,
SessionType::Vision => Self::Vision,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum ConnectionType {
Database,
Opc,
}
impl From<ConnectionType> for ignition_core::actions::connections::ConnectionType {
fn from(value: ConnectionType) -> Self {
match value {
ConnectionType::Database => Self::Database,
ConnectionType::Opc => Self::Opc,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, clap::ValueEnum)]
pub enum CollisionPolicy {
Abort,
Overwrite,
}
impl From<CollisionPolicy> for ignition_core::actions::projects::CollisionPolicy {
fn from(value: CollisionPolicy) -> Self {
match value {
CollisionPolicy::Abort => Self::Abort,
CollisionPolicy::Overwrite => Self::Overwrite,
}
}
}
#[derive(Debug, Subcommand)]
#[command(arg_required_else_help = true)]
pub enum ProfileCmd {
Add {
name: String,
url: String,
#[arg(long, value_name = "TEXT")]
label: Option<String>,
#[arg(long, value_name = "VAR")]
token_env: Option<String>,
#[arg(long, value_name = "USER")]
keyring: Option<String>,
#[arg(long, value_name = "VAR")]
user_env: Option<String>,
#[arg(long, value_name = "VAR")]
password_env: Option<String>,
#[arg(long)]
active: bool,
},
List,
Use {
name: String,
},
}