Skip to main content

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}