Skip to content

Commit

Permalink
Merge pull request #18 from wslongchen/develop
Browse files Browse the repository at this point in the history
feat(develop): Add Features.
  • Loading branch information
wslongchen authored Nov 29, 2021
2 parents 5806f2d + a04f267 commit 14282ba
Show file tree
Hide file tree
Showing 13 changed files with 400 additions and 95 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "akita"
version = "0.3.1"
version = "0.3.2"
authors = ["mrpan <[email protected]>"]
edition = "2018"
description = "Akita - Mini orm for rust."
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Akita &emsp; [![Build Status]][actions] [![Latest Version]][crates.io] [![akita: rustc 1.13+]][Rust 1.13] [![akita_derive: rustc 1.31+]][Rust 1.31]

[Build Status]: https://img.shields.io/docsrs/akita/0.3.0?style=plastic
[Build Status]: https://img.shields.io/docsrs/akita/0.3.2?style=plastic
[actions]: https://github.com/wslongchen/akita/actions?query=branch%3Amaster
[Latest Version]: https://img.shields.io/crates/v/akita?style=plastic
[crates.io]: https://crates.io/crates/akita
Expand Down Expand Up @@ -43,7 +43,7 @@ Click to show Cargo.toml.
# The core APIs, including the Table traits. Always
# required when using Akita. using #[derive(Table)]
# to make Akita work with structs defined in your crate.
akita = { version = "0.2.0"] }
akita = { version = "0.3.0"] }

