1use kdl::{KdlEntry, KdlNode};
2use serde::{Deserialize, Serialize};
3
4use crate::error::UsageErr;
5use crate::spec::context::ParsingContext;
6use crate::spec::helpers::{string_entry, NodeHelper};
7use crate::spec::is_false;
8
9#[derive(Debug, Default, Clone, Serialize, Deserialize)]
10#[non_exhaustive]
11pub struct SpecComplete {
12 pub name: String,
13 #[serde(skip_serializing_if = "Option::is_none")]
14 pub run: Option<String>,
15 #[serde(skip_serializing_if = "is_false")]
16 pub descriptions: bool,
17 #[serde(skip_serializing_if = "Option::is_none")]
18 pub type_: Option<String>,
19}
20
21impl SpecComplete {
22 pub fn new(name: impl Into<String>) -> Self {
24 Self {
25 name: name.into(),
26 ..Default::default()
27 }
28 }
29
30 pub fn run(mut self, run: impl Into<String>) -> Self {
32 self.run = Some(run.into());
33 self
34 }
35
36 pub fn descriptions(mut self, descriptions: bool) -> Self {
38 self.descriptions = descriptions;
39 self
40 }
41}
42
43impl SpecComplete {
44 pub(crate) fn parse(ctx: &ParsingContext, node: &NodeHelper) -> Result<Self, UsageErr> {
45 let mut config = Self::default();
46 node.ensure_arg_len(1..=1)?;
47 config.name = node.arg(0)?.ensure_string()?.to_string().to_lowercase();
48 for (k, v) in node.props() {
49 match k {
50 "run" => {
51 if config.type_.is_some() {
52 bail_parse!(ctx, v.entry.span(), "can set run or type, not both")
53 }
54 config.run = Some(v.ensure_string()?.to_string())
55 }
56 "descriptions" => config.descriptions = v.ensure_bool()?,
57 "type" => {
58 if config.run.is_some() {
59 bail_parse!(ctx, v.entry.span(), "can set run or type, not both")
60 }
61 config.type_ = Some(v.ensure_string()?.to_string())
62 }
63 k => bail_parse!(ctx, v.entry.span(), "unsupported complete key {k}"),
64 }
65 }
66 Ok(config)
67 }
68}
69
70impl From<&SpecComplete> for KdlNode {
71 fn from(complete: &SpecComplete) -> Self {
72 let mut node = KdlNode::new("complete");
73 node.push(string_entry(None, &complete.name));
74 if let Some(run) = &complete.run {
75 node.push(string_entry(Some("run"), run));
76 }
77 if let Some(type_) = &complete.type_ {
78 node.push(string_entry(Some("type"), type_));
79 }
80 if complete.descriptions {
81 node.push(KdlEntry::new_prop("descriptions", true));
82 }
83 node
84 }
85}