wip
ci/woodpecker/push/woodpecker Pipeline failed
Details
ci/woodpecker/push/woodpecker Pipeline failed
Details
parent
6fb0d5f1b6
commit
3a6db03ac1
|
@ -1,4 +1,4 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::addressing::{Address, Addressable};
|
||||
use crate::entry::InvariantEntry;
|
||||
use crate::hash::{LargeMultihash, UpMultihash};
|
||||
|
||||
|
@ -20,7 +20,7 @@ pub const ATTR_KEY: &str = "KEY";
|
|||
lazy_static! {
|
||||
pub static ref HIER_ROOT_INVARIANT: InvariantEntry = InvariantEntry {
|
||||
attribute: String::from(ATTR_KEY),
|
||||
value: "HIER_ROOT".into(),
|
||||
value: "HIER_ROOT".address().unwrap().into(),
|
||||
};
|
||||
pub static ref HIER_ROOT_ADDR: Address = HIER_ROOT_INVARIANT.entity().unwrap();
|
||||
pub static ref TYPE_HASH_ADDRESS: Address =
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::addressing::{Address, Addressable};
|
||||
use crate::error::UpEndError;
|
||||
use crate::hash::{b58_decode, sha256hash, AsMultihash, AsMultihashError, UpMultihash};
|
||||
use chrono::NaiveDateTime;
|
||||
|
@ -28,11 +28,9 @@ pub struct InvariantEntry {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
pub enum EntryValue {
|
||||
String(String),
|
||||
Number(f64),
|
||||
Address(Address),
|
||||
Number(f64),
|
||||
Null,
|
||||
Invalid,
|
||||
}
|
||||
|
||||
impl Default for Entry {
|
||||
|
@ -68,7 +66,7 @@ impl InvariantEntry {
|
|||
.write_all(self.attribute.as_bytes())
|
||||
.map_err(UpEndError::from_any)?;
|
||||
entity
|
||||
.write_all(self.value.to_string()?.as_bytes())
|
||||
.write_all(self.value.to_string().as_bytes())
|
||||
.map_err(UpEndError::from_any)?;
|
||||
Ok(Address::Hash(
|
||||
sha256hash(entity.into_inner()).map_err(UpEndError::from_any)?,
|
||||
|
@ -92,12 +90,7 @@ impl AsMultihash for Entry {
|
|||
.as_slice(),
|
||||
)?;
|
||||
result.write_all(self.attribute.as_bytes())?;
|
||||
result.write_all(
|
||||
self.value
|
||||
.to_string()
|
||||
.map_err(|e| AsMultihashError(e.to_string()))?
|
||||
.as_bytes(),
|
||||
)?;
|
||||
result.write_all(self.value.to_string().as_bytes())?;
|
||||
sha256hash(result.get_ref())
|
||||
}
|
||||
}
|
||||
|
@ -111,27 +104,25 @@ impl AsMultihash for InvariantEntry {
|
|||
}
|
||||
|
||||
impl EntryValue {
|
||||
pub fn to_string(&self) -> Result<String, UpEndError> {
|
||||
pub fn to_string(&self) -> String {
|
||||
let (type_char, content) = match self {
|
||||
EntryValue::String(value) => ('S', value.to_owned()),
|
||||
EntryValue::Number(n) => ('N', n.to_string()),
|
||||
EntryValue::Address(address) => ('O', address.to_string()),
|
||||
EntryValue::Number(n) => ('N', n.to_string()),
|
||||
EntryValue::Null => ('X', "".to_string()),
|
||||
EntryValue::Invalid => return Err(UpEndError::CannotSerializeInvalid),
|
||||
};
|
||||
|
||||
Ok(format!("{}{}", type_char, content))
|
||||
format!("{}{}", type_char, content)
|
||||
}
|
||||
|
||||
pub fn guess_from<S: AsRef<str>>(string: S) -> Self {
|
||||
pub fn guess_from<S: AsRef<str>>(string: S) -> Result<Self, UpEndError> {
|
||||
let string = string.as_ref();
|
||||
match string.parse::<f64>() {
|
||||
Ok(num) => EntryValue::Number(num),
|
||||
Ok(num) => Ok(EntryValue::Number(num)),
|
||||
Err(_) => {
|
||||
if let Ok(url) = Url::parse(string) {
|
||||
EntryValue::Address(Address::Url(url))
|
||||
Ok(EntryValue::Address(Address::Url(url)))
|
||||
} else {
|
||||
EntryValue::String(string.to_string())
|
||||
Ok(EntryValue::Address(string.address()?))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,34 +130,32 @@ impl EntryValue {
|
|||
}
|
||||
|
||||
impl std::str::FromStr for EntryValue {
|
||||
type Err = std::convert::Infallible;
|
||||
type Err = UpEndError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.len() < 2 {
|
||||
match s.chars().next() {
|
||||
Some('S') => Ok(EntryValue::String("".into())),
|
||||
Some('X') => Ok(EntryValue::Null),
|
||||
_ => Ok(EntryValue::Invalid),
|
||||
_ => Err(UpEndError::EntryValueInvalid(s.to_string())),
|
||||
}
|
||||
} else {
|
||||
let (type_char, content) = s.split_at(1);
|
||||
match (type_char, content) {
|
||||
("S", content) => Ok(EntryValue::String(String::from(content))),
|
||||
("N", content) => {
|
||||
if let Ok(n) = content.parse::<f64>() {
|
||||
Ok(EntryValue::Number(n))
|
||||
} else {
|
||||
Ok(EntryValue::Invalid)
|
||||
Err(UpEndError::EntryValueInvalid(s.to_string()))
|
||||
}
|
||||
}
|
||||
("O", content) => {
|
||||
if let Ok(addr) = b58_decode(content).and_then(|v| Address::decode(&v)) {
|
||||
Ok(EntryValue::Address(addr))
|
||||
} else {
|
||||
Ok(EntryValue::Invalid)
|
||||
Err(UpEndError::EntryValueInvalid(s.to_string()))
|
||||
}
|
||||
}
|
||||
_ => Ok(EntryValue::Invalid),
|
||||
_ => Err(UpEndError::EntryValueInvalid(s.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,27 +171,13 @@ impl std::fmt::Display for EntryValue {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let (entry_type, entry_value) = match self {
|
||||
EntryValue::Address(address) => ("ADDRESS", address.to_string()),
|
||||
EntryValue::String(string) => ("STRING", string.to_owned()),
|
||||
EntryValue::Number(n) => ("NUMBER", n.to_string()),
|
||||
EntryValue::Null => ("NULL", "NULL".to_string()),
|
||||
EntryValue::Invalid => ("INVALID", "INVALID".to_string()),
|
||||
};
|
||||
write!(f, "{}: {}", entry_type, entry_value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for EntryValue {
|
||||
fn from(str: &str) -> Self {
|
||||
Self::String(str.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for EntryValue {
|
||||
fn from(str: String) -> Self {
|
||||
Self::String(str)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for EntryValue {
|
||||
fn from(num: f64) -> Self {
|
||||
Self::Number(num)
|
||||
|
@ -221,28 +196,28 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_value_from_to_string() -> Result<(), UpEndError> {
|
||||
let entry = EntryValue::String("hello".to_string());
|
||||
let encoded = entry.to_string()?;
|
||||
let entry = EntryValue::Address("hello".address().unwrap());
|
||||
let encoded = entry.to_string();
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Number(1337.93);
|
||||
let encoded = entry.to_string()?;
|
||||
let encoded = entry.to_string();
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Address(Address::Url(Url::parse("https://upend.dev").unwrap()));
|
||||
let encoded = entry.to_string()?;
|
||||
let encoded = entry.to_string();
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::String("".to_string());
|
||||
let encoded = entry.to_string()?;
|
||||
let entry = EntryValue::Address("".address().unwrap());
|
||||
let encoded = entry.to_string();
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Null;
|
||||
let encoded = entry.to_string()?;
|
||||
let encoded = entry.to_string();
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
|
@ -251,7 +226,6 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_into() {
|
||||
assert_eq!(EntryValue::String(String::from("UPEND")), "UPEND".into());
|
||||
assert_eq!(EntryValue::Number(1337.93), 1337.93.into());
|
||||
let addr = Address::Url(Url::parse("https://upend.dev").unwrap());
|
||||
assert_eq!(EntryValue::Address(addr.clone()), addr.into());
|
||||
|
@ -260,15 +234,15 @@ mod tests {
|
|||
#[test]
|
||||
fn test_guess_value() {
|
||||
assert_eq!(
|
||||
EntryValue::guess_from("UPEND"),
|
||||
EntryValue::String("UPEND".into())
|
||||
EntryValue::guess_from("UPEND").unwrap(),
|
||||
EntryValue::Address("UPEND".address().unwrap())
|
||||
);
|
||||
assert_eq!(
|
||||
EntryValue::guess_from("1337.93"),
|
||||
EntryValue::guess_from("1337.93").unwrap(),
|
||||
EntryValue::Number(1337.93)
|
||||
);
|
||||
assert_eq!(
|
||||
EntryValue::guess_from("https://upend.dev"),
|
||||
EntryValue::guess_from("https://upend.dev").unwrap(),
|
||||
EntryValue::Address(Address::Url(Url::parse("https://upend.dev").unwrap()))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
pub enum UpEndError {
|
||||
HashDecodeError(String),
|
||||
AddressParseError(String),
|
||||
EntryValueInvalid(String),
|
||||
AddressComponentsDecodeError(AddressComponentsDecodeError),
|
||||
CannotSerializeInvalid,
|
||||
QueryParseError(String),
|
||||
|
@ -23,6 +24,7 @@ impl std::fmt::Display for UpEndError {
|
|||
match self {
|
||||
UpEndError::HashDecodeError(err) => format!("Could not decode hash: {err}"),
|
||||
UpEndError::AddressParseError(err) => format!("Error parsing address: {err}"),
|
||||
UpEndError::EntryValueInvalid(err) => format!("Invalid Entry value: {err}"),
|
||||
UpEndError::AddressComponentsDecodeError(cde) => match cde {
|
||||
AddressComponentsDecodeError::UnknownType(t) =>
|
||||
format!("Unknown type: \"{t}\""),
|
||||
|
@ -47,3 +49,9 @@ impl UpEndError {
|
|||
UpEndError::Other(error.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::hash::AsMultihashError> for UpEndError {
|
||||
fn from(error: crate::hash::AsMultihashError) -> Self {
|
||||
UpEndError::Other(error.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::addressing::{Address, Addressable};
|
||||
use crate::entry::EntryValue;
|
||||
use crate::error::UpEndError;
|
||||
use nonempty::NonEmpty;
|
||||
|
@ -64,8 +64,28 @@ impl TryFrom<lexpr::Value> for EntryValue {
|
|||
lexpr::Value::Number(num) => Ok(EntryValue::Number(num.as_f64().ok_or_else(|| {
|
||||
UpEndError::QueryParseError(format!("Error processing number ({num:?})."))
|
||||
})?)),
|
||||
lexpr::Value::Char(chr) => Ok(EntryValue::String(chr.to_string())),
|
||||
lexpr::Value::String(str) => Ok(EntryValue::String(str.to_string())),
|
||||
lexpr::Value::Char(chr) => Ok(EntryValue::Address(
|
||||
chr.to_string()
|
||||
.address()
|
||||
.map_err(|e| {
|
||||
UpEndError::QueryParseError(format!(
|
||||
"Error hashing character `{}`: {:}",
|
||||
chr, e
|
||||
))
|
||||
})?
|
||||
.into(),
|
||||
)),
|
||||
lexpr::Value::String(str) => Ok(EntryValue::Address(
|
||||
str.to_string()
|
||||
.address()
|
||||
.map_err(|e| {
|
||||
UpEndError::QueryParseError(format!(
|
||||
"Error hashing string `{}`: {:}",
|
||||
str, e
|
||||
))
|
||||
})?
|
||||
.into(),
|
||||
)),
|
||||
lexpr::Value::Symbol(_) => Ok(EntryValue::Address(Address::try_from(value.clone())?)),
|
||||
_ => Err(UpEndError::QueryParseError(
|
||||
"Value can only be a string, number or address.".into(),
|
||||
|
@ -298,7 +318,7 @@ impl FromStr for Query {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::error::UpEndError;
|
||||
use crate::{addressing::Addressable, error::UpEndError};
|
||||
|
||||
use super::*;
|
||||
use url::Url;
|
||||
|
@ -377,7 +397,10 @@ mod test {
|
|||
}))
|
||||
);
|
||||
|
||||
let values: Vec<EntryValue> = vec!["FOO".into(), "BAR".into()];
|
||||
let values: Vec<EntryValue> = vec![
|
||||
"FOO".address().unwrap().into(),
|
||||
"BAR".address().unwrap().into(),
|
||||
];
|
||||
let query = r#"(matches ? ? (in "FOO" "BAR"))"#.parse::<Query>()?;
|
||||
assert_eq!(
|
||||
query,
|
||||
|
@ -400,7 +423,8 @@ mod test {
|
|||
);
|
||||
|
||||
// Invalid queries
|
||||
let values: Vec<EntryValue> = vec!["FOO".into(), EntryValue::Number(1337.93)];
|
||||
let values: Vec<EntryValue> =
|
||||
vec!["FOO".address().unwrap().into(), EntryValue::Number(1337.93)];
|
||||
let query = r#"(matches ? ? (in "FOO" 1337.93))"#.parse::<Query>()?;
|
||||
|
||||
assert_eq!(
|
||||
|
@ -412,7 +436,7 @@ mod test {
|
|||
}))
|
||||
);
|
||||
|
||||
let values = vec![EntryValue::Number(1337.93), "FOO".into()];
|
||||
let values = vec![EntryValue::Number(1337.93), "FOO".address().unwrap().into()];
|
||||
let query = r#"(matches ? ? (in 1337.93 "FOO"))"#.parse::<Query>()?;
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
@ -698,7 +698,7 @@ pub async fn get_all_attributes(state: web::Data<State>) -> Result<HttpResponse,
|
|||
.into_iter()
|
||||
.filter_map(|e| {
|
||||
if e.attribute == ATTR_LABEL {
|
||||
if let EntryValue::String(label) = e.value {
|
||||
if let EntryValue::String(label) = e.value { // TODO
|
||||
Some(label)
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -197,11 +197,7 @@ fn to_sqlite_predicates(query: Query) -> Result<SqlResult, QueryExecutionError>
|
|||
match &eq.value {
|
||||
QueryComponent::Exact(q_value) => match q_value {
|
||||
EntryValue::Number(n) => subqueries.push(Box::new(data::value_num.eq(*n))),
|
||||
_ => subqueries.push(Box::new(data::value_str.eq(
|
||||
q_value.to_string().map_err(|e| {
|
||||
QueryExecutionError(format!("failed producing sql: {e}"))
|
||||
})?,
|
||||
))),
|
||||
_ => subqueries.push(Box::new(data::value_str.eq(q_value.to_string()))),
|
||||
},
|
||||
QueryComponent::In(q_values) => {
|
||||
let first = q_values.first().ok_or_else(|| {
|
||||
|
@ -239,11 +235,7 @@ fn to_sqlite_predicates(query: Query) -> Result<SqlResult, QueryExecutionError>
|
|||
string values! (Found {v})"
|
||||
)))
|
||||
} else {
|
||||
v.to_string().map_err(|e| {
|
||||
QueryExecutionError(format!(
|
||||
"failed producing sql: {e}"
|
||||
))
|
||||
})
|
||||
Ok(v.to_string())
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<String>, QueryExecutionError>>()?,
|
||||
|
|
|
@ -48,7 +48,7 @@ impl TryFrom<&Entry> for models::Entry {
|
|||
entity_searchable: match &e.entity {
|
||||
Address::Attribute(attr) => Some(attr.clone()),
|
||||
Address::Url(url) => Some(url.to_string()),
|
||||
_ => None,
|
||||
_ => None, // TODO
|
||||
},
|
||||
entity: e.entity.encode()?,
|
||||
attribute: e.attribute.clone(),
|
||||
|
@ -66,7 +66,7 @@ impl TryFrom<&Entry> for models::Entry {
|
|||
..base_entry
|
||||
}),
|
||||
_ => Ok(models::Entry {
|
||||
value_str: Some(e.value.to_string()?),
|
||||
value_str: Some(e.value.to_string()),
|
||||
value_num: None,
|
||||
..base_entry
|
||||
}),
|
||||
|
|
|
@ -6,7 +6,7 @@ use lru::LruCache;
|
|||
use tracing::trace;
|
||||
use uuid::Uuid;
|
||||
|
||||
use upend_base::addressing::Address;
|
||||
use upend_base::addressing::{Address, Addressable};
|
||||
use upend_base::constants::ATTR_LABEL;
|
||||
use upend_base::constants::{ATTR_IN, HIER_ROOT_ADDR, HIER_ROOT_INVARIANT};
|
||||
use upend_base::entry::Entry;
|
||||
|
@ -112,7 +112,7 @@ pub fn fetch_or_create_dir(
|
|||
.query(Query::SingleQuery(QueryPart::Matches(PatternQuery {
|
||||
entity: QueryComponent::Variable(None),
|
||||
attribute: QueryComponent::Exact(ATTR_LABEL.into()),
|
||||
value: QueryComponent::Exact(String::from(directory.clone()).into()),
|
||||
value: QueryComponent::Exact(String::from(directory.clone()).address()?.into()),
|
||||
})))?
|
||||
.into_iter()
|
||||
.map(|e: Entry| e.entity);
|
||||
|
@ -142,7 +142,7 @@ pub fn fetch_or_create_dir(
|
|||
let directory_entry = Entry {
|
||||
entity: new_directory_address.clone(),
|
||||
attribute: String::from(ATTR_LABEL),
|
||||
value: String::from(directory).into(),
|
||||
value: String::from(directory).address()?.into(),
|
||||
provenance: "SYSTEM FS".to_string(),
|
||||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
};
|
||||
|
|
|
@ -247,7 +247,7 @@ impl UpEndConnection {
|
|||
|
||||
let primary = data
|
||||
.filter(entity.eq(object_address.encode()?))
|
||||
.or_filter(value_str.eq(EntryValue::Address(object_address.clone()).to_string()?))
|
||||
.or_filter(value_str.eq(EntryValue::Address(object_address.clone()).to_string()))
|
||||
.load::<models::Entry>(&conn)?;
|
||||
|
||||
let entries = primary
|
||||
|
@ -287,7 +287,7 @@ impl UpEndConnection {
|
|||
let matches = data
|
||||
.filter(identity.eq(object_address.encode()?))
|
||||
.or_filter(entity.eq(object_address.encode()?))
|
||||
.or_filter(value_str.eq(EntryValue::Address(object_address).to_string()?));
|
||||
.or_filter(value_str.eq(EntryValue::Address(object_address).to_string()));
|
||||
|
||||
Ok(diesel::delete(matches).execute(&conn)?)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ macro_rules! upend_insert_val {
|
|||
$db_connection.insert_entry(Entry {
|
||||
entity: $entity.clone(),
|
||||
attribute: String::from($attribute),
|
||||
value: upend_base::entry::EntryValue::String(String::from($value)),
|
||||
value: upend_base::entry::EntryValue::Address(String::from($value).address().unwrap()),
|
||||
provenance: "SYSTEM INIT".to_string(),
|
||||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
})
|
||||
|
|
|
@ -21,7 +21,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
|||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
use std::{fs, iter};
|
||||
use tracing::{error, info, trace, warn};
|
||||
use upend_base::addressing::Address;
|
||||
use upend_base::addressing::{Address, Addressable};
|
||||
use upend_base::constants::{ATTR_ADDED, ATTR_BY, ATTR_IN, ATTR_LABEL, ATTR_OF, TYPE_HASH_ADDRESS};
|
||||
use upend_base::entry::Entry;
|
||||
use upend_base::hash::{b58_encode, UpMultihash};
|
||||
|
@ -410,12 +410,16 @@ impl FsStore {
|
|||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
};
|
||||
|
||||
let mime_entry = mime_type.map(|mime_type| Entry {
|
||||
entity: blob_address.clone(),
|
||||
attribute: FILE_MIME_KEY.to_string(),
|
||||
value: mime_type.into(),
|
||||
provenance: "SYSTEM INIT".to_string(),
|
||||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
let mime_entry = mime_type.and_then(|mime_type| {
|
||||
mime_type.address().ok().and_then(|mime_type_address| {
|
||||
Some(Entry {
|
||||
entity: blob_address.clone(),
|
||||
attribute: FILE_MIME_KEY.to_string(),
|
||||
value: mime_type_address.into(),
|
||||
provenance: "SYSTEM INIT".to_string(),
|
||||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
let added_entry = Entry {
|
||||
|
@ -472,6 +476,7 @@ impl FsStore {
|
|||
attribute: ATTR_LABEL.to_string(),
|
||||
value: name
|
||||
.unwrap_or_else(|| filename.as_os_str().to_string_lossy().to_string())
|
||||
.address()?
|
||||
.into(),
|
||||
provenance: "SYSTEM INIT".to_string(),
|
||||
timestamp: chrono::Utc::now().naive_utc(),
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "upend_text"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
path = "src/lib.rs"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
pulldown-cmark = "0.9.3"
|
||||
serde = "1.0.188"
|
||||
wasm-bindgen = "0.2.87"
|
||||
wee_alloc = "0.4.5"
|
|
@ -0,0 +1,45 @@
|
|||
use pulldown_cmark::{Options, Parser};
|
||||
|
||||
pub struct Annotation {
|
||||
pub id: String,
|
||||
pub body: String,
|
||||
pub target: String,
|
||||
}
|
||||
|
||||
pub struct MarkdownResult {
|
||||
pub text: String,
|
||||
pub annotations: Vec<Annotation>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UpEndTextError(pub String);
|
||||
|
||||
impl std::fmt::Display for UpEndTextError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{:#?}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn markdown_to_annotations(markdown: &str) -> Result<MarkdownResult, UpEndTextError> {
|
||||
let mut options = Options::empty();
|
||||
options.insert(Options::ENABLE_STRIKETHROUGH);
|
||||
let parser = Parser::new_ext(markdown, options);
|
||||
|
||||
for ev in parser {
|
||||
println!("{:?}", ev);
|
||||
}
|
||||
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let markdown_input = "Hello world, this is a ~~complicated~~ *very simple* example.";
|
||||
let result = markdown_to_annotations(markdown_input);
|
||||
result.unwrap();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue