whitaker_common/decomposition_advice/profile.rs
1//! Public input types for decomposition analysis.
2
3use std::collections::BTreeSet;
4
5/// The kind of subject being analysed for decomposition advice.
6///
7/// # Examples
8///
9/// ```
10/// use whitaker_common::decomposition_advice::SubjectKind;
11///
12/// assert_eq!(SubjectKind::Type, SubjectKind::Type);
13/// assert_ne!(SubjectKind::Type, SubjectKind::Trait);
14/// ```
15#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
16pub enum SubjectKind {
17 /// Analyse methods that belong to a type.
18 Type,
19 /// Analyse methods that belong to a trait.
20 Trait,
21}
22
23impl std::str::FromStr for SubjectKind {
24 type Err = String;
25
26 fn from_str(s: &str) -> Result<Self, Self::Err> {
27 match s {
28 "type" => Ok(Self::Type),
29 "trait" => Ok(Self::Trait),
30 _ => Err(format!("unknown subject kind: {s}")),
31 }
32 }
33}
34
35/// Context about the analysed subject.
36///
37/// # Examples
38///
39/// ```
40/// use whitaker_common::decomposition_advice::{DecompositionContext, SubjectKind};
41///
42/// let context = DecompositionContext::new("Parser", SubjectKind::Type);
43/// assert_eq!(context.subject_name(), "Parser");
44/// assert_eq!(context.subject_kind(), SubjectKind::Type);
45/// ```
46#[derive(Clone, Debug, Eq, PartialEq)]
47pub struct DecompositionContext {
48 subject_name: String,
49 subject_kind: SubjectKind,
50}
51
52impl DecompositionContext {
53 /// Creates a new decomposition-analysis context.
54 #[must_use]
55 pub fn new(subject_name: impl Into<String>, subject_kind: SubjectKind) -> Self {
56 Self {
57 subject_name: subject_name.into(),
58 subject_kind,
59 }
60 }
61
62 /// Returns the analysed subject name.
63 #[must_use]
64 pub fn subject_name(&self) -> &str {
65 &self.subject_name
66 }
67
68 /// Returns the analysed subject kind.
69 #[must_use]
70 pub fn subject_kind(&self) -> SubjectKind {
71 self.subject_kind
72 }
73}
74
75/// Immutable per-method metadata used to build feature vectors.
76///
77/// The profile stores the inputs called for by the design document:
78/// accessed fields, signature types, local-variable types, external domains,
79/// and the method name itself.
80///
81/// # Examples
82///
83/// ```
84/// use whitaker_common::decomposition_advice::MethodProfileBuilder;
85///
86/// let mut builder = MethodProfileBuilder::new("parse_tokens");
87/// builder
88/// .record_accessed_field("grammar")
89/// .record_accessed_field("tokens")
90/// .record_signature_type("TokenStream");
91///
92/// let profile = builder.build();
93/// assert_eq!(profile.name(), "parse_tokens");
94/// assert_eq!(profile.accessed_fields().len(), 2);
95/// assert_eq!(profile.signature_types().len(), 1);
96/// ```
97#[derive(Clone, Debug, Eq, PartialEq)]
98pub struct MethodProfile {
99 name: String,
100 accessed_fields: BTreeSet<String>,
101 signature_types: BTreeSet<String>,
102 local_types: BTreeSet<String>,
103 external_domains: BTreeSet<String>,
104}
105
106impl MethodProfile {
107 /// Returns the method name.
108 #[must_use]
109 pub fn name(&self) -> &str {
110 &self.name
111 }
112
113 /// Returns accessed fields.
114 #[must_use]
115 pub fn accessed_fields(&self) -> &BTreeSet<String> {
116 &self.accessed_fields
117 }
118
119 /// Returns types used in the method signature.
120 #[must_use]
121 pub fn signature_types(&self) -> &BTreeSet<String> {
122 &self.signature_types
123 }
124
125 /// Returns types used in local variables.
126 #[must_use]
127 pub fn local_types(&self) -> &BTreeSet<String> {
128 &self.local_types
129 }
130
131 /// Returns external domains used by the method.
132 #[must_use]
133 pub fn external_domains(&self) -> &BTreeSet<String> {
134 &self.external_domains
135 }
136}
137
138/// Mutable builder for [`MethodProfile`].
139///
140/// This follows the existing builder pattern used elsewhere in `common` and
141/// keeps callers away from constructors with too many arguments.
142///
143/// # Examples
144///
145/// ```
146/// use whitaker_common::decomposition_advice::MethodProfileBuilder;
147///
148/// let mut builder = MethodProfileBuilder::new("write_cache");
149/// builder
150/// .record_external_domain("std::fs")
151/// .record_local_type("PathBuf");
152///
153/// let profile = builder.build();
154/// assert_eq!(profile.external_domains().iter().next().map(String::as_str), Some("std::fs"));
155/// ```
156#[derive(Clone, Debug, Default, Eq, PartialEq)]
157pub struct MethodProfileBuilder {
158 name: String,
159 accessed_fields: BTreeSet<String>,
160 signature_types: BTreeSet<String>,
161 local_types: BTreeSet<String>,
162 external_domains: BTreeSet<String>,
163}
164
165impl MethodProfileBuilder {
166 /// Creates an empty builder for the given method name.
167 #[must_use]
168 pub fn new(name: impl Into<String>) -> Self {
169 Self {
170 name: name.into(),
171 ..Self::default()
172 }
173 }
174
175 /// Records an accessed field.
176 pub fn record_accessed_field(&mut self, field_name: impl Into<String>) -> &mut Self {
177 self.accessed_fields.insert(field_name.into());
178 self
179 }
180
181 /// Records a signature type.
182 pub fn record_signature_type(&mut self, type_name: impl Into<String>) -> &mut Self {
183 self.signature_types.insert(type_name.into());
184 self
185 }
186
187 /// Records a local-variable type.
188 pub fn record_local_type(&mut self, type_name: impl Into<String>) -> &mut Self {
189 self.local_types.insert(type_name.into());
190 self
191 }
192
193 /// Records an external domain.
194 pub fn record_external_domain(&mut self, domain: impl Into<String>) -> &mut Self {
195 self.external_domains.insert(domain.into());
196 self
197 }
198
199 /// Builds the immutable profile.
200 #[must_use]
201 pub fn build(self) -> MethodProfile {
202 MethodProfile {
203 name: self.name,
204 accessed_fields: self.accessed_fields,
205 signature_types: self.signature_types,
206 local_types: self.local_types,
207 external_domains: self.external_domains,
208 }
209 }
210}