Skip to content

Commit 2979ce7

Browse files
authored
Implement SizedString type (#198)
* Implement SizedString type * Update SizedString for example * Add macro to write down the field for SizedString
1 parent 75d2ebc commit 2979ce7

File tree

8 files changed

+140
-10
lines changed

8 files changed

+140
-10
lines changed

examples/rdb-contract/src/lib.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn main() -> anyhow::Result<sewup::primitives::EwasmAny> {
141141
#[ewasm_test]
142142
mod tests {
143143
use super::*;
144-
use sewup::types::Raw;
144+
use sewup::types::{Raw, SizedString};
145145
use sewup_derive::{ewasm_assert_eq, ewasm_assert_ok, ewasm_auto_assert_eq, ewasm_err_output};
146146

147147
#[ewasm_test]
@@ -155,12 +155,11 @@ mod tests {
155155
let mut expect_output = create_input.clone();
156156
expect_output.set_id(1);
157157
ewasm_auto_assert_eq!(person::create(create_input), expect_output);
158-
158+
let s = SizedString::new(50)
159+
.from_str("No Day but today, Embrace who you are.")
160+
.unwrap();
159161
let post = Post {
160-
content: [
161-
sewup::types::Raw::from("No Day but today"),
162-
sewup::types::Raw::from("Embrace who you are"),
163-
],
162+
content: s.into(),
164163
person_id: 1,
165164
};
166165
let mut create_post_input = post::protocol(post);

examples/rdb-contract/src/modules.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde_derive::{Deserialize, Serialize};
22
use sewup::types::Raw;
3-
use sewup_derive::Table;
3+
use sewup_derive::{SizedString, Table};
44

55
// Table derive provides the handers for CRUD,
66
// to communicate with these handler, you will need protocol.
@@ -15,8 +15,7 @@ pub struct Person {
1515
#[derive(Table, Default, Clone, PartialEq, Serialize, Deserialize)]
1616
#[belongs_to(Person)]
1717
pub struct Post {
18-
// Use the fix size Raw to save the content
19-
pub content: [Raw; 2],
18+
pub content: SizedString!(50),
2019

2120
// Currently, this field need to set up manually, this will be enhance later
2221
pub person_id: usize,

sewup-derive/src/lib.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1409,3 +1409,27 @@ pub fn ewasm_err_output(item: TokenStream) -> TokenStream {
14091409
.parse()
14101410
.unwrap()
14111411
}
1412+
1413+
/// help you write the field which storage string no longer than the specific size
1414+
///```compile_fail
1415+
///#[derive(Table)]
1416+
///pub struct Blog {
1417+
/// pub content: SizedString!(50),
1418+
///}
1419+
///```
1420+
#[allow(non_snake_case)]
1421+
#[proc_macro_error]
1422+
#[proc_macro]
1423+
pub fn SizedString(item: TokenStream) -> TokenStream {
1424+
let num = item.to_string();
1425+
1426+
if let Ok(num) = num.trim().parse::<usize>() {
1427+
if num > 0 {
1428+
let raw_size = num / 32usize + 1;
1429+
return format!("[sewup::types::Raw; {}]", raw_size)
1430+
.parse()
1431+
.unwrap();
1432+
}
1433+
}
1434+
panic!("The input of SizedString! should be a greator than zero integer")
1435+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// This test case need following issue support
2+
// https://github.com/dtolnay/trybuild/issues/108
3+
use sewup_derive::{SizedString, Table};
4+
5+
#[derive(Table)]
6+
struct Blog {
7+
content: SizedString!(50),
8+
}
9+
10+
fn main() {}

sewup/src/types/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use thiserror::Error;
2+
3+
#[derive(Error, Debug, PartialEq)]
4+
pub enum TypeError {
5+
#[error("data size excess the limitation `{0}`")]
6+
SizeExcess(usize),
7+
}

sewup/src/types/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Sewup using Raw and Row as basic unit to storage the data
22
//! - `Raw` is the storage unit in the contract, which contains 32 bytes.
33
//! - `Row` is the list structure of `Raw`
4+
//! - `SizedString` is a structure to storage String with fixed number of Row
45
//!
56
//! It is easy to convert following types into `Raw` or `Row`:
67
//! `str`, `&str`, `String`, `&String`, `Vec<u8>`, `[u8]`, `Address`, unsigned integer types
@@ -13,3 +14,7 @@ pub use raw::*;
1314

1415
mod row;
1516
pub use row::*;
17+
18+
pub mod errors;
19+
pub mod sized_str;
20+
pub use sized_str::*;

sewup/src/types/raw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::types::*;
1313
use ewasm_api::types::Address;
1414

1515
/// The storage unit in the contract, which contains 32 bytes
16-
#[derive(Clone)]
16+
#[derive(Clone, Copy)]
1717
pub struct Raw {
1818
pub(crate) bytes: [u8; 32],
1919
// TODO: design a feature using the flag to write only needed

sewup/src/types/sized_str.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use serde_derive::{Deserialize, Serialize};
2+
3+
use anyhow::Result;
4+
5+
use crate::types::*;
6+
7+
/// SizedString is a type help you store string in with a predefined size
8+
/// ```
9+
/// let ss = sewup::types::sized_str::SizedString::new(10).from_str("hello").unwrap();
10+
/// assert!(ss.len() == 5);
11+
/// assert!(ss.capacity() == 10);
12+
/// assert_eq!(ss.to_utf8_string().unwrap(), "hello");
13+
/// ```
14+
#[derive(Clone, Serialize, Deserialize)]
15+
pub struct SizedString {
16+
/// The size of bytes limited
17+
pub capacity: usize,
18+
pub len: usize,
19+
inner: Vec<Raw>,
20+
}
21+
22+
impl SizedString {
23+
pub fn new(capacity: usize) -> Self {
24+
Self {
25+
capacity,
26+
len: 0,
27+
inner: Vec::<Raw>::with_capacity(capacity / 32usize + 1),
28+
}
29+
}
30+
pub fn from_bytes(mut self, slice: &[u8]) -> Result<Self> {
31+
if slice.len() > self.capacity {
32+
return Err(errors::TypeError::SizeExcess(self.capacity).into());
33+
}
34+
self.len = slice.len();
35+
self.inner = slice.iter().copied().collect::<Vec<u8>>().chunks(32).fold(
36+
Vec::<Raw>::new(),
37+
|mut vec, chunk| {
38+
vec.push(chunk.into());
39+
vec
40+
},
41+
);
42+
for _ in 0..(self.capacity / 32usize) - (self.len / 32usize) {
43+
self.inner.push(Raw::default());
44+
}
45+
Ok(self)
46+
}
47+
48+
pub fn from_str(self, s: &str) -> Result<Self> {
49+
self.from_bytes(s.as_bytes())
50+
}
51+
52+
pub fn to_utf8_string(&self) -> Result<String> {
53+
let buf = self.inner.iter().fold(Vec::<u8>::new(), |mut vec, raw| {
54+
vec.extend_from_slice(raw.as_ref());
55+
vec
56+
});
57+
String::from_utf8(buf[0..self.len].to_vec()).map_err(|e| e.into())
58+
}
59+
60+
pub fn capacity(&self) -> usize {
61+
self.capacity
62+
}
63+
64+
pub fn len(&self) -> usize {
65+
self.len
66+
}
67+
}
68+
69+
macro_rules! into_slice {
70+
($($s:expr),*) => {
71+
$(
72+
impl Into<[Raw; $s]> for SizedString {
73+
fn into(self) -> [Raw; $s] {
74+
let mut output = Default::default();
75+
<[Raw; $s] as AsMut<[Raw]>>::as_mut(&mut output).copy_from_slice(self.inner.as_slice());
76+
output
77+
}
78+
}
79+
)*
80+
}
81+
}
82+
83+
into_slice!(
84+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
85+
26, 27, 28, 29, 30, 31, 32
86+
);

0 commit comments

Comments
 (0)