link_cli/protocol/
archive.rs1use super::error::{ProtocolError, ProtocolResult};
18use super::packet::{DecodeLimits, LinksPacket, Reference};
19use crate::lino_database_input::update_link;
20use crate::named_type_links::NamedTypeLinks;
21use anyhow::{Context, Result};
22use std::fs;
23use std::io::{Read, Write};
24use std::path::Path;
25
26pub fn export_store<S>(storage: &mut S, writer: &mut dyn Write) -> Result<()>
28where
29 S: NamedTypeLinks,
30{
31 let mut links = storage.all_links();
32 links.sort_by_key(|link| link.index);
33 let doublets: Vec<(u64, Vec<Reference>)> = links
34 .iter()
35 .map(|link| {
36 (
37 u64::from(link.index),
38 vec![
39 Reference::Internal(u64::from(link.source)),
40 Reference::Internal(u64::from(link.target)),
41 ],
42 )
43 })
44 .collect();
45 let mut names = Vec::new();
46 for link in &links {
47 if let Some(name) = storage.get_name(link.index)? {
48 let mut references = vec![Reference::External(u64::from(link.index))];
49 references.extend(
50 name.chars()
51 .map(|code_point| Reference::External(u64::from(u32::from(code_point)))),
52 );
53 names.push((names.len() as u64 + 1, references));
54 }
55 }
56 LinksPacket::pack(false, &doublets, true)?.write_to(writer)?;
57 LinksPacket::pack(true, &names, true)?.write_to(writer)?;
58 Ok(())
59}
60
61pub fn export_store_file<S, P>(storage: &mut S, path: P) -> Result<()>
63where
64 S: NamedTypeLinks,
65 P: AsRef<Path>,
66{
67 let path = path.as_ref();
68 let mut bytes = Vec::new();
69 export_store(storage, &mut bytes)?;
70 fs::write(path, bytes)
71 .with_context(|| format!("Failed to write the store archive: {}", path.display()))
72}
73
74pub fn import_store<S>(storage: &mut S, reader: &mut dyn Read) -> Result<()>
77where
78 S: NamedTypeLinks,
79{
80 let limits = DecodeLimits::unlimited();
81 let links = read_packet(reader, &limits, "links")?;
82 let names = read_packet(reader, &limits, "names")?;
83 let mut trailing = [0u8; 1];
84 if reader.read(&mut trailing)? != 0 {
85 return Err(ProtocolError::malformed("trailing bytes after the store archive").into());
86 }
87 let doublets = links
88 .links()
89 .map(|(address, references)| match references {
90 [Reference::Internal(source), Reference::Internal(target)] => Ok((
91 store_address(address)?,
92 store_address(*source)?,
93 store_address(*target)?,
94 )),
95 _ => Err(ProtocolError::malformed(format!(
96 "archive link {address} is not a doublet of link addresses"
97 ))),
98 })
99 .collect::<ProtocolResult<Vec<_>>>()?;
100 let named = names
101 .links()
102 .map(|(_, references)| decode_name(references))
103 .collect::<ProtocolResult<Vec<_>>>()?;
104 for &(index, _, _) in &doublets {
106 if !storage.exists(index) {
107 storage.ensure_created(index);
108 }
109 }
110 for (index, source, target) in doublets {
111 update_link(storage, index, source, target)?;
112 }
113 for (index, name) in named {
114 storage.set_name(index, &name)?;
115 }
116 storage.save()
117}
118
119pub fn import_store_file<S, P>(storage: &mut S, path: P) -> Result<()>
121where
122 S: NamedTypeLinks,
123 P: AsRef<Path>,
124{
125 let path = path.as_ref();
126 let bytes = fs::read(path)
127 .with_context(|| format!("Failed to read the store archive: {}", path.display()))?;
128 import_store(storage, &mut bytes.as_slice())
129}
130
131fn read_packet(
132 reader: &mut dyn Read,
133 limits: &DecodeLimits,
134 part: &str,
135) -> ProtocolResult<LinksPacket> {
136 LinksPacket::read_from(reader, limits)?.ok_or_else(|| {
137 ProtocolError::malformed(format!("the store archive ends before its {part}"))
138 })
139}
140
141fn store_address(address: u64) -> ProtocolResult<u32> {
142 u32::try_from(address).map_err(|_| {
143 ProtocolError::malformed(format!("address {address} does not fit a 32-bit store"))
144 })
145}
146
147fn decode_name(references: &[Reference]) -> ProtocolResult<(u32, String)> {
148 let values = references
149 .iter()
150 .map(|reference| match reference {
151 Reference::External(value) => Ok(*value),
152 Reference::Internal(_) => Err(ProtocolError::malformed(
153 "an archive name holds only external values",
154 )),
155 })
156 .collect::<ProtocolResult<Vec<_>>>()?;
157 let name = values[1..]
159 .iter()
160 .map(|&code_point| {
161 u32::try_from(code_point)
162 .ok()
163 .and_then(char::from_u32)
164 .ok_or_else(|| ProtocolError::malformed(format!("invalid code point {code_point}")))
165 })
166 .collect::<ProtocolResult<String>>()?;
167 Ok((store_address(values[0])?, name))
168}