soft3/neuron/model/src/records.rs

use crate::{Builder, Error, Particle, Reader, Source};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Head {
    pub index: u64,
    pub commit: Particle,
}

pub enum Value<'a> {
    Atom(u64),
    Uint(u64),
    Ref(Particle),
    Optional(Option<Particle>),
    Text(&'a str),
    Refs(&'a [Particle]),
    Raw(Particle),
}
impl Builder {
    pub fn nonce(&mut self, bytes: [u8; 32]) -> Result<Particle, Error> {
        let words = bytes
            .chunks_exact(4)
            .map(|v| {
                self.atom(u64::from(u32::from_le_bytes(
                    v.try_into().map_err(|_| Error::InvalidData)?,
                )))
            })
            .collect::<Result<Vec<_>, _>>()?;
        self.fields(&words)
    }
    pub fn values(&mut self, name: &str, values: &[Value<'_>]) -> Result<Particle, Error> {
        let mut fields = Vec::new();
        for value in values {
            fields.push(match value {
                Value::Atom(n) => self.atom(*n)?,
                Value::Uint(n) => self.uint(*n)?,
                Value::Ref(id) => self.reference(*id)?,
                Value::Text(s) => self.text(s)?,
                Value::Optional(id) => {
                    let node = id.map(|id| self.reference(id)).transpose()?;
                    self.optional(node)?
                }
                Value::Refs(ids) => {
                    let refs = ids
                        .iter()
                        .map(|id| self.reference(*id))
                        .collect::<Result<Vec<_>, _>>()?;
                    self.list(&refs)?
                }
                Value::Raw(node) => *node,
            });
        }
        self.record(name, &fields)
    }
    pub fn head(&mut self, head: Head) -> Result<Particle, Error> {
        self.values(
            "cell/head/1",
            &[Value::Uint(head.index), Value::Ref(head.commit)],
        )
    }
    pub fn artifact(&mut self, bytes: Vec<u8>, media: &str) -> Result<Particle, Error> {
        let len = bytes.len() as u64;
        let content = self.blob(bytes)?;
        let codec = self.blob(b"hemera/bytes/0.3".to_vec())?;
        self.values(
            "cell/artifact/1",
            &[
                Value::Ref(content),
                Value::Ref(codec),
                Value::Uint(len),
                Value::Text(media),
            ],
        )
    }
    pub fn variant(&mut self, name: &str, fields: &[Value<'_>]) -> Result<Particle, Error> {
        self.values(&format!("cell/{name}/1"), fields)
    }
    pub fn empty_map(&mut self) -> Result<Particle, Error> {
        self.map(&Default::default())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Lifecycle {
    Installed,
    Active,
    Paused,
    Retiring,
    Retired,
}
impl Lifecycle {
    pub fn name(self) -> &'static str {
        match self {
            Self::Installed => "installed",
            Self::Active => "active",
            Self::Paused => "paused",
            Self::Retiring => "retiring",
            Self::Retired => "retired",
        }
    }
    pub fn encode(self, b: &mut Builder) -> Result<Particle, Error> {
        b.variant(&format!("lifecycle/{}", self.name()), &[])
    }
}

#[derive(Debug, Clone)]
pub struct Snapshot {
    pub definition: Particle,
    pub application_state: Particle,
    pub lifecycle: Lifecycle,
    pub authority_policy: Particle,
    pub profile: Particle,
    pub epoch: u64,
    pub inbox: Particle,
    pub continuations: Particle,
    pub outbox: Particle,
    pub subscriptions: Particle,
    pub management: Particle,
}
impl Snapshot {
    pub fn encode(&self, b: &mut Builder) -> Result<Particle, Error> {
        use Value::*;
        let lifecycle = self.lifecycle.encode(b)?;
        b.values(
            "cell/snapshot/1",
            &[
                Ref(self.definition),
                Ref(self.application_state),
                Raw(lifecycle),
                Ref(self.authority_policy),
                Ref(self.profile),
                Uint(self.epoch),
                Ref(self.inbox),
                Ref(self.continuations),
                Ref(self.outbox),
                Ref(self.subscriptions),
                Ref(self.management),
            ],
        )
    }
    pub fn read<S: Source>(r: &mut Reader<'_, S>, id: Particle) -> Result<Self, Error> {
        let f = r.record(id, "cell/snapshot/1", 11)?;
        let lifecycle = [
            Lifecycle::Installed,
            Lifecycle::Active,
            Lifecycle::Paused,
            Lifecycle::Retiring,
            Lifecycle::Retired,
        ]
        .into_iter()
        .find(|v| v.encode(&mut Builder::new()).ok() == Some(f[2]))
        .ok_or(Error::UnsupportedSchema)?;
        Ok(Self {
            definition: r.reference(f[0])?,
            application_state: r.reference(f[1])?,
            lifecycle,
            authority_policy: r.reference(f[3])?,
            profile: r.reference(f[4])?,
            epoch: r.uint(f[5])?,
            inbox: r.reference(f[6])?,
            continuations: r.reference(f[7])?,
            outbox: r.reference(f[8])?,
            subscriptions: r.reference(f[9])?,
            management: r.reference(f[10])?,
        })
    }
}

impl<S: Source> Reader<'_, S> {
    pub fn optional_ref(&mut self, id: Particle) -> Result<Option<Particle>, Error> {
        self.optional(id)?.map(|v| self.reference(v)).transpose()
    }
    pub fn head(&mut self, id: Particle) -> Result<Head, Error> {
        let f = self.record(id, "cell/head/1", 2)?;
        Ok(Head {
            index: self.uint(f[0])?,
            commit: self.reference(f[1])?,
        })
    }
    pub fn artifact(&mut self, id: Particle) -> Result<Vec<u8>, Error> {
        let f = self.record(id, "cell/artifact/1", 4)?;
        let content = self.reference(f[0])?;
        let codec = self.reference(f[1])?;
        if codec != *hemera::hash(b"hemera/bytes/0.3").as_bytes() {
            return Err(Error::UnsupportedSchema);
        }
        let expected = self.uint(f[2])?;
        let content = self.content(content)?;
        if !content.blob || content.bytes.len() as u64 != expected {
            return Err(Error::InvalidData);
        }
        Ok(content.bytes)
    }
    pub fn map(&mut self, id: Particle, limit: usize) -> Result<Vec<(Particle, Particle)>, Error> {
        let mut out = Vec::new();
        for entry in self.list(id, limit)? {
            let (k, v) = self.pair(entry)?;
            let key = self.reference(k)?;
            let value = self.reference(v)?;
            if out.last().is_some_and(|(prior, _)| prior >= &key) {
                return Err(Error::InvalidData);
            }
            out.push((key, value));
        }
        Ok(out)
    }
}

Homonyms

soft3/neuron/engine/src/records.rs

Graph