rskit_cli/prompt/
choice.rs1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
12pub struct ChoiceId(String);
13
14impl ChoiceId {
15 #[must_use]
17 pub fn new(id: impl Into<String>) -> Self {
18 Self(id.into())
19 }
20
21 #[must_use]
23 pub fn as_str(&self) -> &str {
24 &self.0
25 }
26
27 #[must_use]
29 pub fn into_string(self) -> String {
30 self.0
31 }
32}
33
34impl From<&str> for ChoiceId {
35 fn from(value: &str) -> Self {
36 Self(value.to_string())
37 }
38}
39
40impl From<String> for ChoiceId {
41 fn from(value: String) -> Self {
42 Self(value)
43 }
44}
45
46impl fmt::Display for ChoiceId {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.write_str(&self.0)
49 }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub struct Choice {
56 id: ChoiceId,
57 label: String,
58 annotation: Option<String>,
59 recommended: bool,
60}
61
62impl Choice {
63 #[must_use]
65 pub fn new(id: impl Into<ChoiceId>, label: impl Into<String>) -> Self {
66 Self {
67 id: id.into(),
68 label: label.into(),
69 annotation: None,
70 recommended: false,
71 }
72 }
73
74 #[must_use]
76 pub fn with_annotation(mut self, annotation: impl Into<String>) -> Self {
77 self.annotation = Some(annotation.into());
78 self
79 }
80
81 #[must_use]
86 pub const fn recommended(mut self) -> Self {
87 self.recommended = true;
88 self
89 }
90
91 #[must_use]
93 pub const fn id(&self) -> &ChoiceId {
94 &self.id
95 }
96
97 #[must_use]
99 pub fn label(&self) -> &str {
100 &self.label
101 }
102
103 #[must_use]
105 pub fn annotation(&self) -> Option<&str> {
106 self.annotation.as_deref()
107 }
108
109 #[must_use]
111 pub const fn is_recommended(&self) -> bool {
112 self.recommended
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::{Choice, ChoiceId};
119
120 #[test]
121 fn choice_id_borrows_and_consumes_its_string() {
122 let id = ChoiceId::new("rust");
123 assert_eq!(id.as_str(), "rust");
124 assert_eq!(id.into_string(), "rust");
125 }
126
127 #[test]
128 fn choice_id_converts_from_owned_and_borrowed_strings() {
129 assert_eq!(ChoiceId::from("go").as_str(), "go");
130 assert_eq!(ChoiceId::from("node".to_string()).as_str(), "node");
131 }
132
133 #[test]
134 fn choice_id_displays_its_inner_value() {
135 assert_eq!(ChoiceId::new("rust").to_string(), "rust");
136 }
137
138 #[test]
139 fn choice_accessors_expose_metadata() {
140 let choice = Choice::new("rust", "Rust")
141 .with_annotation("detected in dev-deps")
142 .recommended();
143 assert_eq!(choice.id(), &ChoiceId::new("rust"));
144 assert_eq!(choice.label(), "Rust");
145 assert_eq!(choice.annotation(), Some("detected in dev-deps"));
146 assert!(choice.is_recommended());
147
148 let plain = Choice::new("go", "Go");
149 assert_eq!(plain.annotation(), None);
150 assert!(!plain.is_recommended());
151 }
152}