Skip to main content

usage/
usage.rs

1//! Parsing betula JSON with the Rust bindings. Run in CI; the asserts are checked.
2
3use betula_schema::{Annotation, Sequence, Strand, Tree};
4use serde_json::json;
5
6fn main() -> Result<(), betula_schema::Error> {
7    // Parse JSON text. The type parameter picks both the schema and the result.
8    let tree: Tree = betula_schema::parse_str(
9        r#"{"name": "root", "length": 0, "children": [{"name": "A", "length": 0.1, "children": []}]}"#,
10    )?;
11    assert_eq!(tree.children[0].name, "A");
12
13    // Or validate a serde_json::Value you've already decoded.
14    let seq: Sequence = betula_schema::parse(json!({"type": "rna-sequence", "identifier": "s1", "sequence": "ACGU"}))?;
15
16    // Union kinds are enums. Constrained strings (Identifier, the alphabets)
17    // are newtypes that Deref to String.
18    match &seq {
19        Sequence::RnaSequence(rna) => assert_eq!((rna.identifier.as_str(), rna.sequence.as_str()), ("s1", "ACGU")),
20        other => panic!("unexpected variant {other:?}"),
21    }
22
23    // GFF3 strand is a plain enum.
24    let gene: Annotation = betula_schema::parse(json!({
25        "ID": "gene1", "seqid": "chr1", "source": "example", "interval_type": "gene",
26        "start": 1, "end": 900, "score": ".", "strand": "-", "phase": ".",
27        "attributes": {}, "children": []
28    }))?;
29    assert_eq!(gene.strand, Strand::Reverse);
30
31    // Invalid data is Error::Invalid, with one message per problem. parse
32    // checks the schema before deserializing, which matters: the generated
33    // types alone don't enforce every keyword.
34    match betula_schema::parse_str::<Sequence>(r#"{"type": "dna-sequence", "identifier": "s2", "sequence": "ACGU"}"#) {
35        Err(betula_schema::Error::Invalid { kind, errors }) => assert!(kind == "Sequence" && !errors.is_empty()),
36        other => panic!("expected Error::Invalid, got {other:?}"),
37    }
38
39    // validate() checks a Value without deserializing it.
40    assert!(betula_schema::validate::<Tree>(&json!({"name": "A", "length": -1, "children": []})).is_err());
41
42    // Serialize with serde as usual.
43    let text = serde_json::to_string(&tree).map_err(betula_schema::Error::Json)?;
44    assert_eq!(betula_schema::parse_str::<Tree>(&text)?, tree);
45
46    // Every generated type implements Kind, so generic code can take any of
47    // them; KINDS lists the names.
48    fn count_valid<K: betula_schema::Kind>(values: &[serde_json::Value]) -> usize {
49        values.iter().filter(|v| betula_schema::validate::<K>(v).is_ok()).count()
50    }
51    let candidates = [json!({"name": "A", "length": 1, "children": []}), json!({"name": "B"})];
52    assert_eq!(count_valid::<Tree>(&candidates), 1);
53    assert!(betula_schema::KINDS.contains(&<Tree as betula_schema::Kind>::NAME));
54
55    println!("rust usage example ok");
56    Ok(())
57}