Skip to content

Commit 666fe7b

Browse files
committed
feat: store foreign key type in Field
1 parent 20c313f commit 666fe7b

File tree

4 files changed

+131
-93
lines changed

4 files changed

+131
-93
lines changed

flareon-cli/src/migration_generator.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,15 @@ pub enum DynOperation {
761761
},
762762
}
763763

764+
impl DynOperation {
765+
fn foreign_keys_added(&self) -> Vec<&syn::Type> {
766+
match self {
767+
DynOperation::CreateModel { fields, .. } => {}
768+
DynOperation::AddField { field, .. } => {}
769+
}
770+
}
771+
}
772+
764773
impl Repr for DynOperation {
765774
fn repr(&self) -> TokenStream {
766775
match self {

flareon-codegen/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
extern crate self as flareon_codegen;
22

33
pub mod expr;
4+
mod maybe_unknown;
45
pub mod model;
56
#[cfg(feature = "symbol-resolver")]
67
pub mod symbol_resolver;
8+
79
#[cfg(not(feature = "symbol-resolver"))]
810
pub mod symbol_resolver {
911
/// Dummy SymbolResolver for use in contexts when it's not useful (e.g.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/// Wraps a type whose value may or may not be possible to be determined using
2+
/// the information available.
3+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
4+
pub enum MaybeUnknown<T> {
5+
/// Indicates that this instance is determined to be a certain value
6+
/// (possibly [`None`] if wrapping an [`Option`]).
7+
Determined(T),
8+
/// Indicates that the value is unknown.
9+
Unknown,
10+
}
11+
12+
impl<T> MaybeUnknown<T> {
13+
pub fn unwrap(self) -> T {
14+
self.expect("called `MaybeUnknown::unwrap()` on an `Unknown` value")
15+
}
16+
17+
pub fn expect(self, msg: &str) -> T {
18+
match self {
19+
MaybeUnknown::Determined(value) => value,
20+
MaybeUnknown::Unknown => {
21+
panic!("{}", msg)
22+
}
23+
}
24+
}
25+
}
26+
27+
#[cfg(test)]
28+
mod tests {
29+
use super::*;
30+
31+
#[test]
32+
fn maybe_unknown_determined() {
33+
let value = MaybeUnknown::Determined(42);
34+
assert_eq!(value.unwrap(), 42);
35+
}
36+
37+
#[test]
38+
#[should_panic(expected = "called `MaybeUnknown::unwrap()` on an `Unknown` value")]
39+
fn maybe_unknown_unknown_unwrap() {
40+
let value: MaybeUnknown<i32> = MaybeUnknown::Unknown;
41+
assert_eq!(value.unwrap(), 42);
42+
}
43+
44+
#[test]
45+
fn maybe_unknown_expect() {
46+
let value = MaybeUnknown::Determined(42);
47+
assert_eq!(value.expect("value should be determined"), 42);
48+
}
49+
50+
#[test]
51+
#[should_panic(expected = "value should be determined")]
52+
fn maybe_unknown_unknown_expect() {
53+
let value: MaybeUnknown<i32> = MaybeUnknown::Unknown;
54+
value.expect("value should be determined");
55+
}
56+
}

flareon-codegen/src/model.rs

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use convert_case::{Case, Casing};
22
use darling::{FromDeriveInput, FromField, FromMeta};
3+
use syn::spanned::Spanned;
34

5+
use crate::maybe_unknown::MaybeUnknown;
46
use crate::symbol_resolver::SymbolResolver;
57

68
#[allow(clippy::module_name_repetitions)]
@@ -66,11 +68,11 @@ impl ModelOpts {
6668
args: &ModelArgs,
6769
symbol_resolver: Option<&SymbolResolver>,
6870
) -> Result<Model, syn::Error> {
69-
let fields: Vec<_> = self
71+
let fields = self
7072
.fields()
7173
.iter()
7274
.map(|field| field.as_field(symbol_resolver))
73-
.collect();
75+
.collect::<Result<Vec<_>, _>>()?;
7476

7577
let mut original_name = self.ident.to_string();
7678
if args.model_type == ModelType::Migration {
@@ -132,45 +134,6 @@ pub struct FieldOpts {
132134
}
133135

134136
impl FieldOpts {
135-
#[must_use]
136-
fn has_type(&self, type_to_check: &str, symbol_resolver: &SymbolResolver) -> bool {
137-
let mut ty = self.ty.clone();
138-
symbol_resolver.resolve(&mut ty);
139-
Self::inner_type_names(&ty)
140-
.iter()
141-
.any(|name| name == type_to_check)
142-
}
143-
144-
#[must_use]
145-
fn inner_type_names(ty: &syn::Type) -> Vec<String> {
146-
let mut names = Vec::new();
147-
Self::inner_type_names_impl(ty, &mut names);
148-
names
149-
}
150-
151-
fn inner_type_names_impl(ty: &syn::Type, names: &mut Vec<String>) {
152-
if let syn::Type::Path(type_path) = ty {
153-
let name = type_path
154-
.path
155-
.segments
156-
.iter()
157-
.map(|s| s.ident.to_string())
158-
.collect::<Vec<_>>()
159-
.join("::");
160-
names.push(name);
161-
162-
for arg in &type_path.path.segments {
163-
if let syn::PathArguments::AngleBracketed(arg) = &arg.arguments {
164-
for arg in &arg.args {
165-
if let syn::GenericArgument::Type(ty) = arg {
166-
Self::inner_type_names_impl(ty, names);
167-
}
168-
}
169-
}
170-
}
171-
}
172-
}
173-
174137
fn find_type(&self, type_to_find: &str, symbol_resolver: &SymbolResolver) -> Option<syn::Type> {
175138
let mut ty = self.ty.clone();
176139
symbol_resolver.resolve(&mut ty);
@@ -225,31 +188,32 @@ impl FieldOpts {
225188
///
226189
/// Panics if the field does not have an identifier (i.e. it is a tuple
227190
/// struct).
228-
#[must_use]
229-
pub fn as_field(&self, symbol_resolver: Option<&SymbolResolver>) -> Field {
191+
pub fn as_field(&self, symbol_resolver: Option<&SymbolResolver>) -> Result<Field, syn::Error> {
230192
let name = self.ident.as_ref().unwrap();
231193
let column_name = name.to_string();
194+
232195
let (auto_value, foreign_key) = match symbol_resolver {
233196
Some(resolver) => (
234197
MaybeUnknown::Determined(self.find_type("flareon::db::Auto", resolver).is_some()),
235198
MaybeUnknown::Determined(
236199
self.find_type("flareon::db::ForeignKey", resolver)
237-
.map(ForeignKeySpec::from),
200+
.map(ForeignKeySpec::try_from)
201+
.transpose()?,
238202
),
239203
),
240204
None => (MaybeUnknown::Unknown, MaybeUnknown::Unknown),
241205
};
242206
let is_primary_key = column_name == "id" || self.primary_key.is_present();
243207

244-
Field {
208+
Ok(Field {
245209
field_name: name.clone(),
246210
column_name,
247211
ty: self.ty.clone(),
248212
auto_value,
249213
primary_key: is_primary_key,
250214
foreign_key,
251215
unique: self.unique.is_present(),
252-
}
216+
})
253217
}
254218
}
255219

@@ -288,52 +252,60 @@ pub struct Field {
288252
pub unique: bool,
289253
}
290254

291-
/// Wraps a type whose value may or may not be possible to be determined using
292-
/// the information available.
293-
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
294-
pub enum MaybeUnknown<T> {
295-
/// Indicates that this instance is determined to be a certain value
296-
/// (possibly [`None`] if wrapping an [`Option`]).
297-
Determined(T),
298-
/// Indicates that the value is unknown.
299-
Unknown,
255+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
256+
pub struct ForeignKeySpec {
257+
pub to_model: syn::Type,
300258
}
301259

302-
impl<T> MaybeUnknown<T> {
303-
pub fn unwrap(self) -> T {
304-
match self {
305-
MaybeUnknown::Determined(value) => value,
306-
MaybeUnknown::Unknown => {
307-
panic!("called `MaybeUnknown::unwrap()` on an `Unknown` value")
308-
}
309-
}
310-
}
260+
impl TryFrom<syn::Type> for ForeignKeySpec {
261+
type Error = syn::Error;
311262

312-
pub fn expect(self, msg: &str) -> T {
313-
match self {
314-
MaybeUnknown::Determined(value) => value,
315-
MaybeUnknown::Unknown => {
316-
panic!("{}", msg)
317-
}
318-
}
319-
}
320-
}
263+
fn try_from(ty: syn::Type) -> Result<Self, Self::Error> {
264+
let type_path = if let syn::Type::Path(type_path) = &ty {
265+
type_path
266+
} else {
267+
panic!("Expected a path type for a foreign key");
268+
};
321269

322-
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
323-
pub struct ForeignKeySpec {
324-
pub ty: syn::Type,
325-
}
270+
let args = if let syn::PathArguments::AngleBracketed(args) = &type_path
271+
.path
272+
.segments
273+
.last()
274+
.expect("type path must have at least one segment")
275+
.arguments
276+
{
277+
args
278+
} else {
279+
return Err(syn::Error::new(
280+
ty.span(),
281+
"expected ForeignKey to have angle-bracketed generic arguments",
282+
));
283+
};
284+
285+
if args.args.len() != 1 {
286+
return Err(syn::Error::new(
287+
ty.span(),
288+
"expected ForeignKey to have only one generic parameter",
289+
));
290+
}
326291

327-
impl From<syn::Type> for ForeignKeySpec {
328-
fn from(value: syn::Type) -> Self {
329-
todo!()
292+
let inner = &args.args[0];
293+
if let syn::GenericArgument::Type(ty) = inner {
294+
Ok(Self {
295+
to_model: ty.clone(),
296+
})
297+
} else {
298+
Err(syn::Error::new(
299+
ty.span(),
300+
"expected ForeignKey to have a type generic argument",
301+
))
302+
}
330303
}
331304
}
332305

333306
#[cfg(test)]
334307
mod tests {
335308
use syn::parse_quote;
336-
use syn::TraitBoundModifier::Maybe;
337309

338310
use super::*;
339311
#[cfg(feature = "symbol-resolver")]
@@ -463,7 +435,7 @@ mod tests {
463435
name: String
464436
};
465437
let field_opts = FieldOpts::from_field(&input).unwrap();
466-
let field = field_opts.as_field(None);
438+
let field = field_opts.as_field(None).unwrap();
467439
assert_eq!(field.field_name.to_string(), "name");
468440
assert_eq!(field.column_name, "name");
469441
assert_eq!(field.ty, parse_quote!(String));
@@ -473,19 +445,18 @@ mod tests {
473445
}
474446

475447
#[test]
476-
fn inner_type_names() {
448+
fn find_type_resolved() {
477449
let input: syn::Type =
478450
parse_quote! { ::my_crate::MyContainer<'a, Vec<std::string::String>> };
479-
let names = FieldOpts::inner_type_names(&input);
480-
assert_eq!(
481-
names,
482-
vec!["my_crate::MyContainer", "Vec", "std::string::String"]
483-
);
451+
assert!(FieldOpts::find_type_resolved(&input, "my_crate::MyContainer").is_some());
452+
assert!(FieldOpts::find_type_resolved(&input, "Vec").is_some());
453+
assert!(FieldOpts::find_type_resolved(&input, "std::string::String").is_some());
454+
assert!(!FieldOpts::find_type_resolved(&input, "OtherType").is_some());
484455
}
485456

486457
#[cfg(feature = "symbol-resolver")]
487458
#[test]
488-
fn contains_type() {
459+
fn find_type() {
489460
let symbols = vec![VisibleSymbol::new(
490461
"MyContainer",
491462
"my_crate::MyContainer",
@@ -500,9 +471,9 @@ mod tests {
500471
unique: Default::default(),
501472
};
502473

503-
assert!(opts.has_type("my_crate::MyContainer", &resolver));
504-
assert!(opts.has_type("std::string::String", &resolver));
505-
assert!(!opts.has_type("MyContainer", &resolver));
506-
assert!(!opts.has_type("String", &resolver));
474+
assert!(opts.find_type("my_crate::MyContainer", &resolver).is_some());
475+
assert!(opts.find_type("std::string::String", &resolver).is_some());
476+
assert!(!opts.find_type("MyContainer", &resolver).is_none());
477+
assert!(!opts.find_type("String", &resolver).is_none());
507478
}
508479
}

0 commit comments

Comments
 (0)