mirror of
https://github.com/meilisearch/meilisearch.git
synced 2024-11-25 03:25:06 +08:00
Add rendering based on document trait
This commit is contained in:
parent
9e7c455a01
commit
c8189e975c
@ -3,23 +3,19 @@ use liquid::model::{
|
||||
};
|
||||
use liquid::{ObjectView, ValueView};
|
||||
|
||||
use super::document::Document;
|
||||
use super::fields::Fields;
|
||||
use super::FieldsIdsMapWithMetadata;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Context<'a> {
|
||||
document: &'a Document<'a>,
|
||||
fields: Fields<'a>,
|
||||
pub struct Context<'a, D: ObjectView, F: ArrayView> {
|
||||
document: &'a D,
|
||||
fields: &'a F,
|
||||
}
|
||||
|
||||
impl<'a> Context<'a> {
|
||||
pub fn new(document: &'a Document<'a>, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
||||
Self { document, fields: Fields::new(document, field_id_map) }
|
||||
impl<'a, D: ObjectView, F: ArrayView> Context<'a, D, F> {
|
||||
pub fn new(document: &'a D, fields: &'a F) -> Self {
|
||||
Self { document, fields }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ObjectView for Context<'a> {
|
||||
impl<'a, D: ObjectView, F: ArrayView> ObjectView for Context<'a, D, F> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
@ -56,7 +52,7 @@ impl<'a> ObjectView for Context<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ValueView for Context<'a> {
|
||||
impl<'a, D: ObjectView, F: ArrayView> ValueView for Context<'a, D, F> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
|
@ -1,10 +1,15 @@
|
||||
use std::cell::OnceCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::{self, Debug};
|
||||
|
||||
use bumpalo::Bump;
|
||||
use liquid::model::{
|
||||
DisplayCow, KString, KStringCow, ObjectRender, ObjectSource, State, Value as LiquidValue,
|
||||
ArrayView, DisplayCow, KString, KStringCow, ObjectRender, ObjectSource, ScalarCow, State,
|
||||
Value as LiquidValue,
|
||||
};
|
||||
use liquid::{ObjectView, ValueView};
|
||||
use raw_collections::{RawMap, RawVec};
|
||||
use serde_json::value::RawValue;
|
||||
|
||||
use crate::update::del_add::{DelAdd, KvReaderDelAdd};
|
||||
use crate::FieldsIdsMap;
|
||||
@ -93,7 +98,7 @@ impl<'a> ObjectView for Document<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ValueView for Document<'a> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
fn as_debug(&self) -> &dyn Debug {
|
||||
self
|
||||
}
|
||||
|
||||
@ -128,4 +133,518 @@ impl<'a> ValueView for Document<'a> {
|
||||
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn is_object(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// Implementation for any type that implements the Document trait
|
||||
use crate::update::new::document::Document as DocumentTrait;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ParseableDocument<'doc, D> {
|
||||
document: D,
|
||||
doc_alloc: &'doc Bump,
|
||||
}
|
||||
|
||||
impl<'doc, D> ParseableDocument<'doc, D> {
|
||||
pub fn new(document: D, doc_alloc: &'doc Bump) -> Self {
|
||||
Self { document, doc_alloc }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc, D: DocumentTrait<'doc> + Debug> ObjectView for ParseableDocument<'doc, D> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self) -> i64 {
|
||||
self.document.len() as i64
|
||||
}
|
||||
|
||||
fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
|
||||
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||
let (field, _) = res.unwrap();
|
||||
KStringCow::from_ref(field)
|
||||
}))
|
||||
}
|
||||
|
||||
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||
let (_, value) = res.unwrap();
|
||||
ParseableValue::new_bump(value, self.doc_alloc) as _
|
||||
}))
|
||||
}
|
||||
|
||||
fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
|
||||
Box::new(self.document.iter_top_level_fields().map(|res| {
|
||||
let (field, value) = res.unwrap();
|
||||
(KStringCow::from_ref(field), ParseableValue::new_bump(value, self.doc_alloc) as _)
|
||||
}))
|
||||
}
|
||||
|
||||
fn contains_key(&self, index: &str) -> bool {
|
||||
self.document.top_level_field(index).unwrap().is_some()
|
||||
}
|
||||
|
||||
fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
|
||||
let s = self.document.top_level_field(index).unwrap()?;
|
||||
Some(ParseableValue::new_bump(s, self.doc_alloc))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc, D: DocumentTrait<'doc> + Debug> ValueView for ParseableDocument<'doc, D> {
|
||||
fn as_debug(&self) -> &dyn fmt::Debug {
|
||||
self
|
||||
}
|
||||
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ObjectRender::new(self)))
|
||||
}
|
||||
|
||||
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ObjectSource::new(self)))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"object"
|
||||
}
|
||||
|
||||
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||
match state {
|
||||
State::Truthy => true,
|
||||
State::DefaultValue | State::Empty | State::Blank => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||
let s = ObjectRender::new(self).to_string();
|
||||
KStringCow::from_string(s)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> LiquidValue {
|
||||
LiquidValue::Object(
|
||||
self.document
|
||||
.iter_top_level_fields()
|
||||
.map(|res| {
|
||||
let (k, v) = res.unwrap();
|
||||
(k.to_string().into(), ParseableValue::new(v, self.doc_alloc).to_value())
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn is_object(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseableValue<'doc> {
|
||||
value: raw_collections::Value<'doc>,
|
||||
}
|
||||
|
||||
impl<'doc> ParseableValue<'doc> {
|
||||
pub fn new(value: &'doc RawValue, doc_alloc: &'doc Bump) -> Self {
|
||||
let value = raw_collections::Value::from_raw_value(value, doc_alloc).unwrap();
|
||||
Self { value }
|
||||
}
|
||||
|
||||
pub fn new_bump(value: &'doc RawValue, doc_alloc: &'doc Bump) -> &'doc Self {
|
||||
doc_alloc.alloc(Self::new(value, doc_alloc))
|
||||
}
|
||||
}
|
||||
|
||||
// transparent newtype for implementing ValueView
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
struct ParseableMap<'doc>(RawMap<'doc>);
|
||||
|
||||
// transparent newtype for implementing ValueView
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
struct ParseableArray<'doc>(RawVec<'doc>);
|
||||
|
||||
impl<'doc> ParseableMap<'doc> {
|
||||
pub fn as_parseable<'a>(map: &'a RawMap<'doc>) -> &'a ParseableMap<'doc> {
|
||||
// SAFETY: repr(transparent)
|
||||
unsafe { &*(map as *const RawMap as *const Self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ParseableArray<'doc> {
|
||||
pub fn as_parseable<'a>(array: &'a RawVec<'doc>) -> &'a ParseableArray<'doc> {
|
||||
// SAFETY: repr(transparent)
|
||||
unsafe { &*(array as *const RawVec as *const Self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ArrayView for ParseableArray<'doc> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self) -> i64 {
|
||||
self.0.len() as _
|
||||
}
|
||||
|
||||
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||
Box::new(self.0.iter().map(|v| ParseableValue::new_bump(v, self.0.bump()) as _))
|
||||
}
|
||||
|
||||
fn contains_key(&self, index: i64) -> bool {
|
||||
let index = convert_index(index, self.size());
|
||||
index < self.size() && index >= 0
|
||||
}
|
||||
|
||||
fn get(&self, index: i64) -> Option<&dyn ValueView> {
|
||||
let index = convert_index(index, self.size());
|
||||
if index <= 0 {
|
||||
return None;
|
||||
}
|
||||
let v = self.0.get(index as usize)?;
|
||||
Some(ParseableValue::new_bump(v, self.0.bump()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ValueView for ParseableArray<'doc> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&self) -> DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ArrayRender { s: &self.0 }))
|
||||
}
|
||||
|
||||
fn source(&self) -> DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ArraySource { s: &self.0 }))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"array"
|
||||
}
|
||||
|
||||
fn query_state(&self, state: State) -> bool {
|
||||
match state {
|
||||
State::Truthy => true,
|
||||
State::DefaultValue | State::Empty | State::Blank => self.0.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_kstr(&self) -> KStringCow<'_> {
|
||||
let s = ArrayRender { s: &self.0 }.to_string();
|
||||
KStringCow::from_string(s)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> LiquidValue {
|
||||
LiquidValue::Array(self.values().map(|v| v.to_value()).collect())
|
||||
}
|
||||
|
||||
fn is_array(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn as_array(&self) -> Option<&dyn ArrayView> {
|
||||
Some(self as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ObjectView for ParseableMap<'doc> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self) -> i64 {
|
||||
self.0.len() as i64
|
||||
}
|
||||
|
||||
fn keys<'k>(&'k self) -> Box<dyn Iterator<Item = KStringCow<'k>> + 'k> {
|
||||
Box::new(self.0.keys().map(Into::into))
|
||||
}
|
||||
|
||||
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||
Box::new(self.0.values().map(|value| {
|
||||
let doc_alloc = self.0.bump();
|
||||
ParseableValue::new_bump(value, doc_alloc) as _
|
||||
}))
|
||||
}
|
||||
|
||||
fn iter<'k>(&'k self) -> Box<dyn Iterator<Item = (KStringCow<'k>, &'k dyn ValueView)> + 'k> {
|
||||
Box::new(self.0.iter().map(|(k, v)| {
|
||||
let doc_alloc = self.0.bump();
|
||||
(k.into(), ParseableValue::new_bump(v, doc_alloc) as _)
|
||||
}))
|
||||
}
|
||||
|
||||
fn contains_key(&self, index: &str) -> bool {
|
||||
self.0.get(index).is_some()
|
||||
}
|
||||
|
||||
fn get<'s>(&'s self, index: &str) -> Option<&'s dyn ValueView> {
|
||||
let v = self.0.get(index)?;
|
||||
let doc_alloc = self.0.bump();
|
||||
let value = ParseableValue::new(v, doc_alloc);
|
||||
Some(doc_alloc.alloc(value) as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ValueView for ParseableMap<'doc> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ObjectRender::new(self)))
|
||||
}
|
||||
|
||||
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ObjectSource::new(self)))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"object"
|
||||
}
|
||||
|
||||
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||
match state {
|
||||
State::Truthy => true,
|
||||
State::DefaultValue | State::Empty | State::Blank => self.0.is_empty(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||
let s = ObjectRender::new(self).to_string();
|
||||
KStringCow::from_string(s)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> LiquidValue {
|
||||
LiquidValue::Object(
|
||||
self.0
|
||||
.iter()
|
||||
.map(|(k, v)| {
|
||||
(k.to_string().into(), ParseableValue::new(v, self.0.bump()).to_value())
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn is_object(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'doc> ValueView for ParseableValue<'doc> {
|
||||
fn as_debug(&self) -> &dyn Debug {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&self) -> DisplayCow<'_> {
|
||||
use raw_collections::value::Number;
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => LiquidValue::Nil.render(),
|
||||
Value::Bool(v) => v.render(),
|
||||
Value::Number(number) => match number {
|
||||
Number::PosInt(x) => DisplayCow::Borrowed(x),
|
||||
Number::NegInt(x) => x.render(),
|
||||
Number::Finite(x) => x.render(),
|
||||
},
|
||||
Value::String(s) => s.render(),
|
||||
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).render(),
|
||||
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).render(),
|
||||
}
|
||||
}
|
||||
|
||||
fn source(&self) -> DisplayCow<'_> {
|
||||
use raw_collections::value::Number;
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => LiquidValue::Nil.source(),
|
||||
Value::Bool(v) => ValueView::source(v),
|
||||
Value::Number(number) => match number {
|
||||
Number::PosInt(x) => DisplayCow::Borrowed(x),
|
||||
Number::NegInt(x) => x.source(),
|
||||
Number::Finite(x) => x.source(),
|
||||
},
|
||||
Value::String(s) => s.source(),
|
||||
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).source(),
|
||||
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).source(),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
use raw_collections::value::Number;
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => LiquidValue::Nil.type_name(),
|
||||
Value::Bool(v) => v.type_name(),
|
||||
Value::Number(number) => match number {
|
||||
Number::PosInt(_x) => "whole positive number",
|
||||
Number::NegInt(x) => x.type_name(),
|
||||
Number::Finite(x) => x.type_name(),
|
||||
},
|
||||
Value::String(s) => s.type_name(),
|
||||
Value::Array(_raw_vec) => "array",
|
||||
Value::Object(_raw_map) => "object",
|
||||
}
|
||||
}
|
||||
|
||||
fn query_state(&self, state: State) -> bool {
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => ValueView::query_state(&LiquidValue::Nil, state),
|
||||
Value::Bool(v) => ValueView::query_state(v, state),
|
||||
Value::Number(_number) => match state {
|
||||
State::Truthy => true,
|
||||
State::DefaultValue => false,
|
||||
State::Empty => false,
|
||||
State::Blank => false,
|
||||
},
|
||||
Value::String(s) => ValueView::query_state(s, state),
|
||||
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).query_state(state),
|
||||
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).query_state(state),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_kstr(&self) -> KStringCow<'_> {
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => ValueView::to_kstr(&LiquidValue::Nil),
|
||||
Value::Bool(v) => ValueView::to_kstr(v),
|
||||
Value::Number(_number) => self.render().to_string().into(),
|
||||
Value::String(s) => KStringCow::from_ref(*s),
|
||||
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).to_kstr(),
|
||||
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).to_kstr(),
|
||||
}
|
||||
}
|
||||
|
||||
fn to_value(&self) -> LiquidValue {
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Null => LiquidValue::Nil,
|
||||
Value::Bool(v) => LiquidValue::Scalar(liquid::model::ScalarCow::new(*v)),
|
||||
Value::Number(number) => match number {
|
||||
raw_collections::value::Number::PosInt(number) => {
|
||||
let number: i64 = match (*number).try_into() {
|
||||
Ok(number) => number,
|
||||
Err(_) => {
|
||||
return LiquidValue::Scalar(ScalarCow::new(self.render().to_string()))
|
||||
}
|
||||
};
|
||||
LiquidValue::Scalar(ScalarCow::new(number))
|
||||
}
|
||||
raw_collections::value::Number::NegInt(number) => {
|
||||
LiquidValue::Scalar(ScalarCow::new(*number))
|
||||
}
|
||||
raw_collections::value::Number::Finite(number) => {
|
||||
LiquidValue::Scalar(ScalarCow::new(*number))
|
||||
}
|
||||
},
|
||||
Value::String(s) => LiquidValue::Scalar(liquid::model::ScalarCow::new(s.to_string())),
|
||||
Value::Array(raw_vec) => ParseableArray::as_parseable(raw_vec).to_value(),
|
||||
Value::Object(raw_map) => ParseableMap::as_parseable(raw_map).to_value(),
|
||||
}
|
||||
}
|
||||
|
||||
fn as_scalar(&self) -> Option<liquid::model::ScalarCow<'_>> {
|
||||
use raw_collections::value::Number;
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Bool(v) => Some(liquid::model::ScalarCow::new(*v)),
|
||||
Value::Number(number) => match number {
|
||||
Number::PosInt(number) => {
|
||||
let number: i64 = match (*number).try_into() {
|
||||
Ok(number) => number,
|
||||
Err(_) => return Some(ScalarCow::new(self.render().to_string())),
|
||||
};
|
||||
Some(ScalarCow::new(number))
|
||||
}
|
||||
Number::NegInt(number) => Some(ScalarCow::new(*number)),
|
||||
Number::Finite(number) => Some(ScalarCow::new(*number)),
|
||||
},
|
||||
Value::String(s) => Some(ScalarCow::new(*s)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_scalar(&self) -> bool {
|
||||
use raw_collections::Value;
|
||||
match &self.value {
|
||||
Value::Bool(_) | Value::Number(_) | Value::String(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_array(&self) -> Option<&dyn liquid::model::ArrayView> {
|
||||
if let raw_collections::Value::Array(array) = &self.value {
|
||||
return Some(ParseableArray::as_parseable(array) as _);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn is_array(&self) -> bool {
|
||||
matches!(&self.value, raw_collections::Value::Array(_))
|
||||
}
|
||||
|
||||
fn as_object(&self) -> Option<&dyn ObjectView> {
|
||||
if let raw_collections::Value::Object(object) = &self.value {
|
||||
return Some(ParseableMap::as_parseable(object) as _);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn is_object(&self) -> bool {
|
||||
matches!(&self.value, raw_collections::Value::Object(_))
|
||||
}
|
||||
|
||||
fn is_nil(&self) -> bool {
|
||||
matches!(&self.value, raw_collections::Value::Null)
|
||||
}
|
||||
}
|
||||
|
||||
struct ArraySource<'s, 'doc> {
|
||||
s: &'s RawVec<'doc>,
|
||||
}
|
||||
|
||||
impl<'s, 'doc> fmt::Display for ArraySource<'s, 'doc> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[")?;
|
||||
for item in self.s {
|
||||
let v = ParseableValue::new(item, self.s.bump());
|
||||
write!(f, "{}, ", v.render())?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ArrayRender<'s, 'doc> {
|
||||
s: &'s RawVec<'doc>,
|
||||
}
|
||||
|
||||
impl<'s, 'doc> fmt::Display for ArrayRender<'s, 'doc> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for item in self.s {
|
||||
let v = ParseableValue::new(item, self.s.bump());
|
||||
|
||||
write!(f, "{}", v.render())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_index(index: i64, max_size: i64) -> i64 {
|
||||
if 0 <= index {
|
||||
index
|
||||
} else {
|
||||
max_size + index
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +1,23 @@
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use liquid::model::{
|
||||
ArrayView, DisplayCow, KStringCow, ObjectRender, ObjectSource, State, Value as LiquidValue,
|
||||
};
|
||||
use liquid::{ObjectView, ValueView};
|
||||
|
||||
use super::document::Document;
|
||||
use super::{FieldMetadata, FieldsIdsMapWithMetadata};
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Fields<'a>(Vec<FieldValue<'a>>);
|
||||
|
||||
impl<'a> Fields<'a> {
|
||||
pub fn new(document: &'a Document<'a>, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
||||
Self(
|
||||
std::iter::repeat(document)
|
||||
.zip(field_id_map.iter())
|
||||
.map(|(document, (fid, name))| FieldValue {
|
||||
document,
|
||||
name,
|
||||
metadata: field_id_map.metadata(fid).unwrap_or_default(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
use crate::GlobalFieldsIdsMap;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FieldValue<'a> {
|
||||
pub struct FieldValue<'a, D: ObjectView> {
|
||||
name: &'a str,
|
||||
document: &'a Document<'a>,
|
||||
document: &'a D,
|
||||
metadata: FieldMetadata,
|
||||
}
|
||||
|
||||
impl<'a> ValueView for FieldValue<'a> {
|
||||
impl<'a, D: ObjectView> ValueView for FieldValue<'a, D> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
@ -70,7 +57,7 @@ impl<'a> ValueView for FieldValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FieldValue<'a> {
|
||||
impl<'a, D: ObjectView> FieldValue<'a, D> {
|
||||
pub fn name(&self) -> &&'a str {
|
||||
&self.name
|
||||
}
|
||||
@ -88,7 +75,7 @@ impl<'a> FieldValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ObjectView for FieldValue<'a> {
|
||||
impl<'a, D: ObjectView> ObjectView for FieldValue<'a, D> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
@ -127,7 +114,42 @@ impl<'a> ObjectView for FieldValue<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ArrayView for Fields<'a> {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OwnedFields<'a, D: ObjectView>(Vec<FieldValue<'a, D>>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BorrowedFields<'a, 'map, D: ObjectView> {
|
||||
document: &'a D,
|
||||
field_id_map: &'a RefCell<GlobalFieldsIdsMap<'map>>,
|
||||
doc_alloc: &'a Bump,
|
||||
}
|
||||
|
||||
impl<'a, D: ObjectView> OwnedFields<'a, D> {
|
||||
pub fn new(document: &'a D, field_id_map: &'a FieldsIdsMapWithMetadata<'a>) -> Self {
|
||||
Self(
|
||||
std::iter::repeat(document)
|
||||
.zip(field_id_map.iter())
|
||||
.map(|(document, (fid, name))| FieldValue {
|
||||
document,
|
||||
name,
|
||||
metadata: field_id_map.metadata(fid).unwrap_or_default(),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'map, D: ObjectView> BorrowedFields<'a, 'map, D> {
|
||||
pub fn new(
|
||||
document: &'a D,
|
||||
field_id_map: &'a RefCell<GlobalFieldsIdsMap<'map>>,
|
||||
doc_alloc: &'a Bump,
|
||||
) -> Self {
|
||||
Self { document, field_id_map, doc_alloc }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: ObjectView> ArrayView for OwnedFields<'a, D> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self.0.as_value()
|
||||
}
|
||||
@ -149,7 +171,91 @@ impl<'a> ArrayView for Fields<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ValueView for Fields<'a> {
|
||||
impl<'a, 'map, D: ObjectView> ArrayView for BorrowedFields<'a, 'map, D> {
|
||||
fn as_value(&self) -> &dyn ValueView {
|
||||
self
|
||||
}
|
||||
|
||||
fn size(&self) -> i64 {
|
||||
self.document.size()
|
||||
}
|
||||
|
||||
fn values<'k>(&'k self) -> Box<dyn Iterator<Item = &'k dyn ValueView> + 'k> {
|
||||
Box::new(self.document.keys().map(|k| {
|
||||
let mut field_id_map = self.field_id_map.borrow_mut();
|
||||
let (_, metadata) = field_id_map.id_with_metadata_or_insert(&k).unwrap();
|
||||
let fv = self.doc_alloc.alloc(FieldValue {
|
||||
name: self.doc_alloc.alloc_str(&k),
|
||||
document: self.document,
|
||||
metadata: FieldMetadata { searchable: metadata.searchable },
|
||||
});
|
||||
fv as _
|
||||
}))
|
||||
}
|
||||
|
||||
fn contains_key(&self, index: i64) -> bool {
|
||||
let index = if index >= 0 { index } else { self.size() + index };
|
||||
index >= 0 && index < self.size()
|
||||
}
|
||||
|
||||
fn get(&self, index: i64) -> Option<&dyn ValueView> {
|
||||
let index = if index >= 0 { index } else { self.size() + index };
|
||||
let index: usize = index.try_into().ok()?;
|
||||
let key = self.document.keys().nth(index)?;
|
||||
let mut field_id_map = self.field_id_map.borrow_mut();
|
||||
let (_, metadata) = field_id_map.id_with_metadata_or_insert(&key)?;
|
||||
let fv = self.doc_alloc.alloc(FieldValue {
|
||||
name: self.doc_alloc.alloc_str(&key),
|
||||
document: self.document,
|
||||
metadata: FieldMetadata { searchable: metadata.searchable },
|
||||
});
|
||||
Some(fv as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'map, D: ObjectView> ValueView for BorrowedFields<'a, 'map, D> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
|
||||
fn render(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ArrayRender { s: self }))
|
||||
}
|
||||
|
||||
fn source(&self) -> liquid::model::DisplayCow<'_> {
|
||||
DisplayCow::Owned(Box::new(ArraySource { s: self }))
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
"array"
|
||||
}
|
||||
|
||||
fn query_state(&self, state: liquid::model::State) -> bool {
|
||||
match state {
|
||||
State::Truthy => true,
|
||||
State::DefaultValue | State::Empty | State::Blank => self.document.size() == 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_kstr(&self) -> liquid::model::KStringCow<'_> {
|
||||
let s = ArrayRender { s: self }.to_string();
|
||||
KStringCow::from_string(s)
|
||||
}
|
||||
|
||||
fn to_value(&self) -> LiquidValue {
|
||||
LiquidValue::Array(self.values().map(|v| v.to_value()).collect())
|
||||
}
|
||||
|
||||
fn as_array(&self) -> Option<&dyn ArrayView> {
|
||||
Some(self)
|
||||
}
|
||||
|
||||
fn is_array(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, D: ObjectView> ValueView for OwnedFields<'a, D> {
|
||||
fn as_debug(&self) -> &dyn std::fmt::Debug {
|
||||
self
|
||||
}
|
||||
@ -182,3 +288,39 @@ impl<'a> ValueView for Fields<'a> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
struct ArraySource<'a, 'map, D: ObjectView> {
|
||||
s: &'a BorrowedFields<'a, 'map, D>,
|
||||
}
|
||||
|
||||
impl<'a, 'map, D: ObjectView> fmt::Display for ArraySource<'a, 'map, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[")?;
|
||||
for item in self.s.values() {
|
||||
write!(f, "{}, ", item.render())?;
|
||||
}
|
||||
write!(f, "]")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct ArrayRender<'a, 'map, D: ObjectView> {
|
||||
s: &'a BorrowedFields<'a, 'map, D>,
|
||||
}
|
||||
|
||||
impl<'a, 'map, D: ObjectView> fmt::Display for ArrayRender<'a, 'map, D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
for item in self.s.values() {
|
||||
write!(f, "{}", item.render())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_index(index: i64, max_size: i64) -> i64 {
|
||||
if 0 <= index {
|
||||
index
|
||||
} else {
|
||||
max_size + index
|
||||
}
|
||||
}
|
||||
|
@ -4,17 +4,22 @@ pub(crate) mod error;
|
||||
mod fields;
|
||||
mod template_checker;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt::Debug;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::ops::Deref;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use document::ParseableDocument;
|
||||
use error::{NewPromptError, RenderPromptError};
|
||||
use fields::{BorrowedFields, OwnedFields};
|
||||
|
||||
use self::context::Context;
|
||||
use self::document::Document;
|
||||
use crate::update::del_add::DelAdd;
|
||||
use crate::{FieldId, FieldsIdsMap};
|
||||
use crate::{FieldId, FieldsIdsMap, GlobalFieldsIdsMap};
|
||||
|
||||
pub struct Prompt {
|
||||
template: liquid::Template,
|
||||
@ -109,14 +114,38 @@ impl Prompt {
|
||||
Ok(this)
|
||||
}
|
||||
|
||||
pub fn render(
|
||||
pub fn render_document<
|
||||
'a, // lifetime of the borrow of the document
|
||||
'doc: 'a, // lifetime of the allocator, will live for an entire chunk of documents
|
||||
>(
|
||||
&self,
|
||||
document: impl crate::update::new::document::Document<'a> + Debug,
|
||||
field_id_map: &RefCell<GlobalFieldsIdsMap>,
|
||||
doc_alloc: &'doc Bump,
|
||||
) -> Result<&'doc str, RenderPromptError> {
|
||||
let document = ParseableDocument::new(document, doc_alloc);
|
||||
let fields = BorrowedFields::new(&document, field_id_map, doc_alloc);
|
||||
let context = Context::new(&document, &fields);
|
||||
let mut rendered = bumpalo::collections::Vec::with_capacity_in(
|
||||
self.max_bytes.unwrap_or_else(default_max_bytes).get(),
|
||||
&doc_alloc,
|
||||
);
|
||||
self.template
|
||||
.render_to(&mut rendered, &context)
|
||||
.map_err(RenderPromptError::missing_context)?;
|
||||
Ok(std::str::from_utf8(rendered.into_bump_slice())
|
||||
.expect("render can only write UTF-8 because all inputs and processing preserve utf-8"))
|
||||
}
|
||||
|
||||
pub fn render_kvdeladd(
|
||||
&self,
|
||||
document: &obkv::KvReaderU16,
|
||||
side: DelAdd,
|
||||
field_id_map: &FieldsIdsMapWithMetadata,
|
||||
) -> Result<String, RenderPromptError> {
|
||||
let document = Document::new(document, side, field_id_map);
|
||||
let context = Context::new(&document, field_id_map);
|
||||
let fields = OwnedFields::new(&document, field_id_map);
|
||||
let context = Context::new(&document, &fields);
|
||||
|
||||
let mut rendered =
|
||||
self.template.render(&context).map_err(RenderPromptError::missing_context)?;
|
||||
|
Loading…
Reference in New Issue
Block a user