use serde::{Deserialize, Serialize};
use std::{fs, path::Path};
mod error;
pub use error::Error;
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliDocument {
pub opencli: String,
pub info: OpenCliInfo,
#[serde(skip_serializing_if = "Option::is_none")]
pub conventions: Option<OpenCliConventions>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<OpenCliArgument>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub options: Vec<OpenCliOption>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub commands: Vec<OpenCliCommand>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub exit_codes: Vec<OpenCliExitCode>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interactive: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub metadata: Vec<OpenCliMetadata>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliInfo {
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contact: Option<OpenCliContact>,
#[serde(skip_serializing_if = "Option::is_none")]
pub license: Option<OpenCliLicense>,
pub version: String,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliConventions {
#[serde(skip_serializing_if = "Option::is_none")]
pub group_options: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub option_argument_separator: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliContact {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliLicense {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub identifier: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliCommand {
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub options: Vec<OpenCliOption>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<OpenCliArgument>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub commands: Vec<OpenCliCommand>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub exit_codes: Vec<OpenCliExitCode>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hidden: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub interactive: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub metadata: Vec<OpenCliMetadata>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliArgument {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub arity: Option<OpenCliArity>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub accepted_values: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hidden: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub metadata: Vec<OpenCliMetadata>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliOption {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub aliases: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub arguments: Vec<OpenCliArgument>,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub recursive: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hidden: Option<bool>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub metadata: Vec<OpenCliMetadata>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliArity {
#[serde(skip_serializing_if = "Option::is_none")]
pub minimum: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub maximum: Option<i32>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliExitCode {
pub code: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct OpenCliMetadata {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<serde_json::Value>,
}
impl OpenCliDocument {
pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Self, Error> {
let path = path.as_ref();
let content = fs::read_to_string(path)?;
Self::from_str(&content)
}
pub fn from_slice(content: &[u8]) -> Result<Self, Error> {
let content = str::from_utf8(content).map_err(|_| Error::Other("utf8"))?;
Self::from_str(content)
}
pub fn from_str(content: &str) -> Result<Self, Error> {
match serde_yaml::from_str(content) {
Ok(data) => Ok(data),
Err(_) => {
let json_data = serde_json::from_str(content)?;
Ok(json_data)
}
}
}
}