Add documentation to core/ext and relevant macros

This commit is contained in:
PThorpe92 2025-01-18 15:57:28 -05:00
parent 9c47379927
commit bcd3ae2bd7
No known key found for this signature in database
GPG key ID: 66DB3FBACBDD05CC
3 changed files with 98 additions and 4 deletions

View file

@ -48,7 +48,7 @@ use limbo_ext::{register_extension, Value, scalar};
/// Annotate each with the scalar macro, specifying the name you would like to call it with
/// and optionally, an alias.. e.g. SELECT double(4); or SELECT twice(4);
# [scalar(name = "double", alias = "twice")]
#[scalar(name = "double", alias = "twice")]
fn double(&self, args: &[Value]) -> Value {
if let Some(arg) = args.first() {
match arg.value_type() {

View file

@ -1,5 +1,7 @@
use std::{fmt::Display, os::raw::c_void};
/// Error type is of type ExtError which can be
/// either a user defined error or an error code
#[repr(C)]
pub enum ResultCode {
OK = 0,
@ -150,6 +152,7 @@ impl Blob {
}
impl Value {
/// Creates a new Value with type Null
pub fn null() -> Self {
Self {
value_type: ValueType::Null,
@ -157,10 +160,12 @@ impl Value {
}
}
/// Returns the value type of the Value
pub fn value_type(&self) -> ValueType {
self.value_type
}
/// Returns the float value if the Value is the proper type
pub fn to_float(&self) -> Option<f64> {
if self.value.is_null() {
return None;
@ -175,7 +180,7 @@ impl Value {
_ => None,
}
}
/// Returns the text value if the Value is the proper type
pub fn to_text(&self) -> Option<String> {
if self.value_type != ValueType::Text {
return None;
@ -187,6 +192,7 @@ impl Value {
Some(String::from(txt.as_str()))
}
/// Returns the blob value if the Value is the proper type
pub fn to_blob(&self) -> Option<Vec<u8>> {
if self.value_type != ValueType::Blob {
return None;
@ -199,6 +205,7 @@ impl Value {
Some(slice.to_vec())
}
/// Returns the integer value if the Value is the proper type
pub fn to_integer(&self) -> Option<i64> {
if self.value.is_null() {
return None;
@ -214,6 +221,7 @@ impl Value {
}
}
/// Returns the error message if the value is an error
pub fn to_error(&self) -> Option<String> {
if self.value_type != ValueType::Error {
return None;
@ -234,6 +242,7 @@ impl Value {
}
}
/// Creates a new integer Value from an i64
pub fn from_integer(value: i64) -> Self {
let boxed = Box::new(value);
Self {
@ -242,6 +251,7 @@ impl Value {
}
}
/// Creates a new float Value from an f64
pub fn from_float(value: f64) -> Self {
let boxed = Box::new(value);
Self {
@ -249,7 +259,7 @@ impl Value {
value: Box::into_raw(boxed) as *mut c_void,
}
}
/// Creates a new text Value from a String
pub fn from_text(s: String) -> Self {
let buffer = s.into_boxed_str();
let ptr = buffer.as_ptr();
@ -263,6 +273,7 @@ impl Value {
}
}
/// Creates a new error Value from a ResultCode
pub fn error(err: ResultCode) -> Self {
let error = ExtError {
error_type: ErrorType::ErrCode { code: err },
@ -274,6 +285,7 @@ impl Value {
}
}
/// Create a new user defined error Value with a message
pub fn custom_error(s: String) -> Self {
let buffer = s.into_boxed_str();
let ptr = buffer.as_ptr();
@ -291,6 +303,7 @@ impl Value {
}
}
/// Creates a new blob Value from a Vec<u8>
pub fn from_blob(value: Vec<u8>) -> Self {
let boxed = Box::new(Blob::new(value.as_ptr(), value.len() as u64));
std::mem::forget(value);
@ -338,5 +351,8 @@ pub struct ExtError {
#[repr(C)]
pub enum ErrorType {
User,
ErrCode { code: ResultCode },
/// User type has a user provided message
ErrCode {
code: ResultCode,
},
}

View file

@ -138,6 +138,29 @@ fn generate_get_description(
enum_impl.parse().unwrap()
}
/// Declare a scalar function for your extension. This requires the name:
/// #[scalar(name = "example")] of what you wish to call your function with.
/// Your function __must__ use the signature: `fn (args: &[Value]) -> Value`
/// with proper spelling.
/// ```ignore
/// use limbo_ext::{scalar, Value};
/// #[scalar(name = "double", alias = "twice")] // you can provide an <optional> alias
/// fn double(args: &[Value]) -> Value {
/// match arg.value_type() {
/// ValueType::Float => {
/// let val = arg.to_float().unwrap();
/// Value::from_float(val * 2.0)
/// }
/// ValueType::Integer => {
/// let val = arg.to_integer().unwrap();
/// Value::from_integer(val * 2)
/// }
/// }
/// } else {
/// Value::null()
/// }
/// }
/// ```
#[proc_macro_attribute]
pub fn scalar(attr: TokenStream, input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as ItemFn);
@ -199,6 +222,29 @@ pub fn scalar(attr: TokenStream, input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
/// Define an aggregate function for your extension by deriving
/// AggregateDerive on a struct that implements the AggFunc trait.
/// ```ignore
/// use limbo_ext::{register_extension, Value, AggregateDerive, AggFunc};
///
///#[derive(AggregateDerive)]
///struct SumPlusOne;
///
///impl AggFunc for SumPlusOne {
/// type State = i64;
/// const NAME: &'static str = "sum_plus_one";
/// const ARGS: i32 = 1;
/// fn step(state: &mut Self::State, args: &[Value]) {
/// let Some(val) = args[0].to_integer() else {
/// return;
/// };
/// *state += val;
/// }
/// fn finalize(state: Self::State) -> Value {
/// Value::from_integer(state + 1)
/// }
///}
/// ```
#[proc_macro_derive(AggregateDerive)]
pub fn derive_agg_func(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
@ -278,6 +324,38 @@ pub fn derive_agg_func(input: TokenStream) -> TokenStream {
TokenStream::from(expanded)
}
/// Register your extension with 'core' by providing the relevant functions
///```ignore
///use limbo_ext::{register_extension, scalar, Value, AggregateDerive, AggFunc};
///
/// register_extension!{ scalars: { return_one }, aggregates: { SumPlusOne } }
///
///#[scalar(name = "one")]
///fn return_one(args: &[Value]) -> Value {
/// return Value::from_integer(1);
///}
///
///#[derive(AggregateDerive)]
///struct SumPlusOne;
///
///impl AggFunc for SumPlusOne {
/// type State = i64;
/// const NAME: &'static str = "sum_plus_one";
/// const ARGS: i32 = 1;
///
/// fn step(state: &mut Self::State, args: &[Value]) {
/// let Some(val) = args[0].to_integer() else {
/// return;
/// };
/// *state += val;
/// }
///
/// fn finalize(state: Self::State) -> Value {
/// Value::from_integer(state + 1)
/// }
///}
///
/// ```
#[proc_macro]
pub fn register_extension(input: TokenStream) -> TokenStream {
let input_ast = parse_macro_input!(input as RegisterExtensionInput);