From 4b5ce1dbac3d36f66d4c571b2c87f8da79bb5c84 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 14 Jan 2019 17:03:38 -0800 Subject: [PATCH 01/26] WIP: Add new hdk::query_...() API to return ChainHeader vs. just Address --- core/src/agent/chain_store.rs | 43 ++++++++++++++++------- core/src/nucleus/ribosome/api/query.rs | 19 ++++++---- hdk-rust/src/api.rs | 19 +++++++++- wasm_utils/src/api_serialization/query.rs | 32 ++++++++++++++--- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index 270c008e9b..03a14513a1 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -63,7 +63,9 @@ impl ChainStore { entry_type_names: &[&str], start: u32, limit: u32, - ) -> Result, RibosomeErrorCode> { + entries: bool, + headers: bool, + ) -> Result { // Get entry_type name(s), if any. If empty/blank, returns the complete source chain. A // single matching entry type name with no glob pattern matching will use the single // entry_type optimization. Otherwise, we'll construct a GlobSet match and scan the list to @@ -75,13 +77,13 @@ impl ChainStore { s.chars().any(|c| is_glob(&c)) } - Ok(match entry_type_names { + let headers = match entry_type_names { [] | [""] => { // No filtering desired; uses bare .iter() let base_iter = self .iter(start_chain_header) .skip(start as usize) - .map(|header| header.entry_address().clone()); + .map(|header| header.clone()); if limit > 0 { base_iter.take(limit as usize).collect() } else { @@ -94,15 +96,12 @@ impl ChainStore { Ok(inner) => inner, Err(..) => return Err(UnknownEntryType), }; - let base_iter = self + self .iter_type(start_chain_header, &entry_type) .skip(start as usize) - .map(|header| header.entry_address().clone()); - if limit > 0 { - base_iter.take(limit as usize).collect() - } else { - base_iter.collect() - } + .map(|header| header.clone()) + .take( if limit > 0 { limit } else { usize::max_value() } ) + .collect() } rest => { // 1 or more EntryTypes, may or may not include glob wildcards. Create a @@ -129,14 +128,34 @@ impl ChainStore { > 0 }) .skip(start as usize) - .map(|header| header.entry_address().clone()); + .map(|header| header.clone()); if limit > 0 { base_iter.take(limit as usize).collect() } else { base_iter.collect() } } - }) + }; + + // We have all the matching headers in the specified start/limit range. Now, decide if + // we're returning just Vec
, or if we want Vec + if ( headers || entries ) { + Ok(QueryResult::Data( + headers.iter() + .map( |header| { + QueryResultItem { + address: header.entry_address().to_owned(), + header: if headers { Some( header ) } else { None }, + entry: None, // TODO: Get entry from header.entry_address() + } + }) + .collect())) + } else { + Ok(QueryResult::Addresses( + headers.iter() + .map( |header| header.entry_address().to_owned() ) + .collect())) + } } } diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index b0abd2f8a0..5f28461f33 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -8,7 +8,7 @@ use wasmi::{RuntimeArgs, RuntimeValue}; /// Expected complex argument: ? /// Returns an HcApiReturnCode as I32 /// -/// Specify 0 or more simple or "glob" patterns matching EntryType names. +/// Specify 0 or more simple or "glob" patterns matching EntryType names, returning Vec
. /// /// The empty String or an empty Vec matches all. The '*' glob pattern matches all simple EntryType /// names (with no '/'), while the ** pattern matches everything (use "" or [] for efficiency). @@ -59,14 +59,16 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let top = agent .top_chain_header() .expect("Should have genesis entries."); - let addresses = match query.entry_type_names { + let data = match query.entry_type_names { QueryArgsNames::QueryList(pats) => { let refs: Vec<&str> = pats.iter().map(AsRef::as_ref).collect(); // Vec -> Vec<&str> agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> Vec[&str] - query.start, - query.limit, + query.start.unwrap_or( 0 ), + query.limit.unwrap_or( 0 ), + query.entries.unwrap_or( false.into() ), + query.headers.unwrap_or( false.into() ), ) } QueryArgsNames::QueryName(name) => { @@ -74,12 +76,14 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> &[&str] - query.start, - query.limit, + query.start.unwrap_or( 0 ), + query.limit.unwrap_or( 0 ), + query.entries.unwrap_or( false.into() ), + query.headers.unwrap_or( false.into() ), ) } }; - let result = match addresses { + let result = match data { // TODO #793: the Err(_code) is the RibosomeErrorCode, but we can't import that type here. // Perhaps return chain().query should return Some(result)/None instead, and the fixed // UnknownEntryType code here, rather than trying to return a specific error code. @@ -89,3 +93,4 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult runtime.store_result(result) } + diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index e71a566f59..3cb3117408 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -989,6 +989,22 @@ pub fn query( start: u32, limit: u32, ) -> ZomeApiResult { + let data = query_entries( entry_type_names, start, limit, + false.into(), false.into() )?; + let addresses = data.iter() + .map( |i| i.address ) + .collect(); + + Ok(addresses) +} + +pub fn query_entries( + entry_type_names: QueryArgsNames, + start: u32, + limit: u32, + entries: QueryArgsEntries, + header: QueryArgsHeaders, +) -> ZomeApiResult { let mut mem_stack: SinglePageStack = unsafe { G_MEM_STACK.unwrap() }; // Put args in struct and serialize into memory @@ -998,6 +1014,8 @@ pub fn query( entry_type_names, start, limit, + entries, + headers, }, )?; @@ -1017,7 +1035,6 @@ pub fn query( Err(ZomeApiError::from(result.error)) } } - /// Sends a node-to-node message to the given agent, specified by their address. /// Addresses of agents can be accessed using [hdk::AGENT_ADDRESS](struct.AGENT_ADDRESS.html). /// This works in conjunction with the `receive` callback that has to be defined in the diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index 5a2b6f7048..fbf040c7fe 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -1,5 +1,10 @@ use holochain_core_types::{ - cas::content::Address, entry::entry_type::EntryType, error::HolochainError, json::*, + cas::content::Address, + chain_header::ChainHeader, + entry::Entry, + entry::entry_type::EntryType, + error::HolochainError, + json::*, }; // QueryArgsNames -- support querying single/multiple EntryType names @@ -48,11 +53,30 @@ impl<'a> From> for QueryArgsNames { } // Query{Args,Result} -- the query API parameters and return type +#[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] +pub struct QueryArgsEntries(bool); + +#[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] +pub struct QueryArgsHeaders(bool); + #[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] pub struct QueryArgs { pub entry_type_names: QueryArgsNames, - pub start: u32, - pub limit: u32, + pub start: Option, + pub limit: Option, + pub entries: Option, + pub headers: Option, } -pub type QueryResult = Vec
; +#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] +pub struct QueryResultItem { + header: Option, + entry: Option, +} + +#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] +#[serde(untagged)] // No type in serialized data; try deserializing QueryResultAddr, ...Data +pub enum QueryResult { + Addresses(Vec
), + EntryData(Vec), +} From 6f31bd0e8382402f3e8f00b96f0cb909bdae4e8b Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 15 Jan 2019 10:55:26 -0800 Subject: [PATCH 02/26] Builds successfully. --- core/src/agent/chain_store.rs | 112 ++++++++++++---------- core/src/nucleus/ribosome/api/query.rs | 24 +++-- wasm_utils/src/api_serialization/query.rs | 15 +-- 3 files changed, 81 insertions(+), 70 deletions(-) diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index 03a14513a1..fcba41a364 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -29,6 +29,12 @@ impl PartialEq for ChainStore { } } +#[derive(Debug, Clone)] +pub enum ChainStoreQueryResult { + Addresses(Vec
), + Headers(Vec), +} + impl ChainStore { pub fn new(content_storage: Arc>) -> Self { ChainStore { content_storage } @@ -63,9 +69,8 @@ impl ChainStore { entry_type_names: &[&str], start: u32, limit: u32, - entries: bool, headers: bool, - ) -> Result { + ) -> Result { // Get entry_type name(s), if any. If empty/blank, returns the complete source chain. A // single matching entry type name with no glob pattern matching will use the single // entry_type optimization. Otherwise, we'll construct a GlobSet match and scan the list to @@ -77,17 +82,23 @@ impl ChainStore { s.chars().any(|c| is_glob(&c)) } - let headers = match entry_type_names { - [] | [""] => { + let vector = match entry_type_names { // Vec
or Vec + [] | [""] | ["**"] => { // No filtering desired; uses bare .iter() - let base_iter = self - .iter(start_chain_header) - .skip(start as usize) - .map(|header| header.clone()); - if limit > 0 { - base_iter.take(limit as usize).collect() + if headers { + ChainStoreQueryResult::Headers( + self.iter(start_chain_header) + .skip(start as usize) + .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .map(|header| header.to_owned()) + .collect::>()) } else { - base_iter.collect() + ChainStoreQueryResult::Addresses( + self.iter(start_chain_header) + .skip(start as usize) + .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .map(|header| header.entry_address().to_owned()) + .collect::>()) } } [one] if !is_glob_str(one) => { @@ -96,12 +107,21 @@ impl ChainStore { Ok(inner) => inner, Err(..) => return Err(UnknownEntryType), }; - self - .iter_type(start_chain_header, &entry_type) - .skip(start as usize) - .map(|header| header.clone()) - .take( if limit > 0 { limit } else { usize::max_value() } ) - .collect() + if headers { + ChainStoreQueryResult::Headers( + self.iter_type(start_chain_header, &entry_type) + .skip(start as usize) + .take( if limit > 0 { limit as usize } else { usize::max_value() } ) + .map(|header| header.to_owned()) + .collect::>()) + } else { + ChainStoreQueryResult::Addresses( + self.iter_type(start_chain_header, &entry_type) + .skip(start as usize) + .take( if limit > 0 { limit as usize } else { usize::max_value() } ) + .map(|header| header.entry_address().to_owned()) + .collect::>()) + } } rest => { // 1 or more EntryTypes, may or may not include glob wildcards. Create a @@ -119,43 +139,37 @@ impl ChainStore { ); } let globset = builder.build().map_err(|_| UnknownEntryType)?; - let base_iter = self - .iter(start_chain_header) - .filter(|header| { - globset - .matches(String::from((*header.entry_type()).clone())) - .len() - > 0 - }) - .skip(start as usize) - .map(|header| header.clone()); - if limit > 0 { - base_iter.take(limit as usize).collect() + if headers { + ChainStoreQueryResult::Headers( + self.iter(start_chain_header) + .filter(|header| { + globset + .matches(header.entry_type().to_string()) + .len() + > 0 + }) + .skip(start as usize) + .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .map(|header| header.to_owned()) + .collect::>()) } else { - base_iter.collect() + ChainStoreQueryResult::Addresses( + self.iter(start_chain_header) + .filter(|header| { + globset + .matches(header.entry_type().to_string()) + .len() + > 0 + }) + .skip(start as usize) + .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .map(|header| header.entry_address().to_owned()) + .collect::>()) } } }; - // We have all the matching headers in the specified start/limit range. Now, decide if - // we're returning just Vec
, or if we want Vec - if ( headers || entries ) { - Ok(QueryResult::Data( - headers.iter() - .map( |header| { - QueryResultItem { - address: header.entry_address().to_owned(), - header: if headers { Some( header ) } else { None }, - entry: None, // TODO: Get entry from header.entry_address() - } - }) - .collect())) - } else { - Ok(QueryResult::Addresses( - headers.iter() - .map( |header| header.entry_address().to_owned() ) - .collect())) - } + Ok(vector) } } diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 5f28461f33..aa28ced925 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -1,5 +1,8 @@ -use crate::nucleus::ribosome::{api::ZomeApiResult, Runtime}; -use holochain_wasm_utils::api_serialization::{QueryArgs, QueryArgsNames}; +use crate:: { + nucleus::ribosome::{api::ZomeApiResult, Runtime}, + agent::chain_store::ChainStoreQueryResult, +}; +use holochain_wasm_utils::api_serialization::{QueryArgs, QueryArgsNames, QueryResult}; use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; @@ -59,7 +62,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let top = agent .top_chain_header() .expect("Should have genesis entries."); - let data = match query.entry_type_names { + let maybe_result = match query.entry_type_names { // Result QueryArgsNames::QueryList(pats) => { let refs: Vec<&str> = pats.iter().map(AsRef::as_ref).collect(); // Vec -> Vec<&str> agent.chain().query( @@ -67,8 +70,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult refs.as_slice(), // Vec<&str> -> Vec[&str] query.start.unwrap_or( 0 ), query.limit.unwrap_or( 0 ), - query.entries.unwrap_or( false.into() ), - query.headers.unwrap_or( false.into() ), + query.headers.unwrap_or( false ), ) } QueryArgsNames::QueryName(name) => { @@ -78,16 +80,20 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult refs.as_slice(), // Vec<&str> -> &[&str] query.start.unwrap_or( 0 ), query.limit.unwrap_or( 0 ), - query.entries.unwrap_or( false.into() ), - query.headers.unwrap_or( false.into() ), + query.headers.unwrap_or( false ), ) } }; - let result = match data { + let result = match maybe_result { // TODO #793: the Err(_code) is the RibosomeErrorCode, but we can't import that type here. // Perhaps return chain().query should return Some(result)/None instead, and the fixed // UnknownEntryType code here, rather than trying to return a specific error code. - Ok(result) => Ok(result), + Ok(result) => { + Ok(match result { + ChainStoreQueryResult::Addresses(addresses) => QueryResult::Addresses(addresses), + ChainStoreQueryResult::Headers(headers) => QueryResult::Headers(headers), + }) + } Err(_code) => return ribosome_error_code!(UnknownEntryType), }; diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index fbf040c7fe..bdef528575 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -52,20 +52,12 @@ impl<'a> From> for QueryArgsNames { } } -// Query{Args,Result} -- the query API parameters and return type -#[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] -pub struct QueryArgsEntries(bool); - -#[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] -pub struct QueryArgsHeaders(bool); - #[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] pub struct QueryArgs { pub entry_type_names: QueryArgsNames, - pub start: Option, + pub start: Option, // TODO: These should be "typed", so order cannot be confued pub limit: Option, - pub entries: Option, - pub headers: Option, + pub headers: Option, } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] @@ -75,8 +67,7 @@ pub struct QueryResultItem { } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] -#[serde(untagged)] // No type in serialized data; try deserializing QueryResultAddr, ...Data pub enum QueryResult { Addresses(Vec
), - EntryData(Vec), + Headers(Vec), } From 322d593975b7206aafce44b9f181108831c87acf Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 15 Jan 2019 11:48:37 -0800 Subject: [PATCH 03/26] Add a bit more commentary to query.rs API re: parameter type safety --- wasm_utils/src/api_serialization/query.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index bdef528575..291c6a5a5d 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -56,7 +56,7 @@ impl<'a> From> for QueryArgsNames { pub struct QueryArgs { pub entry_type_names: QueryArgsNames, pub start: Option, // TODO: These should be "typed", so order cannot be confued - pub limit: Option, + pub limit: Option, // see: https://rust-lang-nursery.github.io/api-guidelines/type-safety.html pub headers: Option, } From 8214cb80a754758dc022e6f6bd4fcb38a07468c7 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 15 Jan 2019 15:51:27 -0800 Subject: [PATCH 04/26] Renovate hdk::query/query_result and chain_store query APIs o Package up individual options into ...Options packages o Change u32 options to usize o Retain simple Vec
for original hdk::query o chain_store.query now returns an enum, so handle appropriately --- core/src/agent/chain_store.rs | 167 ++++++++++++++-------- core/src/nucleus/ribosome/api/query.rs | 31 ++-- hdk-rust/src/api.rs | 42 +++--- hdk-rust/tests/integration_test.rs | 3 +- wasm_utils/src/api_serialization/query.rs | 19 +-- 5 files changed, 164 insertions(+), 98 deletions(-) diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index fcba41a364..21b527b0f4 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -29,7 +29,14 @@ impl PartialEq for ChainStore { } } -#[derive(Debug, Clone)] +#[derive(Default, Debug, Clone)] +pub struct ChainStoreQueryOptions { + pub start: Option, + pub limit: Option, + pub headers: Option, +} + +#[derive(Debug)] pub enum ChainStoreQueryResult { Addresses(Vec
), Headers(Vec), @@ -63,13 +70,12 @@ impl ChainStore { ) } + // Supply a None for options to get defaults (all elements, no ChainHeaders just Addresses) pub fn query( &self, start_chain_header: &Option, entry_type_names: &[&str], - start: u32, - limit: u32, - headers: bool, + options: Option ) -> Result { // Get entry_type name(s), if any. If empty/blank, returns the complete source chain. A // single matching entry type name with no glob pattern matching will use the single @@ -82,21 +88,34 @@ impl ChainStore { s.chars().any(|c| is_glob(&c)) } + // Unpack options; start == 0 --> start at beginning, limit == 0 --> take all remaining + let ( start, limit, headers ) = match options { + None => ( 0_usize, usize::max_value(), false ), + Some(o) => ( + o.start.unwrap_or( 0_usize ), + match o.limit { + Some(l) => if l == 0 { usize::max_value() } else { l }, + None => usize::max_value() + }, + o.headers.unwrap_or( false ) + ) + }; + let vector = match entry_type_names { // Vec
or Vec [] | [""] | ["**"] => { // No filtering desired; uses bare .iter() if headers { ChainStoreQueryResult::Headers( self.iter(start_chain_header) - .skip(start as usize) - .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .skip(start) + .take(limit) .map(|header| header.to_owned()) .collect::>()) } else { ChainStoreQueryResult::Addresses( self.iter(start_chain_header) - .skip(start as usize) - .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .skip(start) + .take(limit) .map(|header| header.entry_address().to_owned()) .collect::>()) } @@ -110,15 +129,15 @@ impl ChainStore { if headers { ChainStoreQueryResult::Headers( self.iter_type(start_chain_header, &entry_type) - .skip(start as usize) - .take( if limit > 0 { limit as usize } else { usize::max_value() } ) + .skip(start) + .take(limit) .map(|header| header.to_owned()) .collect::>()) } else { ChainStoreQueryResult::Addresses( self.iter_type(start_chain_header, &entry_type) - .skip(start as usize) - .take( if limit > 0 { limit as usize } else { usize::max_value() } ) + .skip(start) + .take(limit) .map(|header| header.entry_address().to_owned()) .collect::>()) } @@ -148,8 +167,8 @@ impl ChainStore { .len() > 0 }) - .skip(start as usize) - .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .skip(start) + .take(limit) .map(|header| header.to_owned()) .collect::>()) } else { @@ -161,8 +180,8 @@ impl ChainStore { .len() > 0 }) - .skip(start as usize) - .take(if limit > 0 { limit as usize } else { usize::max_value() }) + .skip(start) + .take(limit) .map(|header| header.entry_address().to_owned()) .collect::>()) } @@ -284,7 +303,7 @@ impl Iterator for ChainStoreTypeIterator { pub mod tests { extern crate tempfile; use self::tempfile::tempdir; - use crate::agent::chain_store::ChainStore; + use crate::agent::chain_store::{ ChainStore, ChainStoreQueryOptions, ChainStoreQueryResult, }; use holochain_cas_implementations::cas::file::FilesystemStorage; use holochain_core_types::{ cas::content::AddressableContent, @@ -497,14 +516,17 @@ pub mod tests { .expect("could not add header to cas"); // First, lets see if we can find the EntryType "testEntryTypeB" Entries - let found = chain_store + let found = match chain_store .query( &Some(chain_header_e.clone()), &vec![test_entry_type_b().to_string().as_ref()], - 0, - 0, + None, ) - .unwrap(); + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; + let expected = vec![ chain_header_c.entry_address().clone(), chain_header_b.entry_address().clone(), @@ -512,21 +534,32 @@ pub mod tests { assert_eq!(expected, found); // Then, limit to 1 at a time, starting from the 0'th match - let found = chain_store + let found = match chain_store .query( &Some(chain_header_e.clone()), &vec![test_entry_type_b().to_string().as_ref()], - 0, - 1, + Some(ChainStoreQueryOptions { + start: Some(0), + limit: Some(1), + headers: Some(false), + }) ) - .unwrap(); - let expected = vec![chain_header_c.entry_address().clone()]; + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; + let expected = vec![ + chain_header_c.entry_address().clone() + ]; assert_eq!(expected, found); // Now query for all EntryTypes via entry_type == None - let found = chain_store - .query(&Some(chain_header_e.clone()), &[], 0, 0) - .unwrap(); + let found = match chain_store + .query(&Some(chain_header_e.clone()), &[], None) + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_e.entry_address().clone(), chain_header_d.entry_address().clone(), @@ -539,36 +572,42 @@ pub mod tests { // Test Glob matching, namespacing. // Wildcard glob, all paths - let found = chain_store + let found = match chain_store .query( &Some(chain_header_e.clone()), &vec!["**".to_string().as_ref()], - 0, - 0, + None, ) - .unwrap(); + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; assert_eq!(expected, found); // Globbing plus some arbitrary EntryType names, thus matches everything again - let found = chain_store + let found = match chain_store .query( &Some(chain_header_e.clone()), &vec!["another/*".to_string().as_ref(), "testEntryType*"], - 0, - 0, + None, ) - .unwrap(); + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; assert_eq!(expected, found); // Just globbing - let found = chain_store + let found = match chain_store .query( &Some(chain_header_e.clone()), &vec!["another/*".to_string().as_ref()], - 0, - 0, + None, ) - .unwrap(); + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_e.entry_address().clone(), chain_header_d.entry_address().clone(), @@ -619,14 +658,16 @@ pub mod tests { .expect("could not add header to cas"); // Multiple complex globs. The leading '**/' matches 0 or more leading .../ segments, so returns - let found = chain_store + let found = match chain_store .query( &Some(chain_header_h.clone()), &vec!["another/*", "ns/**/t*"], - 0, - 0, + None, ) - .unwrap(); + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_h.entry_address().clone(), chain_header_g.entry_address().clone(), @@ -636,9 +677,12 @@ pub mod tests { assert_eq!(expected, found); // So, we should be able to find EntryType names by suffix at any depth - let found = chain_store - .query(&Some(chain_header_h.clone()), &vec!["**/*{e,B}"], 0, 0) - .unwrap(); + let found = match chain_store + .query(&Some(chain_header_h.clone()), &vec!["**/*{e,B}"], None) + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_h.entry_address().clone(), // .../three chain_header_f.entry_address().clone(), // .../one @@ -667,26 +711,35 @@ pub mod tests { .expect("could not add header to cas"); // Find EntryTypes which are/not System (start with '%'), and end in 'e' - let found = chain_store - .query(&Some(chain_header_i.clone()), &vec!["[!%]*e"], 0, 0) - .unwrap(); + let found = match chain_store + .query(&Some(chain_header_i.clone()), &vec!["[!%]*e"], None) + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_a.entry_address().clone(), // testEntryType ]; assert_eq!(expected, found); // Including all namespaced EntryTypes - let found = chain_store - .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], 0, 0) - .unwrap(); + let found = match chain_store + .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], None) + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_h.entry_address().clone(), // .../three chain_header_f.entry_address().clone(), // .../one chain_header_a.entry_address().clone(), // testEntryType ]; assert_eq!(expected, found); - let found = chain_store - .query(&Some(chain_header_i.clone()), &vec!["%*e"], 0, 0) - .unwrap(); + let found = match chain_store + .query(&Some(chain_header_i.clone()), &vec!["%*e"], None) + .unwrap() { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other ), + }; let expected = vec![ chain_header_i.entry_address().clone(), // %system_entry_type ]; diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index aa28ced925..9370e8e795 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -1,8 +1,14 @@ use crate:: { - nucleus::ribosome::{api::ZomeApiResult, Runtime}, - agent::chain_store::ChainStoreQueryResult, + nucleus::ribosome::{ + api::ZomeApiResult, Runtime, + }, + agent::chain_store::{ + ChainStoreQueryOptions, ChainStoreQueryResult, + } +}; +use holochain_wasm_utils::api_serialization::{ + QueryArgs, QueryArgsNames, QueryArgsOptions, QueryResult, }; -use holochain_wasm_utils::api_serialization::{QueryArgs, QueryArgsNames, QueryResult}; use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; @@ -50,7 +56,7 @@ use wasmi::{RuntimeArgs, RuntimeValue}; /// // **/ Zero or more of any namespace component /// pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { - // deserialize args + // deserialize args. let args_str = runtime.load_json_string_from_args(&args); let query = match QueryArgs::try_from(args_str) { Ok(input) => input, @@ -62,15 +68,18 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let top = agent .top_chain_header() .expect("Should have genesis entries."); + let options = query.options.unwrap_or( QueryArgsOptions::default() ); let maybe_result = match query.entry_type_names { // Result QueryArgsNames::QueryList(pats) => { let refs: Vec<&str> = pats.iter().map(AsRef::as_ref).collect(); // Vec -> Vec<&str> agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> Vec[&str] - query.start.unwrap_or( 0 ), - query.limit.unwrap_or( 0 ), - query.headers.unwrap_or( false ), + Some(ChainStoreQueryOptions { + start: options.start, + limit: options.limit, + headers: options.headers, + }) ) } QueryArgsNames::QueryName(name) => { @@ -78,9 +87,11 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> &[&str] - query.start.unwrap_or( 0 ), - query.limit.unwrap_or( 0 ), - query.headers.unwrap_or( false ), + Some(ChainStoreQueryOptions { + start: options.start, + limit: options.limit, + headers: options.headers, + }) ) } }; diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index 3cb3117408..d56ee7b467 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -22,7 +22,7 @@ use holochain_wasm_utils::{ get_links::{GetLinksArgs, GetLinksOptions, GetLinksResult}, link_entries::LinkEntriesArgs, send::SendArgs, - QueryArgs, QueryArgsNames, QueryResult, UpdateEntryArgs, ZomeFnCallArgs, + QueryArgs, QueryArgsNames, QueryArgsOptions, QueryResult, UpdateEntryArgs, ZomeFnCallArgs, }, holochain_core_types::{ hash::HashString, @@ -986,25 +986,28 @@ pub fn get_links_and_load>( /// ``` pub fn query( entry_type_names: QueryArgsNames, - start: u32, - limit: u32, -) -> ZomeApiResult { - let data = query_entries( entry_type_names, start, limit, - false.into(), false.into() )?; - let addresses = data.iter() - .map( |i| i.address ) - .collect(); - - Ok(addresses) + start: usize, + limit: usize, +) -> ZomeApiResult> { + // The hdk::query API always returns a simple Vec
+ match query_result( entry_type_names, + QueryArgsOptions { + start: Some(start), + limit: Some(limit), + headers: None, + }) { + Ok(result) => match result { + QueryResult::Addresses(addresses) => Ok(addresses), + _ => return Err(ZomeApiError::FunctionNotImplemented), // should never occur + } + Err(e) => Err(e), + } } -pub fn query_entries( +pub fn query_result( entry_type_names: QueryArgsNames, - start: u32, - limit: u32, - entries: QueryArgsEntries, - header: QueryArgsHeaders, -) -> ZomeApiResult { + options: QueryArgsOptions, +) -> ZomeApiResult { let mut mem_stack: SinglePageStack = unsafe { G_MEM_STACK.unwrap() }; // Put args in struct and serialize into memory @@ -1012,10 +1015,7 @@ pub fn query_entries( &mut mem_stack, QueryArgs { entry_type_names, - start, - limit, - entries, - headers, + options: Some(options), }, )?; diff --git a/hdk-rust/tests/integration_test.rs b/hdk-rust/tests/integration_test.rs index 70ed49f0f0..d6b0cf6819 100755 --- a/hdk-rust/tests/integration_test.rs +++ b/hdk-rust/tests/integration_test.rs @@ -35,7 +35,6 @@ use holochain_wasm_utils::{ api_serialization::{ get_entry::{GetEntryResult, StatusRequestKind}, get_links::GetLinksResult, - QueryResult, }, wasm_target_dir, }; @@ -570,7 +569,7 @@ fn can_check_query() { ); assert!(result.is_ok(), "result = {:?}", result); - let expected: ZomeApiResult = Ok(vec![Address::from( + let expected: ZomeApiResult> = Ok(vec![Address::from( "QmPn1oj8ANGtxS5sCGdKBdSBN63Bb6yBkmWrLc9wFRYPtJ", )]); diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index 291c6a5a5d..ac685af3bf 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -1,7 +1,7 @@ use holochain_core_types::{ cas::content::Address, chain_header::ChainHeader, - entry::Entry, + //entry::Entry, entry::entry_type::EntryType, error::HolochainError, json::*, @@ -52,22 +52,25 @@ impl<'a> From> for QueryArgsNames { } } +// All version of the hdk::query...() API vector to these Args #[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] pub struct QueryArgs { pub entry_type_names: QueryArgsNames, - pub start: Option, // TODO: These should be "typed", so order cannot be confued - pub limit: Option, // see: https://rust-lang-nursery.github.io/api-guidelines/type-safety.html - pub headers: Option, + pub options: Option, } -#[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] -pub struct QueryResultItem { - header: Option, - entry: Option, +#[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] +pub struct QueryArgsOptions { + pub start: Option, + pub limit: Option, + pub headers: Option, } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] +#[serde(untagged)] // No type in serialized data; try deserializing as a String, then as a Vec pub enum QueryResult { Addresses(Vec
), Headers(Vec), + // Entries(Vec), + // Everything(Vec<(ChainHeader,Entry)) } From 9f8fb69b1a064d02fc20fb02368cadc5eef6c796 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 15 Jan 2019 17:12:39 -0800 Subject: [PATCH 05/26] Improve documenation to hdk::query and hdk::query_result WIP --- hdk-rust/src/api.rs | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index d56ee7b467..e6ba3851ab 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -951,15 +951,21 @@ pub fn get_links_and_load>( Ok(entries) } -/// Returns a list of entries from your local source chain, that match a given entry type name or names. +/// Returns a list of entries from your local source chain that match a given entry type name or names. /// -/// Each name may be a plain entry type name, or a "glob" pattern such as "prefix/*" (matches all -/// entry types starting with "prefix/"), or "[!%]*e" (matches all non-system non-name-spaced entry -/// types ending in "e"). All names and patterns are merged into a single efficient Regular -/// Expression for scanning. +/// Each name may be a plain entry type name, or a `"glob"` pattern. All names and patterns are +/// merged into a single efficient Regular Expression for scanning. +/// +/// You can select many names with patterns such as `"boo*"` (match all entry types starting with +/// `"boo"`), or `"[!%]*e"` (all non-system non-name-spaced entry types ending in `"e"`). +/// +/// You can organize your entry types using simple name-spaces, by including `"/"` in your entry type +/// names. For example, if you have several entry types related to fizzing a widget, you might +/// create entry types `"fizz/bar"`, `"fizz/baz"`, `"fizz/qux/foo"` and `"fizz/qux/boo"`. Query for +/// `"fizz/**"` to match them all. /// -/// Entry type name-spaces are supported by including "/" in your entry type names; use vec![], "", -/// or "**" to match all names in all name-spaces, "*" to match all non-namespaced names. +/// Use vec![], `""`, or `"**"` to match all names in all name-spaces. Matching `"*"` will match only +/// non-namespaced names. /// /// entry_type_names: Specify type of entry(s) to retrieve, as a String or Vec of 0 or more names, converted into the QueryArgNames type /// start: First entry in result list to retrieve @@ -984,6 +990,13 @@ pub fn get_links_and_load>( /// } /// # } /// ``` +/// +/// With hdk::query_result, you can specify a package of QueryArgsOptions, including the ability +/// to get a Vec: +/// +/// pub fn get_post_headers() -> ZomeApiResult { +/// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) +/// } pub fn query( entry_type_names: QueryArgsNames, start: usize, From 99194eb1f178e70bde7ec5625d11c8dbdd8adbd9 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Wed, 16 Jan 2019 05:12:39 -0800 Subject: [PATCH 06/26] Tidy up documentation a bit --- core/src/nucleus/ribosome/api/query.rs | 32 +++++++++++++------------- hdk-rust/src/api.rs | 7 +++--- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 9370e8e795..0d36973c23 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -22,38 +22,38 @@ use wasmi::{RuntimeArgs, RuntimeValue}; /// The empty String or an empty Vec matches all. The '*' glob pattern matches all simple EntryType /// names (with no '/'), while the ** pattern matches everything (use "" or [] for efficiency). /// -/// // [ ] -/// // [ "" ] -/// // [ "**" ] +/// `[]` +/// `[""]` +/// `["**"]` /// /// Namespaces (groups of related EntryType names) can be queried easily, eg: /// -/// // [ "name/*" ] +/// `["name/*"]` /// /// Several simple names and/or "glob" patterns can be supplied, and are efficiently /// searched for in a single pass using a single efficient Regular Expression engine: /// -/// // [ "name/*", "and_another", "SomethingElse" ] +/// `["name/*", "and_another", "SomethingElse"]` /// /// EntryType names can be excluded, eg. to return every simple (non-namespaced) EntryType except System: /// -/// // [ "[!%]*" ] +/// `["[!%]*"]` /// /// To match a pattern, including all namespaced EntryType names, eg. every EntryType except System: /// -/// // [ "**/[!%]*" ] +/// `["**/[!%]*"]` /// /// The following standard "glob" patterns are supported: /// -/// // Pattern Match -/// // ======= ===== -/// // . One character (other than a '/') -/// // [abcd] One of a set of characters -/// // [a-d] One of a range of characters -/// // [!a-d] Not one of range of characters -/// // {abc,123} One of a number of sequences of characters -/// // * Zero or more of any character -/// // **/ Zero or more of any namespace component +/// Pattern Match +/// ======= ===== +/// `.` One character (other than a '/') +/// `[abcd]` One of a set of characters +/// `[a-d]` One of a range of characters +/// `[!a-d]` Not one of range of characters +/// `{abc,123}` One of a number of sequences of characters +/// `*` Zero or more of any character +/// `**/` Zero or more of any namespace component /// pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult { // deserialize args. diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index e6ba3851ab..c16224bb4e 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -991,12 +991,13 @@ pub fn get_links_and_load>( /// # } /// ``` /// -/// With hdk::query_result, you can specify a package of QueryArgsOptions, including the ability -/// to get a Vec: +/// With hdk::query_result, you can specify a package of optional QueryArgsOptions, and get a +/// variety of return values, including a vector of Headers as a `Vec`: /// /// pub fn get_post_headers() -> ZomeApiResult { -/// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) +/// hdk::query_result("post".into(), Some(QueryArgsOptions{ headers: true, ..Default::default()})) /// } +/// pub fn query( entry_type_names: QueryArgsNames, start: usize, From 159061ca31508473ef26ff785babe8e4cc0563b2 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Wed, 16 Jan 2019 05:23:58 -0800 Subject: [PATCH 07/26] Make the clean: target more effective at restoring build environment --- Makefile | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 24231d0947..3d4ff11734 100644 --- a/Makefile +++ b/Makefile @@ -212,8 +212,16 @@ ${C_BINDING_TESTS}: # clean up the target directory and all extraneous "C" binding test files clean: ${C_BINDING_CLEAN} - -@$(RM) -rf target - -@$(RM) -rf wasm_utils/wasm-test/integration-test/target + @for target in $$( find . -type d -a -name 'target' ); do \ + echo -e "\033[0;93m## Removing $${target} ##\033[0m"; \ + $(RM) -rf $${target}; \ + done + @$(RM) -rf nodejs_container/dist + @$(RM) -rf app_spec/dist + @for cargo in $$( find . -name 'Cargo.toml' ); do \ + echo -e "\033[0;93m## 'cargo update' in $${cargo%/*} ##\033[0m"; \ + ( cd $${cargo%/*} && cargo update ); \ + done # clean up the extraneous "C" binding test files ${C_BINDING_CLEAN}: From 901aaa636f67726404b508d763674fb1d9340726 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Wed, 16 Jan 2019 06:58:50 -0800 Subject: [PATCH 08/26] Clean up hdk and chain_store query APIs o Simplify by removing some Option-ality o Add some unit tests --- core/src/agent/chain_store.rs | 76 ++++++++++++----------- core/src/nucleus/ribosome/api/query.rs | 23 ++++--- hdk-rust/src/api.rs | 12 ++-- hdk-rust/wasm-test/src/lib.rs | 23 ++++++- wasm_utils/src/api_serialization/query.rs | 8 +-- 5 files changed, 83 insertions(+), 59 deletions(-) diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index 21b527b0f4..ce907b68a5 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -31,9 +31,9 @@ impl PartialEq for ChainStore { #[derive(Default, Debug, Clone)] pub struct ChainStoreQueryOptions { - pub start: Option, - pub limit: Option, - pub headers: Option, + pub start: usize, + pub limit: usize, + pub headers: bool, } #[derive(Debug)] @@ -75,7 +75,7 @@ impl ChainStore { &self, start_chain_header: &Option, entry_type_names: &[&str], - options: Option + options: ChainStoreQueryOptions ) -> Result { // Get entry_type name(s), if any. If empty/blank, returns the complete source chain. A // single matching entry type name with no glob pattern matching will use the single @@ -89,17 +89,9 @@ impl ChainStore { } // Unpack options; start == 0 --> start at beginning, limit == 0 --> take all remaining - let ( start, limit, headers ) = match options { - None => ( 0_usize, usize::max_value(), false ), - Some(o) => ( - o.start.unwrap_or( 0_usize ), - match o.limit { - Some(l) => if l == 0 { usize::max_value() } else { l }, - None => usize::max_value() - }, - o.headers.unwrap_or( false ) - ) - }; + let start = options.start; + let limit = if options.limit == 0 { usize::max_value() } else { options.limit }; + let headers = options.headers; let vector = match entry_type_names { // Vec
or Vec [] | [""] | ["**"] => { @@ -520,7 +512,7 @@ pub mod tests { .query( &Some(chain_header_e.clone()), &vec![test_entry_type_b().to_string().as_ref()], - None, + ChainStoreQueryOptions::default(), ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -538,11 +530,11 @@ pub mod tests { .query( &Some(chain_header_e.clone()), &vec![test_entry_type_b().to_string().as_ref()], - Some(ChainStoreQueryOptions { - start: Some(0), - limit: Some(1), - headers: Some(false), - }) + ChainStoreQueryOptions { + start: 0, + limit: 1, + headers: false, + } ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -555,7 +547,7 @@ pub mod tests { // Now query for all EntryTypes via entry_type == None let found = match chain_store - .query(&Some(chain_header_e.clone()), &[], None) + .query(&Some(chain_header_e.clone()), &[], ChainStoreQueryOptions::default()) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, other => panic!("Unexpected query value {:?}", other ), @@ -576,7 +568,7 @@ pub mod tests { .query( &Some(chain_header_e.clone()), &vec!["**".to_string().as_ref()], - None, + ChainStoreQueryOptions::default(), ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -589,7 +581,7 @@ pub mod tests { .query( &Some(chain_header_e.clone()), &vec!["another/*".to_string().as_ref(), "testEntryType*"], - None, + ChainStoreQueryOptions::default(), ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -602,7 +594,7 @@ pub mod tests { .query( &Some(chain_header_e.clone()), &vec!["another/*".to_string().as_ref()], - None, + ChainStoreQueryOptions::default(), ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -662,7 +654,7 @@ pub mod tests { .query( &Some(chain_header_h.clone()), &vec!["another/*", "ns/**/t*"], - None, + ChainStoreQueryOptions::default(), ) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, @@ -678,7 +670,7 @@ pub mod tests { // So, we should be able to find EntryType names by suffix at any depth let found = match chain_store - .query(&Some(chain_header_h.clone()), &vec!["**/*{e,B}"], None) + .query(&Some(chain_header_h.clone()), &vec!["**/*{e,B}"], ChainStoreQueryOptions::default()) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, other => panic!("Unexpected query value {:?}", other ), @@ -712,7 +704,7 @@ pub mod tests { // Find EntryTypes which are/not System (start with '%'), and end in 'e' let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["[!%]*e"], None) + .query(&Some(chain_header_i.clone()), &vec!["[!%]*e"], ChainStoreQueryOptions::default()) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, other => panic!("Unexpected query value {:?}", other ), @@ -721,29 +713,43 @@ pub mod tests { chain_header_a.entry_address().clone(), // testEntryType ]; assert_eq!(expected, found); - // Including all namespaced EntryTypes + let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], None) + .query(&Some(chain_header_i.clone()), &vec!["%*e"], ChainStoreQueryOptions::default()) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, other => panic!("Unexpected query value {:?}", other ), }; let expected = vec![ - chain_header_h.entry_address().clone(), // .../three - chain_header_f.entry_address().clone(), // .../one - chain_header_a.entry_address().clone(), // testEntryType + chain_header_i.entry_address().clone(), // %system_entry_type ]; assert_eq!(expected, found); + + // Including all namespaced EntryTypes let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["%*e"], None) + .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], ChainStoreQueryOptions::default()) .unwrap() { ChainStoreQueryResult::Addresses(addresses) => addresses, other => panic!("Unexpected query value {:?}", other ), }; let expected = vec![ - chain_header_i.entry_address().clone(), // %system_entry_type + chain_header_h.entry_address().clone(), // .../three + chain_header_f.entry_address().clone(), // .../one + chain_header_a.entry_address().clone(), // testEntryType ]; assert_eq!(expected, found); + + // Including all namespaced EntryTypes, getting ChainHeader + let found = match chain_store + .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], + ChainStoreQueryOptions { headers: true, ..Default::default() }) + .unwrap() { + ChainStoreQueryResult::Headers(headers) => headers, + other => panic!("Unexpected query value {:?}", other ), + }; + for (h, a) in found.iter().zip(expected.iter()) { + assert_eq!(h.entry_address(), a); + } } use globset::{Glob, GlobBuilder, GlobSetBuilder}; diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 0d36973c23..ec34147280 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -7,7 +7,7 @@ use crate:: { } }; use holochain_wasm_utils::api_serialization::{ - QueryArgs, QueryArgsNames, QueryArgsOptions, QueryResult, + QueryArgs, QueryArgsNames, QueryResult, }; use std::convert::TryFrom; use wasmi::{RuntimeArgs, RuntimeValue}; @@ -68,18 +68,17 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let top = agent .top_chain_header() .expect("Should have genesis entries."); - let options = query.options.unwrap_or( QueryArgsOptions::default() ); let maybe_result = match query.entry_type_names { // Result QueryArgsNames::QueryList(pats) => { let refs: Vec<&str> = pats.iter().map(AsRef::as_ref).collect(); // Vec -> Vec<&str> agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> Vec[&str] - Some(ChainStoreQueryOptions { - start: options.start, - limit: options.limit, - headers: options.headers, - }) + ChainStoreQueryOptions { + start: query.options.start, + limit: query.options.limit, + headers: query.options.headers, + } ) } QueryArgsNames::QueryName(name) => { @@ -87,11 +86,11 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult agent.chain().query( &Some(top), refs.as_slice(), // Vec<&str> -> &[&str] - Some(ChainStoreQueryOptions { - start: options.start, - limit: options.limit, - headers: options.headers, - }) + ChainStoreQueryOptions { + start: query.options.start, + limit: query.options.limit, + headers: query.options.headers, + } ) } }; diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index c16224bb4e..90bc54e656 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -991,11 +991,11 @@ pub fn get_links_and_load>( /// # } /// ``` /// -/// With hdk::query_result, you can specify a package of optional QueryArgsOptions, and get a +/// With hdk::query_result, you can specify a package of QueryArgsOptions, and get a /// variety of return values, including a vector of Headers as a `Vec`: /// /// pub fn get_post_headers() -> ZomeApiResult { -/// hdk::query_result("post".into(), Some(QueryArgsOptions{ headers: true, ..Default::default()})) +/// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) /// } /// pub fn query( @@ -1006,9 +1006,9 @@ pub fn query( // The hdk::query API always returns a simple Vec
match query_result( entry_type_names, QueryArgsOptions { - start: Some(start), - limit: Some(limit), - headers: None, + start: start, + limit: limit, + headers: false, }) { Ok(result) => match result { QueryResult::Addresses(addresses) => Ok(addresses), @@ -1029,7 +1029,7 @@ pub fn query_result( &mut mem_stack, QueryArgs { entry_type_names, - options: Some(options), + options, }, )?; diff --git a/hdk-rust/wasm-test/src/lib.rs b/hdk-rust/wasm-test/src/lib.rs index b5d7761ce2..d3f7a59d56 100755 --- a/hdk-rust/wasm-test/src/lib.rs +++ b/hdk-rust/wasm-test/src/lib.rs @@ -24,7 +24,7 @@ use holochain_wasm_utils::{ api_serialization::{ get_entry::{GetEntryOptions, GetEntryResult}, get_links::GetLinksResult, - query::QueryArgsNames, + query::{ QueryArgsNames, QueryArgsOptions, QueryResult }, }, holochain_core_types::{ cas::content::{Address, AddressableContent}, @@ -245,9 +245,28 @@ fn handle_check_query() -> ZomeApiResult> { } let addresses = hdk::query(vec!["[%]*","testEntryType"].into(), 0, 0).unwrap(); if !addresses.len() == 5 { - return err("System Addresses not length 3"); + return err("System + testEntryType Addresses not length 5"); } + // Confirm same results via hdk::query_result + let addresses = match hdk::query_result(vec!["[%]*","testEntryType"].into(), + QueryArgsOptions::default()).unwrap() { + QueryResult::Addresses(av) => av, + _ => return err("Unexpected hdk::query_result"), + }; + if !addresses.len() == 5 { + return err("System + testEntryType Addresses enum not length 5"); + }; + let headers = match hdk::query_result(vec!["[%]*","testEntryType"].into(), + QueryArgsOptions{ headers: true, + ..Default::default()}).unwrap() { + QueryResult::Headers(hv) => hv, + _ => return err("Unexpected hdk::query_result"), + }; + if !headers.len() == 5 { + return err("System + testEntryType Headers enum not length 5"); + }; + hdk::query(QueryArgsNames::QueryName("testEntryType".to_string()), 0, 1) } diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index ac685af3bf..65a854e75c 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -56,14 +56,14 @@ impl<'a> From> for QueryArgsNames { #[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] pub struct QueryArgs { pub entry_type_names: QueryArgsNames, - pub options: Option, + pub options: QueryArgsOptions, } #[derive(Deserialize, Default, Debug, Serialize, DefaultJson)] pub struct QueryArgsOptions { - pub start: Option, - pub limit: Option, - pub headers: Option, + pub start: usize, + pub limit: usize, + pub headers: bool, } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] From d1b8f4db5679f72b0cb97017b0de66408e0259fe Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Wed, 16 Jan 2019 09:21:40 -0800 Subject: [PATCH 09/26] Clean up ChainStore .collect()s, and ensure QueryResult enum has tagging o Without tagging, an empty [] result is ambiguous, so can be assigned to the wrong enum when deserialized. o Add the install: target to Makefile, to rebuild nodejs_container. --- Makefile | 13 ++++++++++--- core/src/agent/chain_store.rs | 12 ++++++------ wasm_utils/src/api_serialization/query.rs | 1 - 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index 3d4ff11734..70e4f642a5 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,14 @@ # holochain-rust Makefile # currently only supports 'debug' builds -.PHONY: all help -all: build_holochain build_cmd +.PHONY: all install help +all: build_holochain build_cmd build_nodejs + +install: install_cmd build_nodejs help: - @echo "run 'make' to build all the libraries and binaries" + @echo "run 'make' to build all the libraries and binaries, and the nodejs bin-package" + @echo "run 'make install' to build and install all the libraries and binaries, and the nodejs bin-package" @echo "run 'make test' to execute all the tests" @echo "run 'make test_app_spec' to build and test app_spec API tests" @echo "run 'make clean' to clean up the build environment" @@ -185,6 +188,10 @@ build_holochain: core_toolchain wasm_build build_cmd: core_toolchain ensure_wasm_target $(CARGO) build -p hc +.PHONY: build_nodejs +build_nodejs: + cd nodejs_container && npm run compile && mkdir -p bin-package && cp native/index.node bin-package + .PHONY: install_cmd install_cmd: build_cmd cd cmd && $(CARGO) install -f --path . diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index ce907b68a5..14a456139f 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -102,14 +102,14 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect::>()) + .collect()) } else { ChainStoreQueryResult::Addresses( self.iter(start_chain_header) .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect::>()) + .collect()) } } [one] if !is_glob_str(one) => { @@ -124,14 +124,14 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect::>()) + .collect()) } else { ChainStoreQueryResult::Addresses( self.iter_type(start_chain_header, &entry_type) .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect::>()) + .collect()) } } rest => { @@ -162,7 +162,7 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect::>()) + .collect()) } else { ChainStoreQueryResult::Addresses( self.iter(start_chain_header) @@ -175,7 +175,7 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect::>()) + .collect()) } } }; diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index 65a854e75c..6123c58bf0 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -67,7 +67,6 @@ pub struct QueryArgsOptions { } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] -#[serde(untagged)] // No type in serialized data; try deserializing as a String, then as a Vec pub enum QueryResult { Addresses(Vec
), Headers(Vec), From e5f668b149f1705fa2293b3127addeab8074e602 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 21 Jan 2019 11:40:53 -0800 Subject: [PATCH 10/26] Added the ability to compare Iso8601 type for equality, order o Since Iso8601 may fail to convert to DateTime, only PartialEq possible --- core_types/Cargo.toml | 1 + core_types/src/lib.rs | 1 + core_types/src/time.rs | 56 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/core_types/Cargo.toml b/core_types/Cargo.toml index bf1953c5a7..b6fae7b3e3 100644 --- a/core_types/Cargo.toml +++ b/core_types/Cargo.toml @@ -6,6 +6,7 @@ authors = ["Holochain Core Dev Team "] [dependencies] arrayref = "0.3.5" base64 = "0.10.0" +chrono = "0.4" serde = "1.0" serde_derive = "1.0" serde_json = { version = "1.0", features = ["preserve_order"] } diff --git a/core_types/src/lib.rs b/core_types/src/lib.rs index de212d0994..a9324c09c6 100644 --- a/core_types/src/lib.rs +++ b/core_types/src/lib.rs @@ -13,6 +13,7 @@ #[macro_use] extern crate arrayref; extern crate base64; +extern crate chrono; extern crate futures; extern crate multihash; extern crate reed_solomon; diff --git a/core_types/src/time.rs b/core_types/src/time.rs index c03651a957..c62a80e544 100644 --- a/core_types/src/time.rs +++ b/core_types/src/time.rs @@ -2,10 +2,17 @@ //! within ChainHeader to enforce that their timestamps //! are defined in a useful and consistent way. +//use std::cmp::Ordering; +use chrono::{ + DateTime, + offset::Utc, +}; +use std::cmp::Ordering; + /// This struct represents datetime data stored as a string /// in the ISO 8601 format. /// More info on the relevant [wikipedia article](https://en.wikipedia.org/wiki/ISO_8601). -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Iso8601(String); impl From<&'static str> for Iso8601 { @@ -17,3 +24,50 @@ impl From<&'static str> for Iso8601 { pub fn test_iso_8601() -> Iso8601 { Iso8601::from("2018-10-11T03:23:38+00:00") } + +/// PartialEq and PartialCmp for ISO 8601 / RFC 3339 timestamps w/ timezone specification. Note +/// that two timestamps that differ in time specification may be equal, because they are the same +/// time specified in two different timezones. Therefore, a String-based Partial{Cmp,Eq} are not +/// correct. If conversion of any Iso8601 String fails, returns false for every test; similarly to +/// how float NaN != NaN. +/// +/// Note that the timezone offset *is* *required*; to default to UTC, append a "Z" to the +/// `--
T::` string, if no timezone is specified. +impl PartialEq for Iso8601 { + fn eq( &self, rhs: &Iso8601 ) -> bool { + match self.0.parse::>() { + Ok(ts_lhs) => match rhs.0.parse::>() { + Ok(ts_rhs) => (&ts_lhs).eq( &ts_rhs ), + Err(_e) => false, + } + Err(_e) => false, + } + } +} + +impl PartialOrd for Iso8601 { + fn partial_cmp( &self, rhs: &Iso8601 ) -> Option { + match self.0.parse::>() { + Ok(ts_lhs) => match rhs.0.parse::>() { + Ok(ts_rhs) => (&ts_lhs).partial_cmp( &ts_rhs ), + Err(_e) => None, // No Ordering available + } + Err(_e) => None, // No Ordering available + } + } +} + + +#[cfg(test)] +pub mod tests { + use super::*; + + + #[test] + fn example_Iso8601_less() { + assert!( Iso8601::from("2018-10-11T03:23:38+00:00") == Iso8601::from("2018-10-11T03:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:15:38-08:00") == Iso8601::from("2018-10-11T03:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:15:39-08:00") > Iso8601::from("2018-10-11T03:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:15:38-08:00") < Iso8601::from("2018-10-11T03:23:38Z")); + } +} From 84dded578e56801c50fbe7f32943f79a833ddcd8 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 21 Jan 2019 12:46:15 -0800 Subject: [PATCH 11/26] Fix ISO-8601 comparison unit tests --- core_types/src/time.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core_types/src/time.rs b/core_types/src/time.rs index c62a80e544..134f456490 100644 --- a/core_types/src/time.rs +++ b/core_types/src/time.rs @@ -62,12 +62,22 @@ impl PartialOrd for Iso8601 { pub mod tests { use super::*; - #[test] - fn example_Iso8601_less() { + fn test_iso_8601_basic() { + // Different ways of specifying UTC "Zulu" assert!( Iso8601::from("2018-10-11T03:23:38+00:00") == Iso8601::from("2018-10-11T03:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:15:38-08:00") == Iso8601::from("2018-10-11T03:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:15:39-08:00") > Iso8601::from("2018-10-11T03:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:15:38-08:00") < Iso8601::from("2018-10-11T03:23:38Z")); + + // Fixed-offset ISO 8601 are comparable to UTC times + assert!( Iso8601::from("2018-10-11T03:23:38-08:00") == Iso8601::from("2018-10-11T11:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:23:39-08:00") > Iso8601::from("2018-10-11T11:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:23:37-08:00") < Iso8601::from("2018-10-11T11:23:38Z")); + + // Ensure PartialOrd respects persistent inequality of invalid ISO 8601 DateTime strings + assert!( Iso8601::from("boo") != Iso8601::from("2018-10-11T03:23:38Z")); + assert!( Iso8601::from("2018-10-11T03:23:38Z") != Iso8601::from("boo")); + assert!( Iso8601::from("boo") != Iso8601::from("boo")); + assert!( ! (Iso8601::from("2018-10-11T03:23:38Z") < Iso8601::from("boo"))); + assert!( ! (Iso8601::from("boo") < Iso8601::from("2018-10-11T03:23:38Z" ))); + assert!( ! (Iso8601::from("boo") < Iso8601::from("boo"))); } } From 1da56b524dea6188b7e5f6106043b9cdf1c71c4e Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 21 Jan 2019 16:43:37 -0800 Subject: [PATCH 12/26] Return Entries from hdk::query_result o Work in progress; needs unit testing --- core/src/nucleus/ribosome/api/query.rs | 54 ++++++++++++++++++----- hdk-rust/src/api.rs | 27 +++++++----- wasm_utils/src/api_serialization/query.rs | 10 +++-- 3 files changed, 65 insertions(+), 26 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index ec34147280..dd00e8518c 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -1,11 +1,17 @@ -use crate:: { +use crate::{ nucleus::ribosome::{ api::ZomeApiResult, Runtime, }, + nucleus::actions::get_entry::get_entry_from_dht, agent::chain_store::{ ChainStoreQueryOptions, ChainStoreQueryResult, } }; +use holochain_core_types::{ + error::HolochainError, + chain_header::ChainHeader, + entry::Entry, +}; use holochain_wasm_utils::api_serialization::{ QueryArgs, QueryArgsNames, QueryResult, }; @@ -33,7 +39,7 @@ use wasmi::{RuntimeArgs, RuntimeValue}; /// Several simple names and/or "glob" patterns can be supplied, and are efficiently /// searched for in a single pass using a single efficient Regular Expression engine: /// -/// `["name/*", "and_another", "SomethingElse"]` +/// `["name/*", "and_another", "something_else"]` /// /// EntryType names can be excluded, eg. to return every simple (non-namespaced) EntryType except System: /// @@ -88,7 +94,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult refs.as_slice(), // Vec<&str> -> &[&str] ChainStoreQueryOptions { start: query.options.start, - limit: query.options.limit, + limit: query.options.limit, headers: query.options.headers, } ) @@ -98,15 +104,43 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult // TODO #793: the Err(_code) is the RibosomeErrorCode, but we can't import that type here. // Perhaps return chain().query should return Some(result)/None instead, and the fixed // UnknownEntryType code here, rather than trying to return a specific error code. - Ok(result) => { - Ok(match result { - ChainStoreQueryResult::Addresses(addresses) => QueryResult::Addresses(addresses), - ChainStoreQueryResult::Headers(headers) => QueryResult::Headers(headers), - }) - } + Ok(result) => Ok(match (query.options.entries, result) { + (false, ChainStoreQueryResult::Addresses(addresses)) => { + QueryResult::Addresses(addresses) + } + (true, ChainStoreQueryResult::Addresses(addresses)) => { + let maybe_entries: Result,HolochainError> = addresses + .iter() + .map(|address| // -> Result + Ok( + get_entry_from_dht(&runtime.context, address.to_owned())? // -> Result, HolochainError> + .ok_or(HolochainError::ErrorGeneric( + format!("Failed to obtain Entry for Address {}", address)))?)) + .collect(); + + match maybe_entries { + Ok(entries) => QueryResult::Entries(entries), + Err(_e) => return ribosome_error_code!(UnknownEntryType), // TODO: return actual error? + } + } + (false, ChainStoreQueryResult::Headers(headers)) => QueryResult::Headers(headers), + (true, ChainStoreQueryResult::Headers(headers)) => { + let maybe_headers_with_entries: Result,HolochainError> = headers + .iter() + .map(|header| // -> Result + Ok((header.to_owned(), + get_entry_from_dht(&runtime.context, header.entry_address().to_owned())? // -> Result, HolochainError> + .ok_or(HolochainError::ErrorGeneric( + format!("Failed to obtain Entry for Address {}", header.entry_address())))?))) + .collect(); + match maybe_headers_with_entries { + Ok(headers_with_entries) => QueryResult::HeadersWithEntries(headers_with_entries), + Err(_e) => return ribosome_error_code!(UnknownEntryType), // TODO: return actual error? + } + } + }), Err(_code) => return ribosome_error_code!(UnknownEntryType), }; runtime.store_result(result) } - diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index 90bc54e656..6677d50f7c 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -955,16 +955,16 @@ pub fn get_links_and_load>( /// /// Each name may be a plain entry type name, or a `"glob"` pattern. All names and patterns are /// merged into a single efficient Regular Expression for scanning. -/// +/// /// You can select many names with patterns such as `"boo*"` (match all entry types starting with /// `"boo"`), or `"[!%]*e"` (all non-system non-name-spaced entry types ending in `"e"`). -/// +/// /// You can organize your entry types using simple name-spaces, by including `"/"` in your entry type /// names. For example, if you have several entry types related to fizzing a widget, you might /// create entry types `"fizz/bar"`, `"fizz/baz"`, `"fizz/qux/foo"` and `"fizz/qux/boo"`. Query for /// `"fizz/**"` to match them all. /// -/// Use vec![], `""`, or `"**"` to match all names in all name-spaces. Matching `"*"` will match only +/// Use vec![], `""`, or `"**"` to match all names in all name-spaces. Matching `"*"` will match only /// non-namespaced names. /// /// entry_type_names: Specify type of entry(s) to retrieve, as a String or Vec of 0 or more names, converted into the QueryArgNames type @@ -990,26 +990,29 @@ pub fn get_links_and_load>( /// } /// # } /// ``` -/// +/// /// With hdk::query_result, you can specify a package of QueryArgsOptions, and get a /// variety of return values, including a vector of Headers as a `Vec`: -/// +/// /// pub fn get_post_headers() -> ZomeApiResult { /// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) /// } -/// +/// pub fn query( entry_type_names: QueryArgsNames, start: usize, limit: usize, ) -> ZomeApiResult> { // The hdk::query API always returns a simple Vec
- match query_result( entry_type_names, - QueryArgsOptions { - start: start, - limit: limit, - headers: false, - }) { + match query_result( + entry_type_names, + QueryArgsOptions { + start: start, + limit: limit, + headers: false, + entries: false, + }, + ) { Ok(result) => match result { QueryResult::Addresses(addresses) => Ok(addresses), _ => return Err(ZomeApiError::FunctionNotImplemented), // should never occur diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index 6123c58bf0..365afb1570 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -1,8 +1,7 @@ use holochain_core_types::{ cas::content::Address, chain_header::ChainHeader, - //entry::Entry, - entry::entry_type::EntryType, + entry::{entry_type::EntryType, Entry}, error::HolochainError, json::*, }; @@ -63,13 +62,16 @@ pub struct QueryArgs { pub struct QueryArgsOptions { pub start: usize, pub limit: usize, + // pub ordering: bool, // forward or reverse (requires doubly-linked chain perhaps) + // pub filter_by: ???, pub headers: bool, + pub entries: bool, } #[derive(Deserialize, Debug, Serialize, DefaultJson, Clone, PartialEq)] pub enum QueryResult { Addresses(Vec
), Headers(Vec), - // Entries(Vec), - // Everything(Vec<(ChainHeader,Entry)) + Entries(Vec), + HeadersWithEntries(Vec<(ChainHeader, Entry)>), } From abd36dac58c0c1fc6cbaa104b75650cdb4f40b62 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 21 Jan 2019 21:16:26 -0800 Subject: [PATCH 13/26] DRY out get_entry_from_dht; questionable improvement... --- core/src/nucleus/ribosome/api/query.rs | 28 +++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index dd00e8518c..8e8ae04069 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -5,9 +5,11 @@ use crate::{ nucleus::actions::get_entry::get_entry_from_dht, agent::chain_store::{ ChainStoreQueryOptions, ChainStoreQueryResult, - } + }, + context::Context }; use holochain_core_types::{ + cas::content::Address, error::HolochainError, chain_header::ChainHeader, entry::Entry, @@ -15,7 +17,10 @@ use holochain_core_types::{ use holochain_wasm_utils::api_serialization::{ QueryArgs, QueryArgsNames, QueryResult, }; -use std::convert::TryFrom; +use std::{ + convert::TryFrom, + sync::Arc, +}; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::query function code @@ -112,10 +117,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let maybe_entries: Result,HolochainError> = addresses .iter() .map(|address| // -> Result - Ok( - get_entry_from_dht(&runtime.context, address.to_owned())? // -> Result, HolochainError> - .ok_or(HolochainError::ErrorGeneric( - format!("Failed to obtain Entry for Address {}", address)))?)) + Ok(get_entry_from_context(&runtime.context, address)?)) .collect(); match maybe_entries { @@ -128,10 +130,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let maybe_headers_with_entries: Result,HolochainError> = headers .iter() .map(|header| // -> Result - Ok((header.to_owned(), - get_entry_from_dht(&runtime.context, header.entry_address().to_owned())? // -> Result, HolochainError> - .ok_or(HolochainError::ErrorGeneric( - format!("Failed to obtain Entry for Address {}", header.entry_address())))?))) + Ok((header.to_owned(), get_entry_from_context(&runtime.context,header.entry_address())?))) .collect(); match maybe_headers_with_entries { Ok(headers_with_entries) => QueryResult::HeadersWithEntries(headers_with_entries), @@ -144,3 +143,12 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult runtime.store_result(result) } + +fn get_entry_from_context(context: &Arc, address: &Address) -> Result { + let entry = match get_entry_from_dht(context, address.to_owned())? { // -> Result, HolochainError> + Some(entry) => entry, + None => return Err(HolochainError::ErrorGeneric( + format!("Failed to obtain Entry for Address {}", address))), + }; + Ok(entry) +} From 33e779214d5be6757b42e08feddea3288b1113bb Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Mon, 21 Jan 2019 21:47:51 -0800 Subject: [PATCH 14/26] Make entries: true always also include an Address or ChainHeader --- core/src/nucleus/ribosome/api/query.rs | 11 +++++------ wasm_utils/src/api_serialization/query.rs | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 8e8ae04069..6bbe0929ee 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -110,14 +110,13 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult // Perhaps return chain().query should return Some(result)/None instead, and the fixed // UnknownEntryType code here, rather than trying to return a specific error code. Ok(result) => Ok(match (query.options.entries, result) { - (false, ChainStoreQueryResult::Addresses(addresses)) => { - QueryResult::Addresses(addresses) - } + (false, ChainStoreQueryResult::Addresses(addresses)) => QueryResult::Addresses(addresses), + (false, ChainStoreQueryResult::Headers(headers)) => QueryResult::Headers(headers), (true, ChainStoreQueryResult::Addresses(addresses)) => { - let maybe_entries: Result,HolochainError> = addresses + let maybe_entries: Result,HolochainError> = addresses .iter() .map(|address| // -> Result - Ok(get_entry_from_context(&runtime.context, address)?)) + Ok((address.to_owned(), get_entry_from_context(&runtime.context, address)?))) .collect(); match maybe_entries { @@ -125,7 +124,6 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult Err(_e) => return ribosome_error_code!(UnknownEntryType), // TODO: return actual error? } } - (false, ChainStoreQueryResult::Headers(headers)) => QueryResult::Headers(headers), (true, ChainStoreQueryResult::Headers(headers)) => { let maybe_headers_with_entries: Result,HolochainError> = headers .iter() @@ -144,6 +142,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult runtime.store_result(result) } +/// Get an Entry via the provided context, returning Entry or HolochainError on failure fn get_entry_from_context(context: &Arc, address: &Address) -> Result { let entry = match get_entry_from_dht(context, address.to_owned())? { // -> Result, HolochainError> Some(entry) => entry, diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index 365afb1570..cd709ffdd3 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -72,6 +72,6 @@ pub struct QueryArgsOptions { pub enum QueryResult { Addresses(Vec
), Headers(Vec), - Entries(Vec), + Entries(Vec<(Address,Entry)>), HeadersWithEntries(Vec<(ChainHeader, Entry)>), } From 776cd087ac2e80c514faebd73154d4b83f1647ff Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 22 Jan 2019 13:11:26 +0100 Subject: [PATCH 15/26] rustfmt --- core/src/agent/chain_store.rs | 197 +++++++++++++--------- core/src/nucleus/ribosome/api/query.rs | 61 +++---- core_types/src/time.rs | 42 ++--- hdk-rust/src/api.rs | 2 +- wasm_utils/src/api_serialization/query.rs | 2 +- 5 files changed, 174 insertions(+), 130 deletions(-) diff --git a/core/src/agent/chain_store.rs b/core/src/agent/chain_store.rs index 14a456139f..06d582391d 100644 --- a/core/src/agent/chain_store.rs +++ b/core/src/agent/chain_store.rs @@ -75,7 +75,7 @@ impl ChainStore { &self, start_chain_header: &Option, entry_type_names: &[&str], - options: ChainStoreQueryOptions + options: ChainStoreQueryOptions, ) -> Result { // Get entry_type name(s), if any. If empty/blank, returns the complete source chain. A // single matching entry type name with no glob pattern matching will use the single @@ -90,10 +90,15 @@ impl ChainStore { // Unpack options; start == 0 --> start at beginning, limit == 0 --> take all remaining let start = options.start; - let limit = if options.limit == 0 { usize::max_value() } else { options.limit }; + let limit = if options.limit == 0 { + usize::max_value() + } else { + options.limit + }; let headers = options.headers; - let vector = match entry_type_names { // Vec
or Vec + let vector = match entry_type_names { + // Vec
or Vec [] | [""] | ["**"] => { // No filtering desired; uses bare .iter() if headers { @@ -102,14 +107,16 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect()) + .collect(), + ) } else { ChainStoreQueryResult::Addresses( self.iter(start_chain_header) .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect()) + .collect(), + ) } } [one] if !is_glob_str(one) => { @@ -124,14 +131,16 @@ impl ChainStore { .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect()) + .collect(), + ) } else { ChainStoreQueryResult::Addresses( self.iter_type(start_chain_header, &entry_type) .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect()) + .collect(), + ) } } rest => { @@ -154,28 +163,24 @@ impl ChainStore { ChainStoreQueryResult::Headers( self.iter(start_chain_header) .filter(|header| { - globset - .matches(header.entry_type().to_string()) - .len() - > 0 + globset.matches(header.entry_type().to_string()).len() > 0 }) .skip(start) .take(limit) .map(|header| header.to_owned()) - .collect()) + .collect(), + ) } else { ChainStoreQueryResult::Addresses( self.iter(start_chain_header) .filter(|header| { - globset - .matches(header.entry_type().to_string()) - .len() - > 0 + globset.matches(header.entry_type().to_string()).len() > 0 }) .skip(start) .take(limit) .map(|header| header.entry_address().to_owned()) - .collect()) + .collect(), + ) } } }; @@ -295,7 +300,7 @@ impl Iterator for ChainStoreTypeIterator { pub mod tests { extern crate tempfile; use self::tempfile::tempdir; - use crate::agent::chain_store::{ ChainStore, ChainStoreQueryOptions, ChainStoreQueryResult, }; + use crate::agent::chain_store::{ChainStore, ChainStoreQueryOptions, ChainStoreQueryResult}; use holochain_cas_implementations::cas::file::FilesystemStorage; use holochain_core_types::{ cas::content::AddressableContent, @@ -514,11 +519,12 @@ pub mod tests { &vec![test_entry_type_b().to_string().as_ref()], ChainStoreQueryOptions::default(), ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; - + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; + let expected = vec![ chain_header_c.entry_address().clone(), chain_header_b.entry_address().clone(), @@ -534,24 +540,28 @@ pub mod tests { start: 0, limit: 1, headers: false, - } + }, ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; - let expected = vec![ - chain_header_c.entry_address().clone() - ]; + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; + let expected = vec![chain_header_c.entry_address().clone()]; assert_eq!(expected, found); // Now query for all EntryTypes via entry_type == None let found = match chain_store - .query(&Some(chain_header_e.clone()), &[], ChainStoreQueryOptions::default()) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_e.clone()), + &[], + ChainStoreQueryOptions::default(), + ) + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_e.entry_address().clone(), chain_header_d.entry_address().clone(), @@ -570,10 +580,11 @@ pub mod tests { &vec!["**".to_string().as_ref()], ChainStoreQueryOptions::default(), ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; assert_eq!(expected, found); // Globbing plus some arbitrary EntryType names, thus matches everything again @@ -583,10 +594,11 @@ pub mod tests { &vec!["another/*".to_string().as_ref(), "testEntryType*"], ChainStoreQueryOptions::default(), ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; assert_eq!(expected, found); // Just globbing @@ -596,10 +608,11 @@ pub mod tests { &vec!["another/*".to_string().as_ref()], ChainStoreQueryOptions::default(), ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_e.entry_address().clone(), chain_header_d.entry_address().clone(), @@ -656,10 +669,11 @@ pub mod tests { &vec!["another/*", "ns/**/t*"], ChainStoreQueryOptions::default(), ) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_h.entry_address().clone(), chain_header_g.entry_address().clone(), @@ -670,11 +684,16 @@ pub mod tests { // So, we should be able to find EntryType names by suffix at any depth let found = match chain_store - .query(&Some(chain_header_h.clone()), &vec!["**/*{e,B}"], ChainStoreQueryOptions::default()) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_h.clone()), + &vec!["**/*{e,B}"], + ChainStoreQueryOptions::default(), + ) + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_h.entry_address().clone(), // .../three chain_header_f.entry_address().clone(), // .../one @@ -704,22 +723,32 @@ pub mod tests { // Find EntryTypes which are/not System (start with '%'), and end in 'e' let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["[!%]*e"], ChainStoreQueryOptions::default()) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_i.clone()), + &vec!["[!%]*e"], + ChainStoreQueryOptions::default(), + ) + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_a.entry_address().clone(), // testEntryType ]; assert_eq!(expected, found); let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["%*e"], ChainStoreQueryOptions::default()) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_i.clone()), + &vec!["%*e"], + ChainStoreQueryOptions::default(), + ) + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_i.entry_address().clone(), // %system_entry_type ]; @@ -727,11 +756,16 @@ pub mod tests { // Including all namespaced EntryTypes let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], ChainStoreQueryOptions::default()) - .unwrap() { - ChainStoreQueryResult::Addresses(addresses) => addresses, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_i.clone()), + &vec!["**/[!%]*e"], + ChainStoreQueryOptions::default(), + ) + .unwrap() + { + ChainStoreQueryResult::Addresses(addresses) => addresses, + other => panic!("Unexpected query value {:?}", other), + }; let expected = vec![ chain_header_h.entry_address().clone(), // .../three chain_header_f.entry_address().clone(), // .../one @@ -741,12 +775,19 @@ pub mod tests { // Including all namespaced EntryTypes, getting ChainHeader let found = match chain_store - .query(&Some(chain_header_i.clone()), &vec!["**/[!%]*e"], - ChainStoreQueryOptions { headers: true, ..Default::default() }) - .unwrap() { - ChainStoreQueryResult::Headers(headers) => headers, - other => panic!("Unexpected query value {:?}", other ), - }; + .query( + &Some(chain_header_i.clone()), + &vec!["**/[!%]*e"], + ChainStoreQueryOptions { + headers: true, + ..Default::default() + }, + ) + .unwrap() + { + ChainStoreQueryResult::Headers(headers) => headers, + other => panic!("Unexpected query value {:?}", other), + }; for (h, a) in found.iter().zip(expected.iter()) { assert_eq!(h.entry_address(), a); } diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 6bbe0929ee..27032f7b3c 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -1,26 +1,16 @@ use crate::{ - nucleus::ribosome::{ - api::ZomeApiResult, Runtime, + agent::chain_store::{ChainStoreQueryOptions, ChainStoreQueryResult}, + context::Context, + nucleus::{ + actions::get_entry::get_entry_from_dht, + ribosome::{api::ZomeApiResult, Runtime}, }, - nucleus::actions::get_entry::get_entry_from_dht, - agent::chain_store::{ - ChainStoreQueryOptions, ChainStoreQueryResult, - }, - context::Context }; use holochain_core_types::{ - cas::content::Address, - error::HolochainError, - chain_header::ChainHeader, - entry::Entry, -}; -use holochain_wasm_utils::api_serialization::{ - QueryArgs, QueryArgsNames, QueryResult, -}; -use std::{ - convert::TryFrom, - sync::Arc, + cas::content::Address, chain_header::ChainHeader, entry::Entry, error::HolochainError, }; +use holochain_wasm_utils::api_serialization::{QueryArgs, QueryArgsNames, QueryResult}; +use std::{convert::TryFrom, sync::Arc}; use wasmi::{RuntimeArgs, RuntimeValue}; /// ZomeApiFunction::query function code @@ -79,7 +69,8 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let top = agent .top_chain_header() .expect("Should have genesis entries."); - let maybe_result = match query.entry_type_names { // Result + let maybe_result = match query.entry_type_names { + // Result QueryArgsNames::QueryList(pats) => { let refs: Vec<&str> = pats.iter().map(AsRef::as_ref).collect(); // Vec -> Vec<&str> agent.chain().query( @@ -89,7 +80,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult start: query.options.start, limit: query.options.limit, headers: query.options.headers, - } + }, ) } QueryArgsNames::QueryName(name) => { @@ -101,7 +92,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult start: query.options.start, limit: query.options.limit, headers: query.options.headers, - } + }, ) } }; @@ -110,9 +101,11 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult // Perhaps return chain().query should return Some(result)/None instead, and the fixed // UnknownEntryType code here, rather than trying to return a specific error code. Ok(result) => Ok(match (query.options.entries, result) { - (false, ChainStoreQueryResult::Addresses(addresses)) => QueryResult::Addresses(addresses), + (false, ChainStoreQueryResult::Addresses(addresses)) => { + QueryResult::Addresses(addresses) + } (false, ChainStoreQueryResult::Headers(headers)) => QueryResult::Headers(headers), - (true, ChainStoreQueryResult::Addresses(addresses)) => { + (true, ChainStoreQueryResult::Addresses(addresses)) => { let maybe_entries: Result,HolochainError> = addresses .iter() .map(|address| // -> Result @@ -124,14 +117,16 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult Err(_e) => return ribosome_error_code!(UnknownEntryType), // TODO: return actual error? } } - (true, ChainStoreQueryResult::Headers(headers)) => { + (true, ChainStoreQueryResult::Headers(headers)) => { let maybe_headers_with_entries: Result,HolochainError> = headers .iter() .map(|header| // -> Result Ok((header.to_owned(), get_entry_from_context(&runtime.context,header.entry_address())?))) .collect(); match maybe_headers_with_entries { - Ok(headers_with_entries) => QueryResult::HeadersWithEntries(headers_with_entries), + Ok(headers_with_entries) => { + QueryResult::HeadersWithEntries(headers_with_entries) + } Err(_e) => return ribosome_error_code!(UnknownEntryType), // TODO: return actual error? } } @@ -143,11 +138,19 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult } /// Get an Entry via the provided context, returning Entry or HolochainError on failure -fn get_entry_from_context(context: &Arc, address: &Address) -> Result { - let entry = match get_entry_from_dht(context, address.to_owned())? { // -> Result, HolochainError> +fn get_entry_from_context( + context: &Arc, + address: &Address, +) -> Result { + let entry = match get_entry_from_dht(context, address.to_owned())? { + // -> Result, HolochainError> Some(entry) => entry, - None => return Err(HolochainError::ErrorGeneric( - format!("Failed to obtain Entry for Address {}", address))), + None => { + return Err(HolochainError::ErrorGeneric(format!( + "Failed to obtain Entry for Address {}", + address + ))); + } }; Ok(entry) } diff --git a/core_types/src/time.rs b/core_types/src/time.rs index 134f456490..ef8c8062ce 100644 --- a/core_types/src/time.rs +++ b/core_types/src/time.rs @@ -3,10 +3,7 @@ //! are defined in a useful and consistent way. //use std::cmp::Ordering; -use chrono::{ - DateTime, - offset::Utc, -}; +use chrono::{offset::Utc, DateTime}; use std::cmp::Ordering; /// This struct represents datetime data stored as a string @@ -34,30 +31,29 @@ pub fn test_iso_8601() -> Iso8601 { /// Note that the timezone offset *is* *required*; to default to UTC, append a "Z" to the /// `--
T::` string, if no timezone is specified. impl PartialEq for Iso8601 { - fn eq( &self, rhs: &Iso8601 ) -> bool { + fn eq(&self, rhs: &Iso8601) -> bool { match self.0.parse::>() { Ok(ts_lhs) => match rhs.0.parse::>() { - Ok(ts_rhs) => (&ts_lhs).eq( &ts_rhs ), + Ok(ts_rhs) => (&ts_lhs).eq(&ts_rhs), Err(_e) => false, - } + }, Err(_e) => false, } } } impl PartialOrd for Iso8601 { - fn partial_cmp( &self, rhs: &Iso8601 ) -> Option { + fn partial_cmp(&self, rhs: &Iso8601) -> Option { match self.0.parse::>() { Ok(ts_lhs) => match rhs.0.parse::>() { - Ok(ts_rhs) => (&ts_lhs).partial_cmp( &ts_rhs ), + Ok(ts_rhs) => (&ts_lhs).partial_cmp(&ts_rhs), Err(_e) => None, // No Ordering available - } + }, Err(_e) => None, // No Ordering available } } } - #[cfg(test)] pub mod tests { use super::*; @@ -65,19 +61,23 @@ pub mod tests { #[test] fn test_iso_8601_basic() { // Different ways of specifying UTC "Zulu" - assert!( Iso8601::from("2018-10-11T03:23:38+00:00") == Iso8601::from("2018-10-11T03:23:38Z")); + assert!( + Iso8601::from("2018-10-11T03:23:38+00:00") == Iso8601::from("2018-10-11T03:23:38Z") + ); // Fixed-offset ISO 8601 are comparable to UTC times - assert!( Iso8601::from("2018-10-11T03:23:38-08:00") == Iso8601::from("2018-10-11T11:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:23:39-08:00") > Iso8601::from("2018-10-11T11:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:23:37-08:00") < Iso8601::from("2018-10-11T11:23:38Z")); + assert!( + Iso8601::from("2018-10-11T03:23:38-08:00") == Iso8601::from("2018-10-11T11:23:38Z") + ); + assert!(Iso8601::from("2018-10-11T03:23:39-08:00") > Iso8601::from("2018-10-11T11:23:38Z")); + assert!(Iso8601::from("2018-10-11T03:23:37-08:00") < Iso8601::from("2018-10-11T11:23:38Z")); // Ensure PartialOrd respects persistent inequality of invalid ISO 8601 DateTime strings - assert!( Iso8601::from("boo") != Iso8601::from("2018-10-11T03:23:38Z")); - assert!( Iso8601::from("2018-10-11T03:23:38Z") != Iso8601::from("boo")); - assert!( Iso8601::from("boo") != Iso8601::from("boo")); - assert!( ! (Iso8601::from("2018-10-11T03:23:38Z") < Iso8601::from("boo"))); - assert!( ! (Iso8601::from("boo") < Iso8601::from("2018-10-11T03:23:38Z" ))); - assert!( ! (Iso8601::from("boo") < Iso8601::from("boo"))); + assert!(Iso8601::from("boo") != Iso8601::from("2018-10-11T03:23:38Z")); + assert!(Iso8601::from("2018-10-11T03:23:38Z") != Iso8601::from("boo")); + assert!(Iso8601::from("boo") != Iso8601::from("boo")); + assert!(!(Iso8601::from("2018-10-11T03:23:38Z") < Iso8601::from("boo"))); + assert!(!(Iso8601::from("boo") < Iso8601::from("2018-10-11T03:23:38Z"))); + assert!(!(Iso8601::from("boo") < Iso8601::from("boo"))); } } diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index 6677d50f7c..4be27e5ec4 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -1016,7 +1016,7 @@ pub fn query( Ok(result) => match result { QueryResult::Addresses(addresses) => Ok(addresses), _ => return Err(ZomeApiError::FunctionNotImplemented), // should never occur - } + }, Err(e) => Err(e), } } diff --git a/wasm_utils/src/api_serialization/query.rs b/wasm_utils/src/api_serialization/query.rs index cd709ffdd3..dfd93e99ee 100644 --- a/wasm_utils/src/api_serialization/query.rs +++ b/wasm_utils/src/api_serialization/query.rs @@ -72,6 +72,6 @@ pub struct QueryArgsOptions { pub enum QueryResult { Addresses(Vec
), Headers(Vec), - Entries(Vec<(Address,Entry)>), + Entries(Vec<(Address, Entry)>), HeadersWithEntries(Vec<(ChainHeader, Entry)>), } From 8932ec691fc0e33297498d8dc5b18804ce1e19de Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 09:04:26 -0800 Subject: [PATCH 16/26] Add some crate-local APIs to access Entry data from dht or agent o Reduce some unnecessary copying of Addresses o Change hdk::query to get local-chain entries only from agent CAS --- core/src/nucleus/actions/get_entry.rs | 42 +++++++++++++++++++------- core/src/nucleus/ribosome/api/query.rs | 11 ++++--- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/core/src/nucleus/actions/get_entry.rs b/core/src/nucleus/actions/get_entry.rs index 04ec438ea4..1659724799 100644 --- a/core/src/nucleus/actions/get_entry.rs +++ b/core/src/nucleus/actions/get_entry.rs @@ -1,21 +1,25 @@ extern crate serde_json; use crate::context::Context; use holochain_core_types::{ - cas::content::Address, + cas::{ + content::Address, + storage::ContentAddressableStorage, + }, crud_status::{CrudStatus, LINK_NAME, STATUS_NAME}, eav::EntityAttributeValue, entry::{Entry, EntryWithMeta}, error::HolochainError, }; -use std::{collections::HashSet, convert::TryInto, str::FromStr, sync::Arc}; +use std::{ + collections::HashSet, convert::TryInto, str::FromStr, + sync::{Arc, RwLock}, +}; -pub(crate) fn get_entry_from_dht( - context: &Arc, - address: Address, +pub(crate) fn get_entry_from_cas( + storage: &Arc>, + address: &Address, ) -> Result, HolochainError> { - let dht = context.state().unwrap().dht().content_storage(); - let storage = &dht.clone(); let json = (*storage.read().unwrap()).fetch(&address)?; let entry: Option = json .and_then(|js| js.try_into().ok()) @@ -23,6 +27,22 @@ pub(crate) fn get_entry_from_dht( Ok(entry) } +pub(crate) fn get_entry_from_agent( + context: &Arc, + address: &Address, +) -> Result, HolochainError> { + let cas = context.state().unwrap().agent().chain().content_storage(); + get_entry_from_cas(&cas.clone(), address) +} + +pub(crate) fn get_entry_from_dht( + context: &Arc, + address: &Address, +) -> Result, HolochainError> { + let cas = context.state().unwrap().dht().content_storage(); + get_entry_from_cas(&cas.clone(), address) +} + pub(crate) fn get_entry_crud_meta_from_dht( context: &Arc, address: Address, @@ -88,13 +108,13 @@ pub fn get_entry_with_meta<'a>( address: Address, ) -> Result, HolochainError> { // 1. try to get the entry - let entry = match get_entry_from_dht(context, address.clone()) { + let entry = match get_entry_from_dht(context, &address) { Err(err) => return Err(err), Ok(None) => return Ok(None), Ok(Some(entry)) => entry, }; // 2. try to get the entry's metadata - let maybe_meta = get_entry_crud_meta_from_dht(context, address.clone()); + let maybe_meta = get_entry_crud_meta_from_dht(context, address); if let Err(err) = maybe_meta { return Err(err); } @@ -118,11 +138,11 @@ pub mod tests { fn get_entry_from_dht_cas() { let entry = test_entry(); let context = test_context_with_state(None); - let result = super::get_entry_from_dht(&context, entry.address()); + let result = super::get_entry_from_dht(&context, &entry.address()); assert_eq!(Ok(None), result); let storage = &context.state().unwrap().dht().content_storage().clone(); (*storage.write().unwrap()).add(&entry).unwrap(); - let result = super::get_entry_from_dht(&context, entry.address()); + let result = super::get_entry_from_dht(&context, &entry.address()); assert_eq!(Ok(Some(entry.clone())), result); } } diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 27032f7b3c..39d0dfe9c2 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -2,7 +2,7 @@ use crate::{ agent::chain_store::{ChainStoreQueryOptions, ChainStoreQueryResult}, context::Context, nucleus::{ - actions::get_entry::get_entry_from_dht, + actions::get_entry::get_entry_from_agent, ribosome::{api::ZomeApiResult, Runtime}, }, }; @@ -109,7 +109,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let maybe_entries: Result,HolochainError> = addresses .iter() .map(|address| // -> Result - Ok((address.to_owned(), get_entry_from_context(&runtime.context, address)?))) + Ok((address.to_owned(), get_entry_from_chain(&runtime.context, address)?))) .collect(); match maybe_entries { @@ -121,7 +121,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult let maybe_headers_with_entries: Result,HolochainError> = headers .iter() .map(|header| // -> Result - Ok((header.to_owned(), get_entry_from_context(&runtime.context,header.entry_address())?))) + Ok((header.to_owned(), get_entry_from_chain(&runtime.context,header.entry_address())?))) .collect(); match maybe_headers_with_entries { Ok(headers_with_entries) => { @@ -138,11 +138,12 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult } /// Get an Entry via the provided context, returning Entry or HolochainError on failure -fn get_entry_from_context( +fn get_entry_from_chain( context: &Arc, address: &Address, ) -> Result { - let entry = match get_entry_from_dht(context, address.to_owned())? { + // TODO: + let entry = match get_entry_from_agent(context, address)? { // -> Result, HolochainError> Some(entry) => entry, None => { From 4caa5d47c75c36083685a12e17ed5e896939553e Mon Sep 17 00:00:00 2001 From: Nicolas Luck Date: Tue, 22 Jan 2019 19:27:17 +0100 Subject: [PATCH 17/26] rustfmt --- core/src/nucleus/actions/get_entry.rs | 9 ++++----- core/src/nucleus/ribosome/api/query.rs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/core/src/nucleus/actions/get_entry.rs b/core/src/nucleus/actions/get_entry.rs index 1659724799..12377ff2b3 100644 --- a/core/src/nucleus/actions/get_entry.rs +++ b/core/src/nucleus/actions/get_entry.rs @@ -1,10 +1,7 @@ extern crate serde_json; use crate::context::Context; use holochain_core_types::{ - cas::{ - content::Address, - storage::ContentAddressableStorage, - }, + cas::{content::Address, storage::ContentAddressableStorage}, crud_status::{CrudStatus, LINK_NAME, STATUS_NAME}, eav::EntityAttributeValue, entry::{Entry, EntryWithMeta}, @@ -12,7 +9,9 @@ use holochain_core_types::{ }; use std::{ - collections::HashSet, convert::TryInto, str::FromStr, + collections::HashSet, + convert::TryInto, + str::FromStr, sync::{Arc, RwLock}, }; diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 39d0dfe9c2..5919ed6e89 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -106,7 +106,7 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult } (false, ChainStoreQueryResult::Headers(headers)) => QueryResult::Headers(headers), (true, ChainStoreQueryResult::Addresses(addresses)) => { - let maybe_entries: Result,HolochainError> = addresses + let maybe_entries: Result, HolochainError> = addresses .iter() .map(|address| // -> Result Ok((address.to_owned(), get_entry_from_chain(&runtime.context, address)?))) @@ -142,7 +142,7 @@ fn get_entry_from_chain( context: &Arc, address: &Address, ) -> Result { - // TODO: + // TODO: let entry = match get_entry_from_agent(context, address)? { // -> Result, HolochainError> Some(entry) => entry, From bee264a989fe0abe2f4168fe66b7f7d6ba530f05 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 11:45:28 -0800 Subject: [PATCH 18/26] Clean up some documentation and formatting. --- core/src/nucleus/ribosome/api/query.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 5919ed6e89..4c30eaf845 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -137,21 +137,17 @@ pub fn invoke_query(runtime: &mut Runtime, args: &RuntimeArgs) -> ZomeApiResult runtime.store_result(result) } -/// Get an Entry via the provided context, returning Entry or HolochainError on failure +/// Get an local-chain Entry via the provided context, returning Entry or HolochainError on failure fn get_entry_from_chain( context: &Arc, address: &Address, ) -> Result { - // TODO: let entry = match get_entry_from_agent(context, address)? { - // -> Result, HolochainError> Some(entry) => entry, - None => { - return Err(HolochainError::ErrorGeneric(format!( - "Failed to obtain Entry for Address {}", - address - ))); - } + None => return Err(HolochainError::ErrorGeneric(format!( + "Failed to obtain Entry for Address {}", + address + ))), }; Ok(entry) } From f2479d2c0fb84c870c0e49e89269441270b78538 Mon Sep 17 00:00:00 2001 From: Michael Dougherty Date: Tue, 22 Jan 2019 13:03:59 -0800 Subject: [PATCH 19/26] Proper rejection when `.call()` throws an exception --- nodejs_container/index.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/nodejs_container/index.js b/nodejs_container/index.js index 95a7e4598a..7e1534d8cf 100644 --- a/nodejs_container/index.js +++ b/nodejs_container/index.js @@ -70,11 +70,15 @@ Container.prototype.call = function (id, zome, trait, fn, params) { } Container.prototype.callWithPromise = function (...args) { - const promise = new Promise((fulfill, reject) => { - this.register_callback(() => fulfill(result)) - }) - const result = this.call(...args) - return [result, promise] + try { + const result = this.call(...args) + const promise = new Promise((fulfill, reject) => { + this.register_callback(() => fulfill()) + }) + return [result, promise] + } catch (e) { + return [undefined, Promise.reject(e)] + } } Container.prototype.callSync = function (...args) { From 059753dd85e026dcf7151fdd1c14f9fc2af24f26 Mon Sep 17 00:00:00 2001 From: Michael Dougherty Date: Tue, 22 Jan 2019 13:14:23 -0800 Subject: [PATCH 20/26] Consistent rejection between callWithPromise and callSync --- nodejs_container/index.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nodejs_container/index.js b/nodejs_container/index.js index 7e1534d8cf..f7aa6a3d45 100644 --- a/nodejs_container/index.js +++ b/nodejs_container/index.js @@ -77,15 +77,16 @@ Container.prototype.callWithPromise = function (...args) { }) return [result, promise] } catch (e) { - return [undefined, Promise.reject(e)] + return [ + undefined, + Promise.reject(e).catch(err => console.error("Error with scenario test system: ", err)) + ] } } Container.prototype.callSync = function (...args) { const [result, promise] = this.callWithPromise(...args) - return promise - .catch(err => console.error("Error with scenario test system: ", err)) - .then(() => { return result }) + return promise.then(() => { return result }) } // Convenience function for making an object that can call into the container From dd56e90817430aa84c1c15631c2a501241b006fa Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 13:18:25 -0800 Subject: [PATCH 21/26] Fix up formatting to satisfy hc-fmt-check --- core/src/nucleus/ribosome/api/query.rs | 10 ++++++---- nodejs_container/index.js | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/nucleus/ribosome/api/query.rs b/core/src/nucleus/ribosome/api/query.rs index 4c30eaf845..7f22770550 100644 --- a/core/src/nucleus/ribosome/api/query.rs +++ b/core/src/nucleus/ribosome/api/query.rs @@ -144,10 +144,12 @@ fn get_entry_from_chain( ) -> Result { let entry = match get_entry_from_agent(context, address)? { Some(entry) => entry, - None => return Err(HolochainError::ErrorGeneric(format!( - "Failed to obtain Entry for Address {}", - address - ))), + None => { + return Err(HolochainError::ErrorGeneric(format!( + "Failed to obtain Entry for Address {}", + address + ))); + } }; Ok(entry) } diff --git a/nodejs_container/index.js b/nodejs_container/index.js index 95a7e4598a..6e2deb6547 100644 --- a/nodejs_container/index.js +++ b/nodejs_container/index.js @@ -71,7 +71,7 @@ Container.prototype.call = function (id, zome, trait, fn, params) { Container.prototype.callWithPromise = function (...args) { const promise = new Promise((fulfill, reject) => { - this.register_callback(() => fulfill(result)) + this.register_callback(() => fulfill()) }) const result = this.call(...args) return [result, promise] From a16c20020cb63de6986a03cd07d3efa3d4f92187 Mon Sep 17 00:00:00 2001 From: Michael Dougherty Date: Tue, 22 Jan 2019 13:30:50 -0800 Subject: [PATCH 22/26] Register callback before call() --- nodejs_container/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodejs_container/index.js b/nodejs_container/index.js index f7aa6a3d45..bb94a23c04 100644 --- a/nodejs_container/index.js +++ b/nodejs_container/index.js @@ -71,10 +71,10 @@ Container.prototype.call = function (id, zome, trait, fn, params) { Container.prototype.callWithPromise = function (...args) { try { - const result = this.call(...args) const promise = new Promise((fulfill, reject) => { this.register_callback(() => fulfill()) }) + const result = this.call(...args) return [result, promise] } catch (e) { return [ From 4c4adb41028cd73d5debb2cbfd19bd13cd8aa4e2 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 13:31:22 -0800 Subject: [PATCH 23/26] Add some documentation for the new hdk::query_result options --- CHANGELOG.md | 1 + hdk-rust/src/api.rs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcf997cfe9..4ac9437d99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - `hc run` now looks for the --interface flag or `HC_INTERFACE` env var if you want to specify the `http` interface [#846]((https://github.com/holochain/holochain-rust/pull/779) - Scenario API added to enable deterministic scenario tests for zome functions. See the [NodeJS Container README](nodejs_container/README.md) for details. +- `hdk::query_result` API supports return of ChainHeader and/or Entry data for the matched EntryType(s) - Admin RPC functions added to container interface. Any (websocket) container interface that is configured with `admin = true` now can call the following functions to remotely change any aspect of the container config (intended to be used in an upcoming container admin UI): diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index 4be27e5ec4..039cdf9b64 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -992,12 +992,25 @@ pub fn get_links_and_load>( /// ``` /// /// With hdk::query_result, you can specify a package of QueryArgsOptions, and get a -/// variety of return values, including a vector of Headers as a `Vec`: +/// variety of return values, such a vector of Headers as a `Vec`: /// +/// ``` /// pub fn get_post_headers() -> ZomeApiResult { /// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) /// } +/// ``` +/// +/// The types of the results available depend on whether `headers` and/or `entries` is set: /// +/// ``` +/// // headers entries +/// pub enum QueryResult { // ------- ------- +/// Addresses(Vec
), // false false +/// Headers(Vec), // true false +/// Entries(Vec<(Address, Entry)>), // false true +/// HeadersWithEntries(Vec<(ChainHeader, Entry)>), // true true +/// } +/// ``` pub fn query( entry_type_names: QueryArgsNames, start: usize, From b222ad1f16f68be8689d958409e4367f08714f99 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 13:43:45 -0800 Subject: [PATCH 24/26] Revert change of WIP, fixing some nodejs test failures --- nodejs_container/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nodejs_container/index.js b/nodejs_container/index.js index 6e2deb6547..95a7e4598a 100644 --- a/nodejs_container/index.js +++ b/nodejs_container/index.js @@ -71,7 +71,7 @@ Container.prototype.call = function (id, zome, trait, fn, params) { Container.prototype.callWithPromise = function (...args) { const promise = new Promise((fulfill, reject) => { - this.register_callback(() => fulfill()) + this.register_callback(() => fulfill(result)) }) const result = this.call(...args) return [result, promise] From 06410cd8a92d2578194d00d72b4cfe4caa98a909 Mon Sep 17 00:00:00 2001 From: Perry Kundert Date: Tue, 22 Jan 2019 14:08:38 -0800 Subject: [PATCH 25/26] Comment out inline doc tests which are not working --- hdk-rust/src/api.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/hdk-rust/src/api.rs b/hdk-rust/src/api.rs index 039cdf9b64..2e2b0fade4 100644 --- a/hdk-rust/src/api.rs +++ b/hdk-rust/src/api.rs @@ -995,21 +995,21 @@ pub fn get_links_and_load>( /// variety of return values, such a vector of Headers as a `Vec`: /// /// ``` -/// pub fn get_post_headers() -> ZomeApiResult { -/// hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) -/// } +/// // pub fn get_post_headers() -> ZomeApiResult { +/// // hdk::query_result("post".into(), QueryArgsOptions{ headers: true, ..Default::default()}) +/// // } /// ``` /// /// The types of the results available depend on whether `headers` and/or `entries` is set: /// /// ``` -/// // headers entries -/// pub enum QueryResult { // ------- ------- -/// Addresses(Vec
), // false false -/// Headers(Vec), // true false -/// Entries(Vec<(Address, Entry)>), // false true -/// HeadersWithEntries(Vec<(ChainHeader, Entry)>), // true true -/// } +/// // // headers entries +/// // pub enum QueryResult { // ------- ------- +/// // Addresses(Vec
), // false false +/// // Headers(Vec), // true false +/// // Entries(Vec<(Address, Entry)>), // false true +/// // HeadersWithEntries(Vec<(ChainHeader, Entry)>), // true true +/// // } /// ``` pub fn query( entry_type_names: QueryArgsNames, From fac5aacb6474c85d7a34101fc7ce61db6ff47769 Mon Sep 17 00:00:00 2001 From: Michael Dougherty Date: Wed, 23 Jan 2019 17:05:06 -0800 Subject: [PATCH 26/26] fmt --- core_types/src/time.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core_types/src/time.rs b/core_types/src/time.rs index 594dc93816..ac4c04ee07 100644 --- a/core_types/src/time.rs +++ b/core_types/src/time.rs @@ -4,10 +4,9 @@ //use std::cmp::Ordering; use chrono::{offset::Utc, DateTime}; -use std::cmp::Ordering; use error::error::HolochainError; use json::JsonString; -use std::time::Duration; +use std::{cmp::Ordering, time::Duration}; /// Represents a timeout for an HDK function #[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash, Serialize, DefaultJson)] @@ -118,4 +117,3 @@ pub mod tests { assert!(!(Iso8601::from("boo") < Iso8601::from("boo"))); } } -