```

Expand Down
2 changes: 1 addition & 1 deletion akita_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "akita_derive"
version = "0.3.0"
version = "0.3.1"
authors = ["mrpan <[email protected]>"]
edition = "2018"
description = "Akita.Mini Database Helper For MySQL."
Expand Down
6 changes: 3 additions & 3 deletions akita_derive/src/convert_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,10 @@ pub fn impl_from_akita(input: TokenStream) -> TokenStream {
quote!(
impl akita::FromAkita for #name {

fn from_data(data: &akita::AkitaData) -> Self {
#name {
fn from_data_opt(data: &akita::AkitaData) -> Result<Self, akita::ConvertError> {
Ok(#name {
#(#from_fields)*
}
})
}
}
).into()
Expand Down
6 changes: 3 additions & 3 deletions akita_derive/src/table_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -406,13 +406,13 @@ pub fn impl_get_table(input: TokenStream) -> TokenStream {

type Item = #name;

fn insert<M: akita::AkitaMapper>(&self, entity_manager: &mut M) -> Result<usize, akita::AkitaError> where Self::Item : akita::GetFields + akita::GetTableName + akita::ToAkita {
fn insert<M: akita::AkitaMapper, I>(&self, entity_manager: &mut M) -> Result<Option<I>, akita::AkitaError> where Self::Item : akita::GetFields + akita::GetTableName + akita::ToAkita, I: akita::FromAkita {
let data: Self::Item = self.clone();
entity_manager.save(&data)
}

fn insert_batch<M: akita::AkitaMapper>(datas: &[&Self::Item], entity_manager: &mut M) -> Result<Vec<usize>, akita::AkitaError> where Self::Item : akita::GetTableName + akita::GetFields {
entity_manager.save_batch::<Self::Item>(datas)
fn insert_batch<M: akita::AkitaMapper, I>(datas: &[&Self::Item], entity_manager: &mut M) -> Result<Vec<Option<I>>, akita::AkitaError> where Self::Item : akita::GetTableName + akita::GetFields, I: akita::FromAkita {
entity_manager.save_batch::<Self::Item, I>(datas)
}

fn update<W: akita::Wrapper, M: akita::AkitaMapper>(&self, wrapper: &mut akita::UpdateWrapper, entity_manager: &mut M) -> Result<(), akita::AkitaError> where Self::Item : akita::GetFields + akita::GetTableName + akita::ToAkita {
Expand Down
213 changes: 191 additions & 22 deletions src/data.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
use std::{collections::{BTreeMap, btree_map::{Keys, Values}}, slice};
use std::{any::type_name, collections::{BTreeMap, btree_map::{Keys, Values}}, slice};

use crate::{value::{ConvertError, FromValue, ToValue, Value}};
use crate::{AkitaError, value::{ConvertError, FromValue, ToValue, Value}};


#[derive(Debug, PartialEq, Clone, Default)]
pub struct AkitaData(BTreeMap<String, Value>);
pub struct AkitaData(BTreeMap<String, Value>, Vec<Value>);



pub trait FromAkita {
pub trait FromAkita : Sized {
/// convert akita to an instance of the corresponding struct of the model
/// taking into considerating the renamed columns
fn from_data(data: &AkitaData) -> Self;
fn from_data(data: &AkitaData) -> Self {
match Self::from_data_opt(data) {
Ok(v) => v,
Err(_err) => panic!(
"Couldn't from_data {:?} to type {}. (see FromRow documentation)",
data,
type_name::<Self>(),
),
}
}

fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError>;
}

pub trait ToAkita {
Expand All @@ -29,6 +38,8 @@ pub enum AkitaDataError {
impl AkitaData {
pub fn new() -> Self { AkitaData::default() }

pub fn from_raw(row: &Vec<Value>) -> Self { AkitaData(BTreeMap::new(), row.to_owned()) }

pub fn insert<K, V>(&mut self, k: K, v: V)
where
K: ToString,
Expand All @@ -50,7 +61,7 @@ impl AkitaData {
{
let value: Option<&'a Value> = self.0.get(s);
match value {
Some(v) => FromValue::from_value(v).map_err(AkitaDataError::ConvertError),
Some(v) => FromValue::from_value_opt(v).map_err(AkitaDataError::ConvertError),
None => Err(AkitaDataError::NoSuchValueError(s.into())),
}
}
Expand All @@ -66,7 +77,7 @@ impl AkitaData {
Value::Nil => Ok(None),
_ => {
Ok(Some(
FromValue::from_value(v).map_err(AkitaDataError::ConvertError)?,
FromValue::from_value(v)
))
}
}
Expand All @@ -82,6 +93,48 @@ impl AkitaData {
pub fn keys<'a>(&'a self) -> Keys<'a, String, Value> { self.0.keys() }

pub fn remove(&mut self, s: &str) -> Option<Value> { self.0.remove(s) }


/// Will take value of a column with index `index` if it exists and wasn't taken earlier then
/// will converts it to `T`.
pub fn take_raw<'a, T>(&'a self, index: usize) -> Option<T>
where
T: FromValue,
{
self.1.get(index).and_then(|v| Some(FromValue::from_value(v)))
}

/// Will take value of a column with index `index` if it exists and wasn't taken earlier then
/// will attempt to convert it to `T`. Unlike `Row::take`, `Row::take_opt` will allow you to
/// directly handle errors if the value could not be converted to `T`.
pub fn take_raw_opt<'a, T>(&'a self, index: usize) -> Option<Result<T, AkitaError>>
where
T: FromValue,
{
self.1.get(index).and_then(|v| Some(FromValue::from_value_opt(v).map_err(AkitaError::from)))
}

/// Unwraps values of a row.
///
/// # Panics
///
/// Panics if any of columns was taken by `take` method.
pub fn unwrap(self) -> Vec<Value> {
self.1
.into_iter()
.collect()
}

/// Unwraps values as is (taken cells will be `None`).
#[doc(hidden)]
pub fn unwrap_raw(self) -> Vec<Value> {
self.1
}

#[doc(hidden)]
pub fn place(&mut self, index: usize, value: Value) {
self.1[index] = value;
}
}


Expand All @@ -107,6 +160,11 @@ impl Rows {
}
}

/// Returns true if the row has a length of 0.
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}

pub fn push(&mut self, row: Vec<Value>) { self.data.push(row) }

/// Returns an iterator over the `Row`s.
Expand All @@ -116,6 +174,10 @@ impl Rows {
iter: self.data.iter(),
}
}

pub fn len(&self) -> usize {
self.data.len()
}
}

/// An iterator over `Row`s.
Expand All @@ -131,7 +193,7 @@ impl<'a> Iterator for Iter<'a> {
let next_row = self.iter.next();
if let Some(row) = next_row {
if !row.is_empty() {
let mut dao = AkitaData::new();
let mut dao = AkitaData::from_raw(row);
for (i, column) in self.columns.iter().enumerate() {
if let Some(value) = row.get(i) {
dao.insert_value(column, value);
Expand Down Expand Up @@ -162,26 +224,133 @@ macro_rules! impl_to_segment {
}
}

impl FromAkita for $ty {
fn from_data(data: &AkitaData) -> Self {
if let Some(value) = data.0.values().next() {
FromValue::from_value(value).unwrap_or_default()
} else {
FromValue::from_value(&Value::Nil).unwrap_or_default()
}
}
}
// impl FromAkita for $ty {
// fn from_data(data: &AkitaData) -> Self {
// if let Some(value) = data.0.values().next() {
// FromValue::from_value(value).unwrap_or_default()
// } else {
// FromValue::from_value(&Value::Nil).unwrap_or_default()
// }
// }
// }

};
}

impl FromAkita for () {
fn from_data(_data: &AkitaData) -> Self {
()
macro_rules! take_or_place {
($row:expr, $index:expr, $t:ident) => (
match $row.take_raw($index) {
Some(v) => v,
None => return Err(ConvertError::FromAkitaError($row.to_owned())),
}
);
($row:expr, $index:expr, $t:ident, $( [$idx:expr, $ir:expr] ),*) => (
match $row.take_raw($index) {
Some(v) => v,
None => return Err(ConvertError::FromAkitaError($row.to_owned())),
}
);
}




impl <T> FromAkita for T
where T: FromValue
{
fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError> {
if data.keys().len() == 1 {
Ok(take_or_place!(data, 0, T))
} else {
Err(ConvertError::FromAkitaError(data.to_owned()))
}
}
}

impl FromAkita for AkitaData {
fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError> {
Ok(data.to_owned())
}
}


impl <T1> FromAkita for (T1,)
where T1: FromValue {
fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError> {
T1::from_data_opt(data).map(|t| (t,))
}
}

impl<T1, T2> FromAkita for (T1, T2)
where
T1: FromValue,
T2: FromValue,
{
fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError> {
if data.keys().len() != 2 {
return Err(ConvertError::FromAkitaError(data.to_owned()))
}
let ir1 = take_or_place!(data, 0, T1);
let ir2 = take_or_place!(data, 1, T2, [0, ir1]);
Ok((ir1, ir2))
}
}

impl<T1, T2, T3> FromAkita for (T1, T2, T3)
where
T1: FromValue,
T2: FromValue,
T3: FromValue,
{
fn from_data_opt(data: &AkitaData) -> Result<Self, ConvertError> {
if data.keys().len() != 2 {
return Err(ConvertError::FromAkitaError(data.to_owned()))
}
let ir1 = take_or_place!(data, 0, T1);
let ir2 = take_or_place!(data, 1, T2, [0, ir1]);
let ir3 = take_or_place!(data, 2, T3, [0, ir1], [1, ir2]);
Ok((ir1, ir2, ir3))
}
}

impl <V> ToAkita for BTreeMap<String, V> where V: ToValue {
fn to_data(&self) -> AkitaData {
let values = self.values().into_iter().map(|v| V::to_value(v)).collect::<Vec<Value>>();
let mut bt_map: BTreeMap<String, Value> = BTreeMap::new();
for key in self.keys() {
if let Some(v) = self.get(key) {
bt_map.insert(key.to_string(), V::to_value(v));
}
}
AkitaData(bt_map,values)

}
}

impl ToAkita for serde_json::Value {
fn to_data(&self) -> AkitaData {
if self.is_object() {
if let Some(data) = self.as_object() {
let values = data.values().into_iter().map(|v| serde_json::Value::to_value(v)).collect::<Vec<Value>>();
let mut bt_map: BTreeMap<String, Value> = BTreeMap::new();
for key in data.keys() {
if let Some(v) = self.get(key) {
bt_map.insert(key.to_string(), serde_json::Value::to_value(v));
}
}
AkitaData(bt_map,values)
} else {
AkitaData::new()
}
} else {
AkitaData::new()
}

}
}
impl_to_segment!(i8);
impl_to_segment!(bool);
impl_to_segment!(isize);
impl_to_segment!(i16);
impl_to_segment!(i32);
impl_to_segment!(i64);
Expand Down
7 changes: 7 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
//!
use std::{fmt, str::Utf8Error, string::ParseError};

use crate::ConvertError;

#[derive(Debug)]
pub enum AkitaError {
InvalidSQL(String),
Expand Down Expand Up @@ -80,6 +82,11 @@ impl From<ParseError> for AkitaError {
AkitaError::UrlParseError(err.to_string())
}
}
impl From<ConvertError> for AkitaError {
fn from(err: ConvertError) -> Self {
AkitaError::DataError(err.to_string())
}
}

#[cfg(feature = "akita-mysql")]
impl From<mysql::Error> for AkitaError {
Expand Down
Loading

0 comments on commit 14282ba

Please sign in to comment.