1use serde::{Serialize, Serializer};
6use std::collections::BTreeMap;
7
8use std::fmt::{Debug, Display, Formatter};
9
10pub trait Visit {
14 fn record_field(&mut self, name: &str, field: OptionField);
16
17 fn record_set(&mut self, name: &str, group: OptionSet);
19}
20
21pub trait OptionsMetadata {
23 fn record(visit: &mut dyn Visit);
25
26 fn documentation() -> Option<&'static str> {
27 None
28 }
29
30 fn metadata() -> OptionSet
32 where
33 Self: Sized + 'static,
34 {
35 OptionSet::of::<Self>()
36 }
37}
38
39impl<T> OptionsMetadata for Option<T>
40where
41 T: OptionsMetadata,
42{
43 fn record(visit: &mut dyn Visit) {
44 T::record(visit);
45 }
46}
47
48#[derive(Copy, Clone)]
53pub struct OptionSet {
54 record: fn(&mut dyn Visit),
55 doc: fn() -> Option<&'static str>,
56}
57
58impl PartialEq for OptionSet {
59 fn eq(&self, other: &Self) -> bool {
60 std::ptr::fn_addr_eq(self.record, other.record) && std::ptr::fn_addr_eq(self.doc, other.doc)
61 }
62}
63
64impl Eq for OptionSet {}
65
66impl OptionSet {
67 pub fn of<T>() -> Self
68 where
69 T: OptionsMetadata + 'static,
70 {
71 Self {
72 record: T::record,
73 doc: T::documentation,
74 }
75 }
76
77 pub fn record(&self, visit: &mut dyn Visit) {
79 let record = self.record;
80 record(visit);
81 }
82
83 pub fn documentation(&self) -> Option<&'static str> {
84 let documentation = self.doc;
85 documentation()
86 }
87}
88
89struct DisplayVisitor<'fmt, 'buf> {
91 f: &'fmt mut Formatter<'buf>,
92 result: std::fmt::Result,
93}
94
95impl<'fmt, 'buf> DisplayVisitor<'fmt, 'buf> {
96 fn new(f: &'fmt mut Formatter<'buf>) -> Self {
97 Self { f, result: Ok(()) }
98 }
99
100 fn finish(self) -> std::fmt::Result {
101 self.result
102 }
103}
104
105impl Visit for DisplayVisitor<'_, '_> {
106 fn record_set(&mut self, name: &str, _: OptionSet) {
107 self.result = self.result.and_then(|()| writeln!(self.f, "{name}"));
108 }
109
110 fn record_field(&mut self, name: &str, field: OptionField) {
111 self.result = self.result.and_then(|()| {
112 write!(self.f, "{name}")?;
113
114 if field.deprecated.is_some() {
115 write!(self.f, " (deprecated)")?;
116 }
117
118 writeln!(self.f)
119 });
120 }
121}
122
123impl Display for OptionSet {
124 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
125 let mut visitor = DisplayVisitor::new(f);
126 self.record(&mut visitor);
127 visitor.finish()
128 }
129}
130
131struct SerializeVisitor<'a> {
132 entries: &'a mut BTreeMap<String, OptionField>,
133}
134
135impl Visit for SerializeVisitor<'_> {
136 fn record_set(&mut self, name: &str, set: OptionSet) {
137 let mut entries = BTreeMap::new();
139 let mut visitor = SerializeVisitor {
140 entries: &mut entries,
141 };
142 set.record(&mut visitor);
143
144 for (key, value) in entries {
146 self.entries.insert(format!("{name}.{key}"), value);
147 }
148 }
149
150 fn record_field(&mut self, name: &str, field: OptionField) {
151 self.entries.insert(name.to_string(), field);
152 }
153}
154
155impl Serialize for OptionSet {
156 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
157 where
158 S: Serializer,
159 {
160 let mut entries = BTreeMap::new();
161 let mut visitor = SerializeVisitor {
162 entries: &mut entries,
163 };
164 self.record(&mut visitor);
165 entries.serialize(serializer)
166 }
167}
168
169impl Debug for OptionSet {
170 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
171 Display::fmt(self, f)
172 }
173}
174
175#[derive(Debug, Eq, PartialEq, Clone, Serialize)]
176pub struct OptionField {
177 pub doc: &'static str,
178 pub default: &'static str,
180 pub value_type: &'static str,
182 pub scope: Option<&'static str>,
184 pub example: &'static str,
185 pub deprecated: Option<Deprecated>,
186 pub possible_values: Option<Vec<PossibleValue>>,
187 pub uv_toml_only: bool,
189}
190
191#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
192pub struct Deprecated {
193 pub since: Option<&'static str>,
194 pub message: Option<&'static str>,
195}
196
197impl Display for OptionField {
198 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
199 writeln!(f, "{}", self.doc)?;
200 writeln!(f)?;
201
202 writeln!(f, "Default value: {}", self.default)?;
203
204 if let Some(possible_values) = self
205 .possible_values
206 .as_ref()
207 .filter(|values| !values.is_empty())
208 {
209 writeln!(f, "Possible values:")?;
210 writeln!(f)?;
211 for value in possible_values {
212 writeln!(f, "- {value}")?;
213 }
214 } else {
215 writeln!(f, "Type: {}", self.value_type)?;
216 }
217
218 if let Some(deprecated) = &self.deprecated {
219 write!(f, "Deprecated")?;
220
221 if let Some(since) = deprecated.since {
222 write!(f, " (since {since})")?;
223 }
224
225 if let Some(message) = deprecated.message {
226 write!(f, ": {message}")?;
227 }
228
229 writeln!(f)?;
230 }
231
232 writeln!(f, "Example usage:\n```toml\n{}\n```", self.example)
233 }
234}
235
236#[derive(Debug, Eq, PartialEq, Clone, Serialize)]
239pub struct PossibleValue {
240 pub name: String,
241 pub help: Option<String>,
242}
243
244impl Display for PossibleValue {
245 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
246 write!(f, "`\"{}\"`", self.name)?;
247 if let Some(help) = &self.help {
248 write!(f, ": {help}")?;
249 }
250 Ok(())
251 }
252}