-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
override column type using macro attribute #67
Changes from all commits
015cc5c
b33436a
dea1952
5941cbb
fac8b40
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
use std::collections::{HashMap, HashSet}; | ||
use std::str::FromStr; | ||
|
||
use darling::FromAttributes; | ||
use syn::spanned::Spanned; | ||
|
@@ -44,6 +45,9 @@ pub enum CqlType { | |
pub struct FieldAttributes { | ||
#[darling(default)] | ||
pub ignore: Option<bool>, | ||
|
||
#[darling(default)] | ||
pub column_type: Option<String>, | ||
} | ||
|
||
pub struct Field<'a> { | ||
|
@@ -52,6 +56,7 @@ pub struct Field<'a> { | |
pub ty: Type, | ||
pub ty_path: syn::TypePath, | ||
pub outer_type: CqlType, | ||
pub column_type_override: Option<String>, | ||
pub span: proc_macro2::Span, | ||
pub attrs: &'a Vec<syn::Attribute>, | ||
pub ignore: bool, | ||
|
@@ -107,6 +112,11 @@ impl<'a> Field<'a> { | |
FieldAttributes::from_attributes(&field.attrs) | ||
.map(|char_attrs| { | ||
let ignore = char_attrs.ignore.unwrap_or(false); | ||
|
||
let column_type = char_attrs.column_type.clone().map( | ||
|tname| CqlType::from_str(tname.as_str()).unwrap() | ||
).unwrap_or_else(|| Field::outer_type(&field.ty, ignore)); | ||
|
||
let ident = field.ident.clone().unwrap(); | ||
|
||
Field { | ||
|
@@ -117,7 +127,8 @@ impl<'a> Field<'a> { | |
Type::Path(type_path) => type_path.clone(), | ||
_ => panic!("Only type path is supported!"), | ||
}, | ||
outer_type: Field::outer_type(&field.ty, ignore), | ||
outer_type: column_type, | ||
column_type_override: char_attrs.column_type.clone(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't need the 'clone' here if we borrow value for the |
||
span: field.span(), | ||
attrs: &field.attrs, | ||
ignore, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,47 @@ pub struct CodeSchema { | |
pub materialized_views: SchemaObjects, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's remove migration changes, I will handle 'target' ignore' on a separate branch. |
||
} | ||
|
||
|
||
/// List all rust files under all /src dirs under the project dir. | ||
fn _list_rust_source_files(project_root: &PathBuf) -> Vec<PathBuf> { | ||
// look for src/ dirs max 4 levels down, to avoid stack overflow when we hit target/ | ||
let mut src_dirs = vec![]; | ||
let mut it = WalkDir::new(project_root).max_depth(4).into_iter(); | ||
loop { | ||
let entry = match it.next() { | ||
None => break, | ||
Some(Err(err)) => panic!("ERROR: {}", err), | ||
Some(Ok(entry)) => entry, | ||
}; | ||
let path = entry.path(); | ||
if !path.is_dir() { | ||
continue; | ||
} | ||
if path.ends_with("target") { | ||
it.skip_current_dir(); | ||
continue; | ||
} | ||
if path.ends_with("src") { | ||
src_dirs.push(path.to_owned()); | ||
it.skip_current_dir(); | ||
continue; | ||
} | ||
} | ||
|
||
let mut src_files = vec![]; | ||
for src in src_dirs { | ||
for entry in WalkDir::new(&src).max_depth(8) { | ||
let entry: DirEntry = entry.unwrap(); | ||
let path = entry.path(); | ||
if path.is_file() && path.to_str().unwrap().ends_with(".rs") { | ||
src_files.push(path.to_owned()); | ||
} | ||
} | ||
} | ||
|
||
src_files | ||
} | ||
|
||
impl CodeSchema { | ||
pub fn new(project_root: &String) -> CodeSchema { | ||
let mut current_code_schema = CodeSchema { | ||
|
@@ -48,31 +89,21 @@ impl CodeSchema { | |
|
||
pub fn get_models_from_code(&mut self, project_root: &String) { | ||
let project_root: PathBuf = PathBuf::from(project_root); | ||
for entry in WalkDir::new(project_root) { | ||
let entry: DirEntry = entry.unwrap(); | ||
|
||
if entry.path().is_file() { | ||
let path = entry.path().to_str().unwrap(); | ||
|
||
if !path.ends_with(".rs") { | ||
continue; | ||
} | ||
|
||
let file_content: String = parser::parse_file_as_string(entry.path()); | ||
let ast: syn::File = syn::parse_file(&file_content) | ||
.map_err(|e| { | ||
println!( | ||
"{}\n", | ||
format!("Error parsing file: {}", file_content).bright_red().bold() | ||
); | ||
e | ||
}) | ||
.unwrap(); | ||
|
||
self.populate_materialized_views(&ast); | ||
self.populate_udts(&ast); | ||
self.populate_tables(&ast); | ||
} | ||
for entry in _list_rust_source_files(&project_root) { | ||
let file_content: String = parser::parse_file_as_string(&entry); | ||
let ast: syn::File = syn::parse_file(&file_content) | ||
.map_err(|e| { | ||
println!( | ||
"{}\n", | ||
format!("Error parsing file: {}", file_content).bright_red().bold() | ||
); | ||
e | ||
}) | ||
.unwrap(); | ||
|
||
self.populate_materialized_views(&ast); | ||
self.populate_udts(&ast); | ||
self.populate_tables(&ast); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,8 +22,12 @@ serde = { version = "1.0.200", features = ["derive"] } | |
colored = "2.1.0" | ||
bigdecimal = { version = "0.4.3", features = ["serde"] } | ||
|
||
|
||
[features] | ||
migrate = ["charybdis-migrate"] | ||
|
||
[dev-dependencies] | ||
tokio = "1.42.0" | ||
strum = { version = "0.26.3", features = ["derive"] } | ||
serde = "1.0" | ||
serde_json = "1.0" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's also fix no endlines in files! |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
use charybdis::scylla::deserialize::DeserializeValue; | ||
use charybdis::scylla::frame::response::result::ColumnType; | ||
use charybdis::scylla::SerializeValue; | ||
use charybdis::types::Text; | ||
|
||
#[derive(Debug, Default, Clone, PartialEq, strum::FromRepr)] | ||
#[repr(i8)] | ||
pub enum AddressTypeCustomField { | ||
#[default] | ||
HomeAddress = 0, | ||
WorkAddress = 1, | ||
} | ||
|
||
#[derive(Debug)] | ||
struct AddressTypeCustomDeserializeErr(i8); | ||
impl std::fmt::Display for AddressTypeCustomDeserializeErr { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "AddressTypeCustomDeserializeErr({})", self.0) | ||
} | ||
} | ||
impl std::error::Error for AddressTypeCustomDeserializeErr {} | ||
|
||
impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for AddressTypeCustomField { | ||
fn type_check( | ||
typ: &scylla::frame::response::result::ColumnType, | ||
) -> std::result::Result<(), scylla::deserialize::TypeCheckError> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here, path prefix for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's update this in other functions as well. |
||
<i8 as DeserializeValue<'frame, 'metadata>>::type_check(typ) | ||
} | ||
|
||
fn deserialize( | ||
typ: &'metadata ColumnType<'metadata>, | ||
v: Option<scylla::deserialize::FrameSlice<'frame>>, | ||
) -> std::result::Result<Self, scylla::deserialize::DeserializationError> { | ||
let si8 = <i8 as DeserializeValue<'frame, 'metadata>>::deserialize(typ, v)?; | ||
let s = Self::from_repr(si8); | ||
s.ok_or_else(|| scylla::deserialize::DeserializationError::new(AddressTypeCustomDeserializeErr(si8))) | ||
} | ||
} | ||
|
||
impl SerializeValue for AddressTypeCustomField { | ||
fn serialize<'b>( | ||
&self, | ||
typ: &ColumnType, | ||
writer: scylla::serialize::writers::CellWriter<'b>, | ||
) -> Result<scylla::serialize::writers::WrittenCellProof<'b>, scylla::serialize::SerializationError> { | ||
let disc = self.clone() as i8; | ||
|
||
let v = <i8 as SerializeValue>::serialize(&disc, typ, writer)?; | ||
Ok(v) | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)] | ||
pub struct UserExtraDataCustomField { | ||
pub user_tags: Vec<(String, String)>, | ||
} | ||
|
||
impl Default for UserExtraDataCustomField { | ||
fn default() -> Self { | ||
Self { user_tags: vec![("some_key".to_string(), "some_value".to_string())] } | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
struct UserExtraDataDeserializeErr(String); | ||
impl std::fmt::Display for UserExtraDataDeserializeErr { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
write!(f, "UserExtraDataDeserializeErr({})", self.0) | ||
} | ||
} | ||
impl std::error::Error for UserExtraDataDeserializeErr {} | ||
|
||
impl<'frame, 'metadata> DeserializeValue<'frame, 'metadata> for UserExtraDataCustomField { | ||
fn type_check( | ||
typ: &scylla::frame::response::result::ColumnType, | ||
) -> std::result::Result<(), scylla::deserialize::TypeCheckError> { | ||
<Text as DeserializeValue<'frame, 'metadata>>::type_check(typ) | ||
} | ||
|
||
fn deserialize( | ||
typ: &'metadata ColumnType<'metadata>, | ||
v: Option<scylla::deserialize::FrameSlice<'frame>>, | ||
) -> std::result::Result<Self, scylla::deserialize::DeserializationError> { | ||
let si8 = <Text as DeserializeValue<'frame, 'metadata>>::deserialize(typ, v)?; | ||
serde_json::from_str::<UserExtraDataCustomField>(&si8) | ||
.map_err( | ||
|_e| scylla::deserialize::DeserializationError::new( | ||
UserExtraDataDeserializeErr(si8))) | ||
} | ||
} | ||
|
||
impl SerializeValue for UserExtraDataCustomField { | ||
fn serialize<'b>( | ||
&self, | ||
typ: &ColumnType, | ||
writer: scylla::serialize::writers::CellWriter<'b>, | ||
) -> Result<scylla::serialize::writers::WrittenCellProof<'b>, scylla::serialize::SerializationError> { | ||
|
||
let disc = serde_json::to_string(&self) | ||
.map_err(|_e| scylla::serialize::SerializationError::new( | ||
_e | ||
))?; | ||
|
||
let v = <Text as SerializeValue>::serialize(&disc, typ, writer)?; | ||
Ok(v) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
mod common; | ||
mod model; | ||
mod query; | ||
|
||
mod custom_fields; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here, we can do:
So we can improve error handling. Otherwise, we will not have a clue why migrations failed. Also, we avoid cloning.