Skip to main content

atproto_dasl/
lib.rs

1//! DASL (Data-Addressed Structures & Links) implementation for AT Protocol.
2//!
3//! This crate implements the [DASL framework](https://dasl.ing/) specifications:
4//!
5//! - **CID**: Content Identifiers - hashes with metadata for addressing resources by content
6//! - **DRISL**: Deterministic Representation for Interoperable Structures and Links -
7//!   deterministic CBOR encoding/decoding with serde integration
8//! - **CAR**: Content-Addressable aRchives - serialized sets of content-addressed resources
9//! - **MASL**: Metadata for Arbitrary Structures & Links - CBOR metadata documents
10//! - **RASL**: Retrieval of Arbitrary Structures & Links - URL scheme and HTTP retrieval
11//! - **BDASL**: Big DASL - large file hashing with streaming verification
12//! - **Web Tiles**: Composable web documents and applications with security constraints
13//!
14//! # Example Usage
15//!
16//! ```rust
17//! use atproto_dasl::{to_vec, from_slice};
18//! use serde::{Serialize, Deserialize};
19//!
20//! #[derive(Serialize, Deserialize, Debug, PartialEq)]
21//! struct Post {
22//!     text: String,
23//!     likes: u64,
24//! }
25//!
26//! let post = Post { text: "Hello!".into(), likes: 42 };
27//!
28//! // Serialize to DAG-CBOR bytes
29//! let bytes = to_vec(&post).unwrap();
30//!
31//! // Deserialize back
32//! let decoded: Post = from_slice(&bytes).unwrap();
33//! assert_eq!(post, decoded);
34//! ```
35
36#![forbid(unsafe_code)]
37#![warn(missing_docs)]
38
39// Core DASL modules
40pub mod cid;
41pub mod drisl;
42pub mod errors;
43pub mod value;
44
45// CAR and storage
46pub mod car;
47pub mod storage;
48pub mod varint;
49
50// DASL specification modules
51pub mod bdasl;
52pub mod masl;
53pub mod rasl;
54pub mod tiles;
55
56// Re-export primary types at crate root for ergonomics
57pub use cid::{
58    Cid, CidCore, DAG_CBOR_CODEC, DaslCid, MULTIBASE_IDENTITY, RawCid, compute_cid_blake3,
59    compute_cid_for, compute_raw_cid, compute_raw_cid_blake3, verify_cid_bytes, verify_cid_reader,
60};
61pub use drisl::{
62    DecodeConfig, EncodeConfig, TimeMode, from_reader, from_reader_non_strict,
63    from_reader_with_config, from_slice, from_slice_non_strict, from_slice_with_config, to_vec,
64    to_vec_with_config, to_writer, to_writer_with_config,
65};
66pub use errors::{
67    CarError, DaslCidError, DecodeError, EncodeError, MaslError, RaslError, StorageError,
68    TilesError, VarintError,
69};
70pub use value::Ipld;
71
72// Re-export CAR and storage types
73pub use car::{CarBlock, CarConfig, CarHeader, CarReader, CarWriter, LimitsConfig};
74pub use storage::{BlockStorage, DiskStorage, MemoryStorage, SpillableBuffer, SpillableReader};
75
76// Conditional re-exports for RASL features
77#[cfg(feature = "reqwest")]
78pub use rasl::fetch::{fetch_verified, fetch_verified_with_client};
79
80#[cfg(feature = "axum")]
81pub use rasl::handler::{directory_handler, func_handler, redirect_handler};
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    use serde::{Deserialize, Serialize};
87
88    #[test]
89    fn test_roundtrip_primitives() {
90        // Boolean
91        let bytes = to_vec(&true).unwrap();
92        assert_eq!(from_slice::<bool>(&bytes).unwrap(), true);
93
94        // Integer
95        let bytes = to_vec(&42i32).unwrap();
96        assert_eq!(from_slice::<i32>(&bytes).unwrap(), 42);
97
98        // String
99        let bytes = to_vec(&"hello").unwrap();
100        assert_eq!(from_slice::<String>(&bytes).unwrap(), "hello");
101
102        // Null (via Option)
103        let none: Option<i32> = None;
104        let bytes = to_vec(&none).unwrap();
105        assert_eq!(from_slice::<Option<i32>>(&bytes).unwrap(), None);
106    }
107
108    #[test]
109    fn test_roundtrip_struct() {
110        #[derive(Serialize, Deserialize, Debug, PartialEq)]
111        struct Test {
112            a: i32,
113            b: String,
114        }
115
116        let original = Test {
117            a: 42,
118            b: "hello".to_string(),
119        };
120        let bytes = to_vec(&original).unwrap();
121        let decoded: Test = from_slice(&bytes).unwrap();
122        assert_eq!(original, decoded);
123    }
124
125    #[test]
126    fn test_roundtrip_nested() {
127        #[derive(Serialize, Deserialize, Debug, PartialEq)]
128        struct Inner {
129            value: i32,
130        }
131
132        #[derive(Serialize, Deserialize, Debug, PartialEq)]
133        struct Outer {
134            inner: Inner,
135            list: Vec<i32>,
136        }
137
138        let original = Outer {
139            inner: Inner { value: 42 },
140            list: vec![1, 2, 3],
141        };
142        let bytes = to_vec(&original).unwrap();
143        let decoded: Outer = from_slice(&bytes).unwrap();
144        assert_eq!(original, decoded);
145    }
146
147    #[test]
148    fn test_map_key_sorting() {
149        use std::collections::BTreeMap;
150
151        let mut map = BTreeMap::new();
152        map.insert("z".to_string(), 1);
153        map.insert("a".to_string(), 2);
154        map.insert("m".to_string(), 3);
155
156        let bytes = to_vec(&map).unwrap();
157
158        assert_eq!(
159            bytes,
160            vec![0xa3, 0x61, 0x61, 0x02, 0x61, 0x6d, 0x03, 0x61, 0x7a, 0x01]
161        );
162    }
163
164    #[test]
165    fn test_strict_rejects_non_canonical() {
166        let bytes = [0x18, 0x00];
167        assert!(from_slice::<u8>(&bytes).is_err());
168        assert_eq!(from_slice_non_strict::<u8>(&bytes).unwrap(), 0);
169    }
170
171    #[test]
172    fn test_float_rejects_nan() {
173        let result = to_vec(&f64::NAN);
174        assert!(result.is_err());
175    }
176
177    #[test]
178    fn test_float_rejects_infinity() {
179        let result = to_vec(&f64::INFINITY);
180        assert!(result.is_err());
181
182        let result = to_vec(&f64::NEG_INFINITY);
183        assert!(result.is_err());
184    }
185}