sonicapi/
articles.rs

1// SONIC: Toolchain for formally-verifiable distributed contracts
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed in 2019-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
6// Written in 2024-2025 by Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2019-2024 LNP/BP Standards Association, Switzerland.
9// Copyright (C) 2024-2025 Laboratories for Ubiquitous Deterministic Computing (UBIDECO),
10//                         Institute for Distributed and Cognitive Systems (InDCS), Switzerland.
11// Copyright (C) 2019-2025 Dr Maxim Orlovsky.
12// All rights under the above copyrights are reserved.
13//
14// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
15// in compliance with the License. You may obtain a copy of the License at
16//
17//        http://www.apache.org/licenses/LICENSE-2.0
18//
19// Unless required by applicable law or agreed to in writing, software distributed under the License
20// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
21// or implied. See the License for the specific language governing permissions and limitations under
22// the License.
23
24use strict_encoding::{StrictDeserialize, StrictSerialize, TypeName};
25use ultrasonic::{Contract, ContractId};
26
27use crate::sigs::ContentSigs;
28use crate::{Api, Schema, LIB_NAME_SONIC};
29
30/// Articles contain the contract and all related codex and API information for interacting with it.
31#[derive(Clone, Eq, PartialEq, Debug)]
32#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
33#[strict_type(lib = LIB_NAME_SONIC)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(rename_all = "camelCase"))]
35pub struct Articles {
36    #[cfg_attr(feature = "serde", serde(flatten))]
37    pub schema: Schema,
38    pub contract_sigs: ContentSigs,
39    pub contract: Contract,
40}
41
42impl StrictSerialize for Articles {}
43impl StrictDeserialize for Articles {}
44
45impl Articles {
46    pub fn contract_id(&self) -> ContractId { self.contract.contract_id() }
47
48    pub fn api(&self, name: &TypeName) -> &Api { self.schema.api(name) }
49
50    pub fn merge(&mut self, other: Self) -> Result<bool, MergeError> {
51        if self.contract_id() != other.contract_id() {
52            return Err(MergeError::ContractMismatch);
53        }
54
55        self.schema.merge(other.schema)?;
56        self.contract_sigs.merge(other.contract_sigs);
57
58        Ok(true)
59    }
60}
61
62#[derive(Clone, Eq, PartialEq, Debug, Display, Error)]
63#[display(doc_comments)]
64pub enum MergeError {
65    /// contract id for the merged contract articles doesn't match
66    ContractMismatch,
67
68    /// codex id for the merged schema doesn't match
69    CodexMismatch,
70}
71
72#[cfg(feature = "std")]
73mod _fs {
74    use std::path::Path;
75
76    use strict_encoding::{DeserializeError, SerializeError, StrictDeserialize, StrictSerialize};
77
78    use super::Articles;
79
80    impl Articles {
81        pub fn load(path: impl AsRef<Path>) -> Result<Self, DeserializeError> {
82            Self::strict_deserialize_from_file::<{ usize::MAX }>(path)
83        }
84
85        pub fn save(&self, path: impl AsRef<Path>) -> Result<(), SerializeError> {
86            self.strict_serialize_to_file::<{ usize::MAX }>(path)
87        }
88    }
89}